232灯带板遇到的问题及解决办法
...About 2 min
232灯带板遇到的问题及解决办法
1. 串口数据丢失问题
问题描述
先看原程序,下面这段代码是串口中断发生之后执行的方法。在STM32上,串口每收到一个字节的数据就会触发一次中断,中断触发后,HAL库自动帮我们把接收缓冲区的数据复制到了aRxBuffer[0]当中。
程序当中首先新建了一个接收缓冲区USART_RX_BUF和flag USART_RX_STA。
当缓冲区的数据到达指定长度并且是以0XFF开头时,则USART_RX_STA最高位置1,此时消费者需要处理数据。
当消费者处理完成数据后,USART_RX_STA最高位置0,此时生产者可以继续接收串口数据。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
if((USART_RX_STA&0x8000)==0)//数据尚未处理完成
{
if (aRxBuffer[0] == 0XFF)
{
USART_RX_STA = 0;
}
// 存储接收到的字节
USART_RX_BUF[USART_RX_STA & 0X3FFF] = aRxBuffer[0];
USART_RX_STA++;
if ((USART_RX_STA & 0X3FFF) >= 17)
{
USART_RX_STA |= 0x8000; //需要处理数据
}
if (USART_RX_BUF[0] != 0XFF)
{
USART_RX_STA = 0;
}
}
}
}
这样做会导致一个问题,当消费者在处理数据没有来得及释放锁时,串口发生两次及以上的中断就会把没有取走的字节覆盖掉,导致数据丢失。
解决方法
最简单的方式是,在主线程中快速的把数据拷贝到其它内存空间,为了避免产生数据不一致的问题,需要在拷贝之前禁用中断,拷贝完成后再恢复。
__disable_irq(); // 禁用中断
memcpy(data, USART_RX_BUF, size());
USART_RX_STA=0; // 串口接收标志位置0,等待下一次接收
__enable_irq(); // 重新启用中断
但直接禁用中断并不是一个好的解决办法,其它的方法还有环形缓冲区和消息队列等,但在裸机程序当中实现都比较复杂,所以我决定使用freeRTOS解决这个问题:基于 freeRTOS 的消息队列、多任务和使用DMA、空闲中断、缓冲区彻底解决串口接收数据丢包拆包粘包的问题