一、串口基础操作
-
打开GPIO和USART的时钟
-
配置GPIO(TX应为复用推挽输出,RX应为浮空或上拉输入)GPIO_Init初始化
-
配置USART(波特率直接写数,USART_Init函数会计算BRR寄存器重小数和整数部分的数值),使用USART_Init初始化,USART_Cmd使能
-
配置NVIC,中断源选择USART,使用NVIC_Init初始化
-
基于USART_SendData库函数编写串口数据处理函数,包括:
发送:
5.1 Serial_SendByte字节发送函数。使用USART_SendData库函数发送一个字节的数据,等待数据被转移到移位寄存器中退出函数(等待TXE(发送数据寄存器空 (Transmit data register empty))标志位置1时说明数据已经从数据寄存器移到了移位寄存器,认为已发送完毕,可以进行下一个数据的发送)。
5.2 Serial_SendArray 数组发送函数。使用循环调用5.1中的Serial_SendByte函数发送数组中的数据。
5.3 Serial_SendString 字符串发送函数。使用循环调用5.1中的Serial_SendByte函数发送字符串中的数据。
5.4 Serial_SendNumber 数字发送函数。使用循环调用5.1中的Serial_SendByte函数发送数字的。。。万、千、百、十、个位发送出去。实现方法是用待传输的数字分别对。。。10000、1000、100、10、1求商再对10求余,分别获得最高位至最低位的值。(例如567,对100求商得5,5对10求余得最高位百位5,567对10求商得56,56对10求余得6,567对1求商得567,567对10求余得最低为7.)。
5.5 重定向printf函数。三种方法,第一种把fputc重定义为调用Serial_SendByte发送数据并返回该数据。第二种方法是使用sprintf将待发送打印格式化为字符串,调用5.3中的Serial_SendString函数将字符串发发送出去,不用担心printf被某个端口独占。第三种方法是编写Serial_Printf函数,调用vsprintf将带输出内容格式化到一个字符串中,调用5.3中的Serial_SendString函数将字符串发发送出去。读取:
5.6 使用查询的方法读取串口数据。在循环中用USART_GetFlagStatus库函数查询RXNE(读数据寄存器非空 (Read data register not empty))标志位,位置1时说明移位寄存器中的数据被转移到USART_DR寄存器中了,使用USART_ReceiveData库函数将该数据存到变量中。该标志位在读数据寄存器后会自动清零,所以不用手动清零。
5.7 使用中断的方法读取串口数据。在USART1_IRQHandler中断服务函数中使用USART_GetITStatus库函数判断是否RXNE(读数据寄存器非空 (Read data register not empty))标志位,位置1时说明有数据传送进来了,使用USART_ReceiveData库函数将该数据存到变量中。
二、数据包
-
发送数据包
编写Serial_SendPacket函数,①调用5.1中的Serial_SendByte函数发送包头,②调用Serial_SendArray发送数据,③调用调用5.1中的Serial_SendByte函数发送包尾 -
编写中断服务函数,使用状态机的方法接收数据包。
数据包分为HEX数据包和文本数据包,这两种数据包又分为可变包长和固定包长两种。
2.1 对HEX数据包,因传感器中的数据都是连续变化的,即使极少数据丢失也有后来的数据提供信息,而且不停变化的数据可能导致包头包尾难以判断,所以使用固定包长比较合适,本例中使用0xFF作为包头,0xFE作为包尾:
在USART1_IRQHandler中断服务函数中,使用USART_GetITStatus判断RXNE标志位是否置1,使用RxHexState变量作为状态标志。①当RxHexState为0时,在等待包头,当读取到的数据为0xFF时认为已读到包头。②RxHexState设置为1,开始读取数据包中的数据,通过变量进行计数,当读满数据包长度后,③RxHexState设置为2,等待包尾,当读取到的数据为0xFE时认为读到包尾,并将RxHexState设置为0,回到初始装填。此时修改标志着是否读取到数据包的全局变量Serial_RxFlag为1,意思是向main函数报告已读取到一个数据包。
2.2 对文本数据包,每句话都是不同的长度,可以使用特殊符号作为包头包尾,使用可变包长,本例中使用@作为包头,换行符作为包尾:
在USART1_IRQHandler中断服务函数中,使用USART_GetITStatus判断RXNE标志位是否置1,使用RxHexState变量作为状态标志。①当RxHexState为0时,在等待包头,当读取到的数据为’@‘时认为已读到包头,②将RxHexState设置为1,先判断数据是否为’\r’,如果不是则为文本数据,将其存到字符数组中,如果是,③则将RxHexState设置为2,等待包尾中的第二个字符’\n’,此时认为读到完整的包尾,修改标志是否读取到数据包的全局变量Serial_RxFlag置1,向main函数报告已读取到一个数据包。 -
在main函数的while循环中用if语句判断标志是否读取到数据包的全局变量Serial_RxFlag是否是1,如果是1则有数据读进来,可通过判断语句执行响应动作。
注释:
\r是指Carriage Return(即为CR),Carriage 是打字机上写字的打印头,Carriage Return是让光标去到行首,是ASCII码的0x0D
\n是指Line Feed(即为LF)是指整体向上移动一行,是ASCII码的0x0A
微软的 MS-DOS/Windows 操作系统采用\r\n这两个字符来实现文本换行效果。
Unix/Linux 操作系统采用单个字符\n来实现文本换行效果。
Mac 操作系统采用单个字符\r 来实现文本换行效果.