STM32单片机初学7-USART串口通信

USART是Universal Synchronous/Asynchronous Receiver/Transmitter的英文缩写,意为通用同步/异步串行接收/发送器。它也是单片机中最常用通信方式之一。

常用于单片机与上位机、蓝牙模块、GPS模块等设备的数据传输。

 

与USART相似的还有一个叫做UART的通信方式,即通用异步串行接收/发送器。所不同的是其只能进行异步通信。

同步与异步通信的区别在于是否有无时钟信号。前面讲过的IIC与SPI都有时钟线,进行传输时,需要通过时钟线使通信双方保持时序一致。

而异步通信则不需要时钟线,发送方按照设定的速率发送数据,接收方按照相同的速率接收数据。所以为了接线简单,很多时候都是使用异步通信,本文也是以异步通信来讲解USART的基本用法。

在UART通信中,发送方发完数据后并不会管接收方是否收到正确的数据,接受方仅仅通过数据校验来确认数据位是否有误。

所以为了让异步通讯顺利进行,通信双方必须保证一致的通信参数:波特率、数据位,停止位数、校验方式。

波特率:

波特(Baud)即调制速率,指的是有效数据讯号调制载波的速率,即单位时间内载波调制状态变化的次数。波特率表示单位时间内传送的码元符号的个数,它是对符号传输速率的一种度量,它用单位时间内载波调制状态改变的次数来表示,波特率即指一个单位时间内传输符号的个数。它是对符号传输速率的一种度量,其值越大,代表传输速率越快。

注意其与比特率的区别,这是两个不同的概念。他们之间的关系为:

 式中I为数据量,S为波特率,N为每个符号的数据量。

例如:

波特率为9600,一个符号有10位的数据位,则N=2^{10},计算出来的结果就是96000bit/s.

数据位:

数据位即一个符号或者说一个数据帧所携带的有效数据位数。STM32有8位与9位两种可供选择,一般情况下使用8位较多,因为8个bit正好等于一个Byte,进行数据处理时要方便一些。其数据按照低位在前,高位在后的顺序排列。

停止位数:

数据位后面会紧接着一个停止位,作为该符号的结束标志。STM32有4种停止位可供选择,会发现其停止位会有非整数的情况,例如1.5个停止位,其实这是指停止位电平的维持时间为1.5倍的单位时间。一般使用1个停止位较多。

 校验方式:

上文说过,异步通信中,数据发送方发完数据后并不会管接收方是否收到正确的数据,那接受方如何知道收的数据是正确的呢?这就是通过校验位来判断。

其原理就是根据传输的数据位中“1”的个数是奇数或偶数来进行校验。采用奇数的称为奇校验,反之,称为偶校验。比如奇校验,即接收端收到这组数据时,校验“1”的个数是否为奇数来确定校验位的状态。

假设有一组数据为10101100,其中1的个数为4。4为偶数,如果采用奇校验,则校验位为1,最后的数据为10101100(1);若采用偶校验,则校验位为0,最后的数据为10101100(0)。可以发现,增加校验位之后,奇校验位数下为1的个数为奇数了,偶校验下为偶数。

除了奇偶校验,还可以设为无校验,即不进行任何校验。

可以发现,这种校验方式存在很大的局限性,首先无法确认是哪些位的数据有误,另外如果同时有两位数据出错,则也会被接收方判定为正确的数据,当然这是概率的问题。

***********************************************************

上面四个参数是进行串口通信必须设置的。接下来以STM32F103C8T6与电脑上位机(串口调试工具)进行数据传输来具体讲解STM32的USART用法。

STM32F103C8T6共有三个USART接口,如下

 其中只有USART1带有CLK时钟线,另外两个则不带,所以只有USART1支持异步通信,当然也支持同步通信。

再来看看其时钟总线,如下:

 USART1挂在APB2时钟总线下,其时钟频率为72MHz,而USART2和USART3挂在APB1总线下,时钟频率36MHz。所以USART1能比另外两路达到更高的通信速率。

这里我就使用USART1与电脑的串口调试助手进行数据的发送与接收。

-----------------------------------------------------------------

电脑与STM32的硬件连接

