串⼝超时处理原理及实现
原理:⼤部分串⼝都是基于⼀字节、⼀字节传输,检测到特定的字符(⽐如换⾏或者空格)才判定⼀帧数据结束,这样的传输机制在⾃⼰调试时可以⽤,但实际运⽤其实⽤的很少,最⼤的坏处是cpu会“死等”特定字符,另外,若是由总线⼲扰出现的特定的字符,若程序同样判定帧起始(或停⽌)符,这明显是错误的。我们需要⼀帧⼀帧的传输,这样,就需要字节超时处理了,即只要字符与字符之间间隔超过⼀定的时间,那么就判定字符是⼀帧的结束。
⼤部分教程没有提到可能是为了降低⼤家的学习难度,这⾥提供⼀份参考代码,STM32的所有串⼝都加进去了,全部测试通过,也经过实际项⽬(⾮精确严格要求)的检验。若不需要⽤到某些串⼝,只需要把app_conf.h,⽂件⾥的相关宏关闭即可。⽐如,不需要⽤到串⼝3、4、5,那么只需要注释掉app_conf.h⾥的USING_USART3、USING_USART4、USING_USART5即可。同时,串⼝相关配置波特率、缓冲区⼤⼩等等也在此⽂件,⼤家看看注释就明⽩了。默认全部开启,字节超时时间可设,例如USART1_RECEIVE_OVERTIME这类名字的宏。最后⼀个配置是printf 输出串⼝的选择,默认为串⼝⼀。
这个实现五个串⼝公⽤⼀个systick,不需要每个串⼝需要单独的硬件定时器;⼆级缓冲区以加⼤吞吐量,⼀个接受缓冲区,接受缓冲区负责接收;⼀个帧准备缓冲区,帧准备缓冲区有⼀个准备好的标志US
ART_ready_buf_ok,应⽤程序可检测这个标志看是否有⼀帧数据存进来。还有⼀个帧长度USART_ready_buf_len以指⽰准备好的缓冲区的帧长度。代码先发放出去,⼤家先试试,稍后有时间在说说具体事怎么实现的,年底⽐较事多,⼤家见谅。
对了,测试代码是简单地回发,即5个串⼝回发⾃⼰收到的数据帧,你可以做个测试,若字节超时时间设的⽐较长(在app_conf.h⽂件的USART1_RECEIVE_OVERTIME宏),那么你在串⼝调试助⼿⾥不停地点发送,等到你停下来,才会回发你刚发的内容。
还有,这个⼯程LED灯部分也有些意思,除了亮状态和灭状态,还有第三种状态————闪烁,⽽且闪烁的次数与闪烁的间隔软件可配置。若有兴趣⼤家也可以试试,配置也在app_conf.h⾥,当然,你的板⼦与我的板⼦肯定不⼀样,那么"AunonBoard_led.h"⾥的各个
LED_PORT和LED_PIN宏定义也要修改。
(⼆)、软件定时器
软件定时器是由⼀个硬件定时器实现的多个定时器,在定时不要求⾮常精确的情况下可以⽤到,⽐如串⼝字节超时等等,特点是需要多少个定时器就可以拥有多少个定时器,不受硬件限制。这⾥的软件定时器源代码soft_timer.c和soft_timer.h两个⽂件,在不做任何改动的情况在ARM和51下测试通过,其他平台未知,(没有相应的硬件平台测试)。先放代码和⼯程,后⾯有时间在细说。
使⽤⽅法(平台⽆关):
(1)硬件定时器初始化,中断配置什么的不要忘了,具体怎么实现⽆所谓,只要能不停地周期性中断(stm32的systick最合适了),中断间隔也是软件定时器的最⼩能分辨的间隔,然后将软件定时器刷新函数void timer_periodic_refresh()--没有参数--加到你的定时器中断服务函数⾥。
(2)定义⼀个定时器,如:struct soft_timer timer,struct soft_timer 是软件定时器的结构体,定义在soft_timer.h⽂件中,timer是你的定时器的名字。
(3)软件定时器链表复位,soft_timer_list_reset(),⽆参数;
(4)然后添加刚才你定义的定时器timer添加到定时器链表add_timer(&timer,timer_over_proc,time_count),第⼀个参数是你要添加的定时器结构体的指针,第⼆个参数是定时间到了你要调⽤的超时函数,第三个参数是定时时间,单位是你的周期性中断时间间隔。
(5)开始启动定时器, start_timer(&timer),参数是你要启动的定时器的结构体指针。
这样,等超时后,就会⾃动调⽤timer_over_proc()函数,(像不像我们⾃⼰实现的软件定时器中断?)
注意,这个实现是⼀次性定时器,即⼀次超时后不再触发,若需要周期性触发,那么可以再timer_over_proc()函数⾥⾯重装初值--reload_timer(&timer,time_count),第⼀个r参数是待启动定时器结构体指针,time_count是重装值,单位依旧是你的硬件定时器中断时间间隔,然后再启动定时器start_timer(timer)即可。
若还有使⽤上的问题,可参考两个测试⼯程的⽤法,⼀个stm32,⼀个51。STM32测试⼯程以systick建了3个软件定时器,分别以
0.3s,0.5s,0.7s的时间间隔闪烁3个led灯。C51⼯程以定时器0建⽴3个软件定时器闪烁led灯。具体代码分析,后⾯有时间在细说。
STM32的测试结果视频和代码见附件。
(三)软件定时器实现串⼝字节超时处理
前⾯第⼀个⼯程是以前的⼀个⼩项⽬上⽤的,原封不动地发了上来,结构除了⾃个⼉⽐较清晰外可能不会有⼈知道我再⼲什么。。。。。。所以写了这个软件定时器的版本,并做了充分的中⽂注释。欢迎指正!
同前⾯的⼀样,每个串⼝⽤了两个缓冲区,⼀个接收缓冲区,⼀个准备缓冲区。当串⼝中断函数⾥⾯
收到⼀个字符时,放⼊接收缓冲区,并开始启动定时器,若在超时范围内没有收到下⼀个字符,超时函数会被⾃动调⽤,将接收缓冲区的数据转移⾄准备缓冲区内,并将准备标志置位,以供应⽤程序查询。
测试⼯程⾥⾯使⽤了串⼝1、2,简单回发收到的数据帧,为了对⽐,这两个串⼝的字节超时设置不⼀样,⼀个50ms,⼀个500ms,所以串⼝2在发送⽐较快的情况会被认为数据没有结束,直到⾄少停500ms才会回发。在我的板⼦上测试通过,由于⼿上没有串⼝3、4、5的板⼦,所以就没添加,需要的朋友可⾃⼰添加⼀下,还是⽐较容易的。
写完了,不知道有⼈试过我这个软件定时器么?以满⾜下我⼩⼩的虚荣⼼啊,哈哈哈。
(四)
timeout on t2 timer在串⼝通信过程中超时处理⼏乎是必须的。
如果⽤过Modbus协议,它的判断结束的唯⼀条件就是超时。
本⼈应⽤的部分代码共享下:
//检测超时函数供定时器中断调⽤-1ms⼀次
// t为超时时间
__inline void Chk_TimeOut(u8 t)
{
if(!OK && (Index>=8) && (++S >=t) ) //超时之后,开始重新解码
}
//接收函数,供USART中断调⽤
__inline void Recieve_MSG(char ch)
{
if(!OK)
{
uartRMsg.Buf.Index ++] = ch; //赋值
Index>=8 && uartRMsg.d!=16)//其他条件成⽴的时候也可进⼊解码程序<;此程序为Modbus应⽤,可改为接收到结束符等>
}
}
//供主函数调⽤
void MSGTransfer(void)
{
if(!OK) return;
//......数据解析部分
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。