STM32标准库_05 | 用定时器写通用串口接收
本篇文章主要介绍STM32串口接收不定长数据,采用比较通用的超时分包的方法,不依赖于空闲中断(因为有些单片机是没有空闲中断的),而是使用定时器判断超时,希望能给人以收获。
1.开发环境
软件环境
使用MDK5.25版本,芯片包为STM32F4系列。
硬件环境
开发板:STM32F407VGT6开发板,是一款大容量芯片,最高能跑168MHz。
烧录器:STlink或者Jlink。
2.工程搭建
注释掉串口空闲中断相关代码
直接复制上一篇代码,然后先把串口空闲中断给去掉,在uart1_Init和串口1中断服务函数中把串口空闲中断的部分注释掉(当然用#if代码会更规范)。
添加定时器相关代码
其实用滴答定时器就可以完成本实验的,不过为了介绍一下其他定时器及其中断,所以就选了通用定时器来完成本实验。因为系统初始化SystemInit函数里初始化APB1总线时钟为4分频即42M,APB2总线时钟为2分频即84M(可以查看F407的时钟树),TIM1、TIM8 ~ TIM11的时钟为APB2时钟的两倍即168M,TIM2 ~ TIM7、TIM12 ~ TIM14的时钟为APB1时钟的两倍即84M。
本实验我们选择定时器3(TIM3)来完成,定时器时钟为84M。然后添加定时器初始函数和定时器3中断服务函数。
定时器测试
先定时500ms,用于打印测试,记得把定时器3的初始化放在串口初始化后面,因为我们在定时器中断服务函数里面打印了数据,所以要防止printf打印时串口没有初始化而导致死机。
通过上图也可以发现一个问题,那就是定时器初始化完之后马上就进入了一次中断,所以我们改进一下代码,把
TIM_ClearFlag(TIM3,TIM_FLAG_Update); //清除定时器3中断标志
加入定时器3的初始化函数中
发现达到了预期效果,其实我测试使用TIM_ClearITPendingBit(TIM3,TIM_IT_Update)
也能达到这样的效果(有些帖子说不行,但一切以实际为准)。
定时器使用小结
可能很多人会问,自动重装载值和预分频系数怎么去选择,其实我们可以根据定时的时间结合定时器的位数共同去决定。重装载值是不能超过定时器位数所能容纳的值的,举个例子如果定时器是8位的,那么能计数的值也就是0~255,所以我们不能超过它的范围。
串口超时时间的判断
首先我们来看看串口通信的时序图。
从上图可以看出,空闲的时候串口电平为高电平,起始信号是低电平,然后传输8个数据位,如果有奇偶校验就在数据位后面加上一位奇偶校验位(默认无奇偶校验),还有一位停止位,总共10位数据,代表传输了一个字节的数据。波特率为9600的时候,通信率 I = 9600 * log22 = 9600bit/s。
所以一个字节的传输时间为:1/960Byte(单位秒) 约等于 1.04ms(干脆算作1ms),同理115200波特率快了10倍,所以一个字节的传输时间为:100us。因此我们定时器定时为100us(串口超时分包设为10个字节的时间)。
编写串口接收代码
定义串口结构体
编写串口接收终端函数与串口超时分包函数
编写串口接收处理函数
修改定时器初始化为100us进入一次中断服务程序,并把打印替换成串口超时分包函数
在主函数中加入串口接收处理函数
3.收发测试
4.后记
因为多开中断会影响系统实时性,所以大可不必开一个硬件Timer,可以直接把串口超时分包函数放到滴答定时器中断里面,对于STM32高波特率通信,最好选择DMA+串口空闲中断完成,不然会遇到例如串口溢出中断,或者中间丢字节错字节等各种各样的问题。
这个方法使用各种类型的单片机,是一种通用的串口接收不定长数据的方法,希望大家能有所收获。
代码已全部上传到gitee,希望各位小伙伴们在下载的同时不忘点击Star,地址:https://gitee.com/Notmi/stm32-standard-peripheral-libraries。