如果使用的是台式机,需要找到主机背面的DB9接口,找到其RXD与TXD的引脚,然后将电脑的RXD与单片机的TXD相连,电脑的TXD与单片机的RXD相连,最后还要将两端的GND相连。

DB9阵脚定义如下:

 如果使用的是笔记本电脑,是不带DB9接口的,所以就需要USB转TTL的工具。这样就能通过笔记本电脑的USB接口与STM32进行串口通信了。

USB转TTL常用CH340这款芯片。

CH340有很多规格,其引脚定义 如下。

 下面是CH340N的实际应用电路。制作电路板时,可将该芯片和单片机一起放PCB上。

 

如果不想把CH340放在板子上,或者只有一个STM32裸板,则需要一个USB转TTL的模块,该模块的核心还是CH340。使用时,将该模块插电脑的USB口,然后找到模块的GND、RXD和TXD,按照前面DB9的方式与单片机的引脚相连即可。

-----------------------------------------------------------------------------

软件部分

电脑端我们需要一个串口调试工具。串口调试工具网上有很多,使用方法基本一样。这里我使用的是正点原子的XCOM,其界面如下。

其使用很简单,设置好通信参数后打开串口。在数据发送区输入想要发送的数据,点击“发送”即可。电脑串口接收到的数据会显示在数据接收区。

发送与接收的数据都可以16进制数显示,勾选“16进制发送/显示”即可。

“白底黑字”可设置窗口背景色。RTS(Request To Send)与DTR(Data Terminal Ready)一般可不勾选。

除了单条数据手动发送,还可以设置成单条或者多条自动发送。

---------------------------------------------------------------

接下来就以一个简单的功能来讲解如何使用USART:STM32将串口接收到的数据原封不动地发送回去。

1.USART初始化

void USART_Userinit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);        //打开GPIOA时钟总线

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;		//设置PA9为复用推挽输出(TX)
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;							
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;								
    GPIO_Init(GPIOA, &GPIO_InitStructure);
	
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;		//设置PA10为浮空输入(RX)				
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;							
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;								
    GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;					//初始化USART1					
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);		
	USART_InitStructure.USART_BaudRate = 115200; 			//波特率设为115200							
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//数据位8位
	USART_InitStructure.USART_StopBits = USART_StopBits_1; 			//停止位1位	
	USART_InitStructure.USART_Parity = USART_Parity_No; 			//校验方式:无校验		
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 	
	USART_InitStructure.USART_Mode = USART_Mode_Tx| USART_Mode_Rx ; 	//收发模式	
	USART_Init(USART1, &USART_InitStructure);	
    USART_Cmd(USART1,ENABLE);		                    //使能USART1
	
	
	USART_ITConfig(USART1,USART_IT_RXNE, ENABLE);		//使能USART1接收中断
	
	NVIC_InitTypeDef NVIC_InitStructure;                //USART1中断初始化
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;			
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;	
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;				
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					
	NVIC_Init(&NVIC_InitStructure);													
	
}

下面是带注释的

 首先是对USART1的IO口进行初始化。TX为数据发送,设置为推挽输出,RX为数据接收,设置为浮空输入。

然后是USART1的初始化。与IIC、SPI的方式一样,定义一个参数配置结构体并打开其对应的时钟总线,然后给结构体里成员赋值,再调用初始化函数即可完成USART的初始化。

其需要设置的参数如上面介绍的一样,具体可查阅STM32标准库手册,

然后还需启用USART1的接收中断,当STM32接收到数据则会跳至中断函数。

USART的中断源有很多,可根据需要灵活使用。

所以还需要对中断进行初始化。关于中断的用法,以后再做详细介绍。

 2,主函数

主函数可以写的很简单,为了直观知道单片机是在执行主函数还是中断函数,特意使用了一个指示灯,由PC13控制,先对其进行初始化。

void GPIO_UserInit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;            //推挽输出
	GPIO_Init(GPIOC,&GPIO_InitStructure);

}

当cpu在执行main主函数时,指示灯快闪;当执行中断函数时,指示灯慢闪。

所以得到主函数如下:

#include<stm32f10x.h>
#include<stm32f10x_gpio.h>
#include<stm32f10x_rcc.h>
#include<stm32f10x_usart.h>

