HAL库写Flash操作引起的串口ORE错误不再接收的解决

一、问题

使用内部EEPROM(Flash的一部分)储存数据,在写入数据到STM32L0单片机时,出现串口接收数据丢失甚至串口不再接收的现象。在正常情况下在没有写数据到Flash中去的时候所有串口数据都能正常接收,没有数据丢包现象,但是当执行一次写入操作就会导致串口数据丢失,导致接收ORE错误或帧错误等。

二、原因

经查阅手册得知在Flash写入操作过程中,系统时钟停止,这意味着写入Flash期间无法访问Flash中的代码和数据。造成了串口那边RDR寄存器有值了,来不及取出下一个值装进移位寄存器出现了ORE错误。 在HAL库的串口中断里,如果出现ORE错误则会先去读取RDR寄存器的值进入中断回调函数。但执行完之后会关闭串口接收中断进入错误中断回调函数。也就是说由于ORE错误的出现造成了接收中断关闭不再接收的现象。具体串口中断处理参考HAL库的底层函数

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);

三、解决方法

编写错误中断回调函数开启接收中断。

/*******************************************************************************
*函 数 名:HAL_UART_ErrorCallback
*功能说明:串口错误回调函数
*形   参:huart:触发中断的串口
*返 回 值:无
*******************************************************************************/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{		
    if (huart->Instance == USART1)
    {
        HAL_UART_Receive_IT(huart, &gUart1.Temp, RECLEN);
    }
    else if (huart->Instance == USART2)
    {
        HAL_UART_Receive_IT(huart, &gUart2.Temp, RECLEN);
    }
    else if (huart->Instance == USART4)
    {
        HAL_UART_Receive_IT(huart, &gUart4.Temp, RECLEN);
    }
    else if(huart->Instance == USART5)
    {
        HAL_UART_Receive_IT(huart, &gUart5.Temp, RECLEN);
    }
    
}

仅供参考,具体根据串口使用的情况来编写,这边博主用到了以上四个串口。(这边还有一点需要注意,就是出ORE错误时,注意接收中断里用于存放串口数据的缓冲,如果用户一直写Flash则可能造成缓冲区溢出——博主的应用场景遇到,具体根据实际情况判断)。

STM32F10x单片机Flash写操作导致中断不响应问题

昨天遇到一个问题,在写入数据到STM32F103单片机的Flash中时会出现串口中断接收数据丢失现象,但是我设置的串口接收中断优先级是最高的,并且没有哪里将全局中断关闭很长时间(除了操作系统部分内核代码执行的时候关闭全局中断,但是没有占用很长时间,不会导致丢失串口数据)。在正常情况下在没有写数据到Flash中去的时候所有串口数据都能正常接收,没有数据丢包现象,但是当执行一次写入操作(写一个页256个字的数据)就会导致串口数据丢失,导致接收帧错误。
一开始我猜测是不是在擦除和写入Flash的时候系统会屏蔽所有中断,但是没有哪个参考文档中找到这样的说明。后来查阅了一下官方文档PM0042《STM32F10xxx闪存编程手册.en》,在Page11中有一段话很重要:
During a write operation to the Flash memory, any attempt to read the Flash memory will
stall the bus. The read operation will proceed correctly once the write operation has
completed. This means that code or data fetches cannot be made while a write/erase
operation is ongoing.
For write and erase operations on the Flash memory (write/erase), the internal RC oscillator
(HSI) must be ON.
The Flash memory can be programmed and erased using in-circuit programming and in-
application programming.

中文翻译第一段话:在Flash写入操作过程中,任何试图读取Flash的操作都会锁定住总线,在完成Flash写操作之后读取Flash操作会继续执行,这意味着写入Flash期间无法访问Flash中的代码和数据。

这段话让人茅塞顿开,也就是在写入数据到Flash中去的时候无法读取flash中保存的任何数据,包括代码和常量,所以当在写入flash数据的时候如果发生了串口中断的话系统是无法执行中断服务代码的。如果长时间无法执行中断服务代码就会导致数据丢失(写入一个页的数据使用的时长太长了)。

STM32 接收完成中断

 

HAL_UARTEx_ReceiveToIdle_IT(&huart1,(uint8_t*)&Rx_Buf,255);

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    if(huart->Instance==USART1)
    {
        RxCount = Size;
        HAL_UARTEx_ReceiveToIdle_IT(&huart1,(uint8_t*)&Rx_Buf,255);
        Rx_MSG = MSG_COM;
    }
//	else if(huart->Instance==USART3)
//	{
//		esp8266.FramLength=Size;
//		HAL_UART_Transmit(&huart1,esp8266.Rx_Data_Buf,esp8266.FramLength,|
//        esp8266.FramLength);
//		HAL_UARTEx_ReceiveToIdle_IT(&huart1,serial.Rx_Data_Buf,RX_BUF_MAX_LEN);
//	}
}

/*******************************************************************************
HAL库写Flash操作引起的串口ORE错误不再接收的解决
*******************************************************************************/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{ 
if (huart->Instance == USART1)
{
HAL_UARTEx_ReceiveToIdle_IT(&huart1,(uint8_t*)&Rx_Buf_IT,200);
}
else if (huart->Instance == USART3)
{
HAL_UARTEx_ReceiveToIdle_IT(&huart3,(uint8_t*)&Rx3_Buf,200);
}

}