
资料转自别处,分享一下供大家参考,感谢原创的付出。 在一项目中,使用STM32作为主控,程序运行一段时间后概率出现主循环卡死现象。 0 j O' u/ q1 ]/ Y; M. S问题分析如下: 1、程序USART2不停接收并处理串口数据,波特率115200; 2、主循环卡死; 3、USART1中断及TIM2中断响应函数运行正常;(USART1及TIM2中断优先级均比USART2高) 4、出现现象后,拔掉USART2的接收数据线,现象不能回复正常; 5、出现现象后,拔掉后再插入USART2的接收数据线,现象不能回复正常; 6、并未出现HardFault现象; $ }7 F4 s, s# d" B E0 F i/ C# e基于以上4点,可能原因如下: 1、USART2接收中断标志没有清除; 2、堆栈数据溢出,导致程序异常; 3、USART2中断重入导致异常; 4、USART2中断函数被异常响应; 5、USART2中断ERR; " h5 v" `% d3 j" b. {6 B对于以上可能原因一一分析: 1、中断接收标志清楚问题: (1)USART2接收中断响应函数如下: [url=]![]() 1 void USART2_Istr(void) 2 { 3 if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) 4 { 5 USART_ClearFlag(USART2, USART_FLAG_RXNE); 6 USART_ClearITPendingBit(USART2, USART_IT_RXNE); 7 Data = USART_ReceiveData(USART2); 8 //Process Data 9 }10 }[url=] ![]() (2)出现现象后,通过Usart1中断获取到如下信息: a. USART_GetITStatus(USART2, USART_IT_RXNE) == RESET b. USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET c. 执行USART_ClearFlag(USART2, USART_FLAG_RXNE)及 USART_ClearITPendingBit(USART2, USART_IT_RXNE)后无法恢复正常; 结论:与USART2 RXNE中断标志无关。 8 F, \/ L* P0 i+ \5 i. t2、堆栈数据溢出,导致程序异常; (1)使用2倍栈空间,问题存在,概率不会降低; (2)使用0.5倍栈空间,问题存在,概率不会提高; (3)使用0.25倍栈空间,程序运行进入HardFault; 结论:与堆栈无关。 : Y4 H# N5 v3 u5 ^( x/ p3 J3、USART2中断重入导致异常; (1)使用标志法,确认出现问题时,中断响应函数没有重入; 结论:中断响应函数没有重入。 4、USART2中断函数被异常响应; (1)USART2中断函数可以被正常调用,只是不停进入中断响应函数,卡死主循环; (2)检查程序Map,没发现与中断响应函数地址相同的函数; (3)检查中断向量表,没发现异常; 结论:中断函数没有被异常调用; . Q; S, e- U7 y% h) K5、USART2中断ERR; (1)关闭USART2中断,主循环恢复正常; (2)启动USART2中断,主循环卡死; (3)获取到DR=0x0000; (4)USART_GetITStatus取到:RXNE=0,PE=0,TXE=0,TC=0,IDLE=0,LBD=0,CTS=0,ERR=0,ORE=0,NE=0,FE=0; (5)通过USART_ClearITPendingBit清除CTS,LBD,TXE,TC,RXNE,IDLE,ORE,NE,FE,PE均无法恢复正常; (6)通过USART_GetFlagStatus: a.第一次:CTS=0,LBD=0,TXE=1,TC=1,RXNE=0,IDLE=1,ORE=1,NE=0,FE=0,PE=0 b.第二次:CTS=0,LBD=0,TXE=1,TC=1,RXNE=0,IDLE=0,ORE=0,NE=0,FE=0,PE=0 c.第三次:CTS=0,LBD=0,TXE=1,TC=1,RXNE=0,IDLE=0,ORE=0,NE=0,FE=0,PE=0 (7)通过USART_ClearFlag清除CTS,LBD,TXE,TC,RXNE,IDLE,ORE,NE,FE,PE均无法恢复正常; 分析: (1)为什么通过USART_GetITStatus获取了所有中断标志,均为RESET(TC、TXE中断没开),还会进中断? (2)为什么通过USART_ClearITPendingBit清除了所有中断标志,还会进入中断? (3)为什么关闭USART2中断后再次启动它还会进入卡死状态? (4)为什么通过USART_GetFlagStatus第一次和第二次读的不一样?而且USART_ClearFlag清掉所有Flag,也没法恢复正常? - Z# s& Y$ ?2 ]$ W6 f* }带着以上几个疑问,查看了参考手册,才恍然大悟!如下: (1)打开RXNEIE,默认会同时打开RXNE和ORE中断。 (2)必须第一时间清零RXNE,如没及时清零,下一帧数据过来时就会产生Overrun error! (3)错误就是ORE导致的 出现错误时,读了RXNE=0,出错应该是上图打勾的情况,如下 (4)如文档说明,要清除ORE中断需要按顺序读取USART_SR和USART_DR寄存器! 那就是说USART_ClearFlag清掉所有Flag后,还必须读一遍USART_DR寄存器! 经过测试出现问题后依次读读取USART_SR和USART_DR,程序回复正常! (5)那还有一个问题,为什么USART_GetITStatus读不到ORE中断标志? 读USART_GetITStatus函数就知道了,只有CR3的EIE置1且SR的ORE置1,读出来USART_GetITStatus(USART2, USART_IT_ORE) 才是 SET。 见CR3的EIE位说明。 解决办法,出现通过接收时,通过USART_GetFlagStatus读取ORE,若不为RESET,则读取DR数据丢弃。 修改如下: - q w% n. z! F0 }; |* R[url=] ![]() 1 void USART2_NewIstr(void) 2 { 3 if (USART_GetFlagStatus(USART2, USART_FLAG_PE) != RESET) 4 { 5 USART_ReceiveData(USART2); 6 USART_ClearFlag(USART2, USART_FLAG_PE); 7 } 8 9 if (USART_GetFlagStatus(USART2, USART_FLAG_ORE) != RESET)10 {11 USART_ReceiveData(USART2);12 USART_ClearFlag(USART2, USART_FLAG_ORE);13 }14 15 if (USART_GetFlagStatus(USART2, USART_FLAG_FE) != RESET)16 {17 USART_ReceiveData(USART2);18 USART_ClearFlag(USART2, USART_FLAG_FE);19 }20 21 if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)22 { 23 USART_ClearFlag(USART2, USART_FLAG_RXNE);24 USART_ClearITPendingBit(USART2, USART_IT_RXNE);25 Data = USART_ReceiveData(USART2);26 }27 }[url=] ![]() - c6 Y h& `! c0 ~) F0 o% Y1 n" g 总结: 1、看文档!看文档!还是看文档!(重要的事情要说3遍) 2、库函数用的时候,也要注意其实现,稍有不慎就可能用错。 3、注意USART_GetFlagStatus与USART_GetITStatus的区别,还有中断响应机制。 4、任意时候都要考虑出错处理。 9 b9 D* v4 D# d& x3 A |
2、库函数用的时候,也要注意其实现,稍有不慎就可能用错。
3、注意USART_GetFlagStatus与USART_GetITStatus的区别,还有中断响应机制。1 z7 k6 |/ n W. G& \
4、任意时候都要考虑出错处理。
总结的非常好,支持。
串口接收函数HAL_UART_Receive_IT调用后,会有三种结果:+ A# I9 ^" ?9 K4 y) R
第一种:只调用HAL_UART_RxCpltCallback回调函数
第二种:HAL_UART_RxCpltCallback和HAL_UART_ErrorCallback回调函数都调用$ O6 [2 L R! h' v+ p) x
第三种:只调用HAL_UART_ErrorCallback回调函数
! b$ T/ x3 c4 }& T
长时间(半小时以上)发送大量串口数据时,单片机会因为来不及处理串口数据出现Overrun的错误,此时USART_SR_ORE位为1。4 G3 q, {8 R4 ?' B. g, M! ]
到了一定时候,当USART_SR寄存器的USART_SR_RXNE为0,且USART_SR_ORE位为1时,就会出现第三种情况,只有HAL_UART_ErrorCallback这个函数会被调用,而且USART_SR_ORE位始终不能自动清除,串口就不能继续接收数据,USART_SR_RXNE位一直为0,这样就产生了死循环。1 {# n9 O" ^: M! m* U& V
函数调用HAL_UART_Receive_IT开始接收数据,而因为ORE=1,马上产生串口中断,进入HAL_UART_ErrorCallback,然后接收自动中止(注意看UART_EndRxTransfer函数,里面只是简单地中止接收)。RXNE位一直为0,永远进不了HAL_UART_RxCpltCallback。而HAL库函数又没有清除ORE位的功能,所以即便再次调用HAL_UART_Receive_IT也是同样的结果。5 n3 g3 V2 T+ j( d
# K$ X" R* y; S, f9 Y# n, {; C* Y
手册里面可以看到,ORE位是只读位,只能用特殊的方法清除:- i7 H2 B; }+ o4 g/ l" V5 L: Z( c
* |* s; o1 d7 H/ p7 g
Bit 3 ORE: Overrun error# p2 s. j7 ]1 v# B3 v* K
This bit is set by hardware when the word currently being received in the shift register is
ready to be transferred into the RDR register while RXNE=1. An interrupt is generated if
RXNEIE=1 in the USART_CR1 register. It is cleared by a software sequence (an read to the
USART_SR register followed by a read to the USART_DR register).( G4 @$ r8 z9 r7 _
0: No Overrun error
1: Overrun error is detected$ l d. E3 c6 g0 N* h: b
Note: When this bit is set, the RDR register content will not be lost but the shift register will be0 f) ~7 [$ U3 x( I
overwritten. An interrupt is generated on ORE flag in case of Multi Buffer% P4 v0 A/ [* R% ]* Y) y- d
communication if the EIE bit is set.
清除ORE位的方法是,先读USART_SR寄存器,再读USART_DR寄存器。只要清除了ORE位,就可以打破这种死循环的状态。
HAL库里面有一个__HAL_UART_FLUSH_DRREGISTER宏可以用来读DR寄存器:" o+ ]0 u; x: V0 L
#define __HAL_UART_FLUSH_DRREGISTER(__HANDLE__) ((__HANDLE__)->Instance->DR)' |# o; f% x$ {. j
所以,我们只要在ORE错误产生时,读一下DR寄存器,就可以解决这个bug,退出这种死循环。0 q( R4 X& z% s6 e/ I, \- }; B9 d; |
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)6 \1 J S! t! n) V+ [
{1 j3 S8 c! a' Z6 \0 ~8 Q
if (HAL_UART_GetError(huart) & HAL_UART_ERROR_ORE)* g$ I: ^1 b# A
__HAL_UART_FLUSH_DRREGISTER(huart);* B3 g# E& B9 {) P6 D9 O
}
另外,调试的时候要特别注意,不要把Keil的USART寄存器窗口打开了。因为Keil在读USART1->DR寄存器的时候,会导致USART_SR_RXNE位被清除,程序就可能收不到串口数据。* G9 E5 H. p( Z# r" t! q* ?
参考程序:& I- Q+ G$ ^5 b$ H3 m, y
程序的里面有两个任务,一个任务不停地在用printf打印,往串口输出数据,另一个任务专门负责接收串口字符。发送和接收函数用printf_mutex这个互斥量来保护(printf_lock和printf_unlock)。接收串口字符时等待xSemaphoreTake信号量,若xSemaphoreTake信号量有信号,说明串口收到了字符。然后判断接收是否已停止,若huart1.RxState == HAL_UART_STATE_READY,说明接收已停止,调用HAL_UART_Receive_IT函数重新开始接收。3 ~0 m% j" {8 I X6 w8 j6 b
在HAL_UART_RxCpltCallback回调函数中使能信号量唤醒接收线程。在HAL_UART_ErrorCallback函数中处理Overrun错误(ORE),及时读取DR寄存器清除ORE位,防止陷入死循环。& @) G) w9 T0 U$ K2 x
* G4 c6 l' b" Z( F& [
特别注意,while(1)里面的if (huart1.RxState == HAL_UART_STATE_READY)不能放到if (bret == pdTRUE)内,因为调用HAL_UART_Receive_IT后有可能只有HAL_UART_ErrorCallback回调函数被调用,这个时候是没有唤醒信号量的,线程就不能够进入if (bret == pdTRUE)里面,调用HAL_UART_Receive_IT恢复数据接收。6 ~* |1 h& X I* x
————————————————& r7 d: i9 d" X1 K7 x- G. B, R @
版权声明:本文为CSDN博主「巨大八爪鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:http://blog.csdn.net/ZLK1214/article/details/105624510