int main(void)

{	
	USART_Userinit( );                                 //调用USART初始化函数
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    //设置中断优先级分2组
	GPIO_UserInit();                                   //调用GPIO初始化函数
	
	while(1)                //主函数循环
	{
			GPIO_SetBits(GPIOC,GPIO_Pin_13);            //指示灯200ms闪烁周期
			Delay_ms(100);
			GPIO_ResetBits(GPIOC,GPIO_Pin_13);
			Delay_ms(100);
	}		
}

为了方便理解,这里就不使用定时器了,直接Delay。

延时函数:

void Delay_ms(int Time)
{
	int i=0;
	while(Time--)
	{
		i=8000;
		while(i--);
	}
	return;
}

然后是中断函数,其执行条件为:当但USART接收到数据时。

接收到数据之后,将其发送出去,然后清除中断标志位,退出中断函数,回到main函数。直至再次接收到数据并进入中断函数。以此循环

中断函数如下:

void USART1_IRQHandler(void)                						//USART中断函数
{
	u16 Data;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)           //如果USART1接收中断SET
	{
		GPIO_SetBits(GPIOC,GPIO_Pin_13);
		Data =USART_ReceiveData(USART1);					//将USART接收到的数据赋值给Data
		USART_SendData(USART1,Data);						//将Data的值发送出去
		Delay_ms(400);										
		GPIO_ResetBits(GPIOC,GPIO_Pin_13);
		Delay_ms(400);	
	}
	USART_ClearITPendingBit(USART1,USART_IT_RXNE);        //清除USART1的接收中断标志位
} 	

这样,程序部分就完成了

-----------------------------------------------------

烧录程序后,将单片机与电脑连接(或者通过·USB转TTL工具与电脑连接),打开XCOM。

首先是选择串口。如果无法选择串口(下拉菜单无可选项),则需要确认单片机与电脑正确连接。如果连接无误仍无法选择,则可能是因为电脑无相关驱动。需要下载一个CH340的驱动并安装(网上可找到)。可在设备管理器中查看是否连接上以及分辨有驱动。如果连接了,但是无法打开串口,显示的是有无法识别的设备插入。

下面是正确连接了并驱动程序工作正常的界面。

然后是设置4个通信参数,需与程序中USART初始化的参数一致。

 然后点击“打开串口”,按钮变成红色,说明打开成功。

未发送数据时,指示灯快闪,说明此时正在执行main的while循环。

然后,我们在数据发送区输入一个2位16进制的数(由于USART_ReceiveData(USART1)函数的返回值就是16位的,所以只能输入2位16进制数),然后发送,可以看到指示灯满闪了一下,同时数据接收区出现了刚才发送的数据。说明此时cpu正在执行中断函数。

注意不要勾选“发送新行”,否则会无法跳出中断函数,永久卡死在中断函数里。

短暂延时后,指示灯又开始快闪,说明此时CPU返回到了main函数并继续执行之前的while循环。

此时再发送新的数据,又会执行中断,再返回main函数。

注意:发送速度不宜过快,如果在STM32在执行中断函数时且接收中断标志未清除,就接收到了串口发送的下一条数据,中断函数就会卡死,无法跳回main函数。勾选“发送新行”会卡死也是这个原因,因为勾选“发送新行”,发送完数据输入窗口的内容之后,会自动发送回车符,导致接收中断标志来不及清除而卡死。

使用“多条发送”还可以连续发送字符串。

注意:如果发送字符的话需要取消勾选“16进制显示”与“16进制发送”

---------------------------------------------------------

其实,除了使用中断函数,也可以使用USART的状态标志位来判断是否接收到数据或者进行其他处理。

 比如:USART_FLAG_RXNE就是是否接收到数据的标志位,可以该寄存器的状态来判断串口是否发送了数据。需要注意,从该寄存器取值后要及时使用USART_ClearFlag(USART1,USART_FLAG_RXNE)清除该标志位。

 以上就是USART的一个简单使用,如果想要实现更复杂的功能,比如实时监控,还需要上位机的软件支持。

 

创作不易,觉得有用,就点个赞吧!

猜你喜欢

转载自blog.csdn.net/qq_55203246/article/details/125953359