背景
在做Nbiot的一个路灯项目,NBiot模块一般都是串口接口,使用AT指令集,对接中国移动onenet平台。先用串口助手去测试,流程测试OK之后需要在MCU上重新写一遍。一开始用的STC15系列的MCU,然后跟平台之间对接协议很多,代码量较大,所以换到了STM32F1系列的MCU。
在STC15MCU上面通过串口接收数据只能老老实实用接收中断来做,每接收一帧数据都需要判断帧头帧尾,一帧结束再处理。之前就听说过STM32串口上面有帧中断,一直没有用过,然后才有了这篇博客。
备注:一帧数据是表示一个或多个字节组成的有含义的字符串。
STM32串口 IDLE中断
IDLE其实是 空闲的意思。IDLE中断叫空闲中断,不叫帧中断。那么什么叫空闲,怎么定义空闲呢?在实际发送数据的时候,比如一串字符串,我们会采用如下方式发送
void uart1_putc(char dat){
SBUF = dat;
while (!TI);
TI = 0;
}
void uart1_puts_n(char *str){
while (*str)
uart1_putc(*str++);
}
void uart1_puts_n("i am handsome");
其实发送的两个字符之间间隔非常短,所以在两个字符之间不叫空闲。空闲的定义是总线上在一个字节的时间内没有再接收到数据。空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。而总线在什么情况时,会有一个字节时间内没有接收到数据呢?一般就只有一个数据帧发送完成的情况,所以串口的空闲中断也叫帧中断。
要怎么开启帧中断呢?其实其他串口配置不用改变,只需要在开启串口接收中断的时候加上一句话就Ok。
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接收中断
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//开启串口空闲中断
然后中断函数如下
void USART2_IRQHandler(void){ //串口1中断服务程序
int clear;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET){ //字符接收中断(接收到的数据必须是0x0d 0x0a结尾)
USART2_RX_BUF[length++] = USART2->DR & 0x0FF;
}else if(USART_GetITStatus(USART2, USART_IT_IDLE ) != RESET){//空闲帧中断
//处理接受的数据
}
}
在普通中断的时候仅仅保存数据,在帧中断的时候需要执行相应处理函数。
如果没有帧中断,必须在接收中断中判断每一个接收数据与帧头帧尾是否相符,效率极低。
之前一直以为天下串口都一样,还是有很多细节的,可以提升不少效率!
改进办法
在原作者地下的评论中,发现有价值的评论,摘抄到此处。
- 空闲中断+串口DMA。不开接收中断,这样收到空闲中断了直接去处理DMA保存过来的数据。这样能减少CPU的负担。
- 使用空闲中断的话,如果连着发两帧数据给它,它有概率会当成一帧数据来接收,这是一个弊端
- 一问一答模式应该没问题。但是如果是主动发送模式,而且很多种数据排队发,总线就没有空闲时间的情况下,就无法进入idle中断了。比如数据量较大、时效性又很高时。你可以自己测试一下,按总线的最大能力来发送。比如波特率设置在9600,每帧数据构造到100个字节左右,每秒发7帧数据,应该就会出现了,评论区给出的解决办法。