之前老是有个问题困惑着小编,串口这种一字节一字节的接收和发送的机制,而在实际应用中如果收发双方不制定一个协议,没有帧头,帧尾做一帧数据接收开始和结束的标准。或者收发数据长度不固定,怎么去确定每一帧长度不同的帧呢?
面对这种每次发送长度不固定的情况,我最开始采用的是定时器中断加串口中断的方式。
如现在来了一帧长度为86字节的数据,我在串口接收中断中将这86个字节一一入循环队列,假设循环队列的长度是256个字节,现在86个字节已经接收完毕,那么,我在哪里出队来确保这86个字节能一次性出完呢,很多人可能考虑的是主函数轮询出队,但是你可想过,如果主函数里面处理的任务不多,导致轮询过快,如果此时出队列就会导致86个字节出队时不能出完,从而会被多次拆分形成多帧数据,处理数据无效。
所以可以采用定时器中断,每80ms中断一次,每次中断,数据出队列,80ms已经可以实现一次性接收256个字节都会完整的出队了。而且80ms肉眼基本看不出,只能看到这边发送,那边就已经把完整的数据打印出来了,而且256个字节以内任意发多少,都没有数据被拆分,丢失的问题。这样显然是初步解决了任意长度发送,接收的问题,但是在实际应用使用中,如果要求串口高速度,发不定长的数据,就会出现一系列的问题,如现在客户要求任意长度的一帧数据,我连续发送多帧,而且每帧的时间间隔要小于10ms,并且我们之间收发没有通信协议,我想给你发什么就发什么,你要解析数据。面对这种客户要求这种定时器中断加串口中断肯定是不行了的。那么采用什么会有效解决这个问题呢。
仔细看单片机的数据手册关于串口中断的描述,会发现,其实串口中断中,存在一个空闲中断,这个空闲中断帮了我大忙,空闲中断并不是说串口空闲就触发,这样的话岂不是串口如果没有数据过来,空闲中断一直触发,那么整个程序不就一直处于中断之中嘛。空闲中断是指,当最后一个字节接收完毕后,硬件接收中断接收完数据后使得IDLE寄存器中的标志位置高,于是,我们便可以采用此空闲中断标志置高来提醒主程序可以出队,因为一帧数据已经发送完毕了。
1、下图为初始化时使能串口接收中断和空闲中断,及重写串口中断函数方法:
注意:USART5_IRQHandler函数里面,用关于先读SR,再读DR是为了清除空闲中断标志,以至于下一帧数据还能触发此中断,而下面那个变量置1,提醒主程序一帧数据接收完毕,你该去处理了。
2、下图为在主程序里轮询检测数据结束标志,如果标志被置1则进行相应处理。
注意:uart5_handle函数里面据实际情况而实现,如果你只想单纯的转发数据,那么直接判断队列里数据长度,根据长度出队所有数据,然后进行转发就行,如果需要解析处理数据的话,也可以先出队再解析处理。
以上就是关于单片机接收不定长数据的解决方案的全部处理过程,其中如果收发双方如果制定了协议,则可以根据协议收发不定长数据。如果没有串口通信协议则可以使用,串口接收中断加串口空闲中断的方式处理!!小编也是刚刚入坑不久,欢迎各位大佬对不对的地方评论。谢谢!!