在调试程序时发现,甲方提供的通信协议,只有帧头和信息位,没有帧尾,没有校验位。 刚开始时按照甲方提供的通信协议,简单的写了一个串口中断接收程序。但是在测试中发现:发送协议 FEFE,000,00000000,002,002,002,?000,设备可以正常接收,但在帧尾部添加一个字节或者减少一个字节再发送,设备依然响应。
以下为错误的代码。
// if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
// {
// Counter_USART++;
// c=USART3->DR;
// clear=USART3->DR;//清除中断标志位
// if(c==0x35 && Start_Rec==0 && Counter_USART==1)
// {
// Buf_USART3[0]=c;
// Start_Rec=1;
// }
// else if(Buf_USART3[0]==0x35 && c==0x35 && Start_Rec==1 && Counter_USART==2)
// {
// Buf_USART3[1]=c;
// Start_Rec=2;
// }
// else if(Buf_USART3[1]==0x35 && c==0x61 &&Start_Rec==2 && Counter_USART==3)
// {
// Buf_USART3[2]=c;
// Start_Rec=3;
// }
// else if(Buf_USART3[2]==0x61 && c==0x61 &&Start_Rec==3 && Counter_USART==4)
// {
// Buf_USART3[3]=c;
// Start_Rec=4;
// }
// else if(Start_Rec==4 && Counter_USART<35 && c!= 0x35)
// {
// Counter_Rec++;
// Buf_USART3[3+Counter_Rec]=c;
// }
// else
// {
// Counter_USART=0;
// Start_Rec=0;
// memset(Buf_USART3,0xFF,50);
// }
// if(Counter_USART>34)
// {
// Counter_USART=0;
// Start_Rec=0;
// memset(Buf_USART3,0xFF,50);
// }
// if(3+Counter_Rec==33)
// {
// Finish_Rec=1;
// Start_Rec=0;
// Counter_Rec=0;
// Counter_USART=0;
// }
// else
// Finish_Rec=0;
经分析发现,代码中只使用了接收数据中断。带有正确枕头,错误的位数,接收程序无法判断该包数据是否已经接受完成。经过查找资料发现有这几种方法可以解决这个问题。
1、设置接收超时
2、在协议中添加帧尾或者 协议前加一个字节长度不就完了,根据这个区接收,然后接收完了,告诉APP层或者协议解析层。这种方法实现的前提是大家都按这个做,否则通信失败,适合公司内部使用。
3、开启串口IDLE中断
我使用的是第三种方法
IDLE中断什么时候发生?
IDLE就是串口收到一帧数据后,发生的中断。什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。
如何判断一帧数据结束,就是我们今天讨论的问题。因为很多项目中都要用到这个,因为只有接收到一帧数据以后,你才可以判断这次收了几个字节和每个字节的内容是否符合协议要求。
看了前面IDLE中断的定义,你就会明白了,一帧数据结束后,就会产生IDLE中断。这个中断真是太TMD有用了。省去了好多判断的麻烦。
如何配置好IDLE中断?
下面我们就配置好串口IDLE断
这是串口CR1寄存器,其中,对bit4写1开启IDLE中断,对bit5写1开启接收数据中断。(注意:不同系列的STM32,对应的寄存器位可能不同)
(RXNE中断和IDLE中断的区别?
当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。)
这是状态寄存器,当串口接收到数据时,bit5就会自动变成1,当接收完一帧数据后,bit4就会变成1.
需要注意的是,在中断函数里面,需要把对应的位清0,否则会影响下一次数据的接收。比如RXNE接收数据中断,只要把接收到的一个字节读出来,就会清除这个中断。IDLE中断,如何是F0系列的单片机,需要用ICR寄存器来清除,如果是F1系列的单片机,清除方法是“先读SR寄存器,再读DR寄存器”。(我怎么知道?手册上写的)
程序如下:
首先代开IDLE中断和RXNE中断
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); //20190703
USART_ITConfig(USART3, USART_IT_IDLE, ENABLE); //20190705
USART_Cmd(USART3, ENABLE);
然后是中断处理函数
void USART3_IRQHandler(void)
{
u8 c,clear;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
Buf_USART3[Counter_USART++]=USART_ReceiveData(USART3);
}
if(USART_GetITStatus(USART3,USART_IT_IDLE) != RESET)//接收到一帧数据
{
USART3->SR;//先读SR
USART3->DR;//再读DR
Finish_Rec=1;//j接收完成标志位
}
}
这样串口接收到的数据放在Buf_USART3[Counter_USART++]中,当一帧数据接收完成后 对Finish_Rec=1;置位,表示已经完整的接收完一帧数据,告诉应用层,准备处理信息。