[首先简单研究一下什么时候会出现overrun的问题,配置正常的HAL串口中断接收如下 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* judge interrupt source */
if(huart ->Instance == USART2)
{
//send data to huart1 from huart2
HAL_UART_Transmit(&huart1, (uint8_t*)recv_buf, 3,0xFFFF);
//enable RX IT
HAL_UART_Receive_IT(huart, (uint8_t*)recv_buf, 3);
}
} [
可以看出,分三种情况:[其中,触发两次接收缓存溢出的时候,就会进入Overrun Error。 [比如说BUF长度为3,连续发两次长度为4的数据,就会进入ORE。或者说第一次发送长度为2的数据,然后接着发送两次长度为3的数据,也是会触发ORE的,后续的两次长度为3的数据因为有之前一条长度为2的铺垫,都是视作BUF溢出的数据。但是如果发送一次长度为4的数据之后,后续发长度一条长度为2的数据,就会进入正常状态,是有弹性的。弹性限度为BUF长度的两倍,也就是说发送一次长度为6的数据,会立即进入ORE。 [ORE状态下,串口进入ORE溢出中断,无法继续接收数据,但是可以发送数据,相当于接收数据不会触发中断了。 [对于ORE溢出常用解决方案有下: [无论哪一种方法都要理解HAL_UART_RxCpltCallback的实现原理: HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中断处理函数) UART_Receive_IT(UART_HandleTypeDef *huart) (接收函数) HAL_UART_RxCpltCallback(huart)(中断回调函数)
[从上到下,层层封装和调用。 - void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
- {
- uint32_t isrflags = READ_REG(huart->Instance->SR);
- uint32_t cr1its = READ_REG(huart->Instance->CR1);
- uint32_t cr3its = READ_REG(huart->Instance->CR3);
- uint32_t errorflags = 0x00U;
- uint32_t dmarequest = 0x00U;
- /* If no error occurs */
- errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
- //close ORE check
- //errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_NE));
- if (errorflags == RESET)
- {
- /* UART in mode Receiver -------------------------------------------------*/
- if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
- {
- UART_Receive_IT(huart);
- return;
- }
- }
复制代码[ 想要关闭ORE中断,将errorflags中的USART_SR_ORE 这一位去掉即可,相当于对ORE错误不予检查,经过实际的测试是可行的,实际的表现就是出现ORE溢出之后,串口只接受固定BUF长度的数据,对于溢出的数据直接丢弃,但是还需要考虑ORE溢出之后,那个时刻中BUF肯定是有数据,但是由于没有清除BUF,后续的数据直接进来会导致错位,因此想要真正实现完美的溢出数据丢弃处理且不影响后续的数据,是要在程序中加入清除BUF的指令的。但是这些方法都会影响HAL库的底层代码,包括去掉USART_SR_ORE 这一位,这也是STM32IDE的一点的不好的地方,底层文件是没有用户代码入口的,修改IOC文件之后,一切都会回到最初的起点。 [ 其次就是双重缓存,具体的就是将BUF长度设置为1,这样子每一次收到一个字节的话都会调用回调函数,为什么长度长度大于1的时候会发生ORE,而1的时候可以接受很长的数据而不ORE呢,我猜是因为1的时候将某些位置位了,间接关闭了ORE,然后用户自己定义BUF区,一个一个去读取写入,这个方法还可以实现不定长的接收,设置数据结束帧位就可以了。缺点也很明显,当数据很长的时候,中断要进入很多次,有点占资源了。 [用户回调函数HAL_UART_RxCpltCallback只有接收到设定的BUF的长度才会调用一次,而不是有字节就调用,实现的方法在UART_Receive_IT函数中: - if (--huart->RxXferCount == 0U)
- {
- /* Disable the UART Data Register not empty Interrupt */
- __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);
-
- /* Disable the UART Parity Error Interrupt */
- __HAL_UART_DISABLE_IT(huart, UART_IT_PE);
-
- /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
- __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
-
- /* Rx process is completed, restore huart->RxState to Ready */
- huart->RxState = HAL_UART_STATE_READY;
-
- #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
- /*Call registered Rx complete callback*/
- huart->RxCpltCallback(huart);
- #else
- /*Call legacy weak Rx complete callback*/
- HAL_UART_RxCpltCallback(huart);
- #endif /* USE_HAL_UART_REGISTER_CALLBACKS */
复制代码
[if (--huart->RxXferCount == 0U)这一句,每次字节来都会调用UART_Receive_IT,然后RxXferCount减1,这个变量就是一开始设置的BUF接收长度,只有等这个到零了,才会调用HAL_UART_RxCpltCallback(huart)。 [最终,因为在项目中串口是需要来高频率接收固定长度的测量数据的,但是在之前要用串口发送上电指令,这个指令会返回一个和测量数据长度不同的ASK信号,处理方法就是在上电之后,再开启串口接收,这样子就可以设置BUF长度为固定长度,同时稍微降低测量的频率,让串口接收有足够的时间,避免ORE的产生。
|