USART是Universal Synchronous/Asynchronous Receiver/Transmitter的英文缩写,意为通用同步/异步串行接收/发送器。它也是单片机中最常用通信方式之一。
常用于单片机与上位机、蓝牙模块、GPS模块等设备的数据传输。
与USART相似的还有一个叫做UART的通信方式,即通用异步串行接收/发送器。所不同的是其只能进行异步通信。
同步与异步通信的区别在于是否有无时钟信号。前面讲过的IIC与SPI都有时钟线,进行传输时,需要通过时钟线使通信双方保持时序一致。
而异步通信则不需要时钟线,发送方按照设定的速率发送数据,接收方按照相同的速率接收数据。所以为了接线简单,很多时候都是使用异步通信,本文也是以异步通信来讲解USART的基本用法。
在UART通信中,发送方发完数据后并不会管接收方是否收到正确的数据,接受方仅仅通过数据校验来确认数据位是否有误。
所以为了让异步通讯顺利进行,通信双方必须保证一致的通信参数:波特率、数据位,停止位数、校验方式。
波特率:
波特(Baud)即调制速率,指的是有效数据讯号调制载波的速率,即单位时间内载波调制状态变化的次数。波特率表示单位时间内传送的码元符号的个数,它是对符号传输速率的一种度量,它用单位时间内载波调制状态改变的次数来表示,波特率即指一个单位时间内传输符号的个数。它是对符号传输速率的一种度量,其值越大,代表传输速率越快。
注意其与比特率的区别,这是两个不同的概念。他们之间的关系为:
式中I为数据量,S为波特率,N为每个符号的数据量。
例如:
波特率为9600,一个符号有10位的数据位,则N=,计算出来的结果就是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的一个简单使用,如果想要实现更复杂的功能,比如实时监控,还需要上位机的软件支持。
创作不易,觉得有用,就点个赞吧!