MCAL接口写完FLS后,偶发性出现一直不空闲问题
-
#define FLASH_WRITE_UNIT ( 32 ) // 固定写入单位 #define FLASH_TIMEOUT ( 10000000 ) // 超时时间 bool bsw_fls_write_pflash1_1mb( uint32_t AddrOffset, const uint8_t* data, uint32_t len ) { uint32_t startAddress = 0; uint32_t alignedLen = 0; uint32_t i = 0; uint32_t chunkSize = 0; uint32_t timeout = 0; uint8_t alignedData[ FLASH_WRITE_UNIT ] = { 0xFF }; // 预填充 0xFF uint8_t readBuffer[ FLASH_WRITE_UNIT ] = { 0 }; // 读取缓存初始化 if ( data == NULL || len == 0 ) { return false; } startAddress = Fls_Config.SectorList[ FlsConf_FlsConfigSet_PFLASH1_1MB ].SectorStartAddress + AddrOffset; alignedLen = ( ( len + ( FLASH_WRITE_UNIT - 1 ) ) / FLASH_WRITE_UNIT ) * FLASH_WRITE_UNIT; // 向上取整到 32 字节对齐 // 按 32 字节批量写入 for ( i = 0; i < alignedLen; i += FLASH_WRITE_UNIT ) { chunkSize = ( i + FLASH_WRITE_UNIT <= len ) ? FLASH_WRITE_UNIT : ( len - i ); memset( alignedData, 0xFF, FLASH_WRITE_UNIT ); // 先填充 0xFF memcpy( alignedData, data + i, chunkSize ); // 复制实际数据 // 超时等待 Flash 空闲 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == 0 ) { return false; } } // 写入 Flash timeout = 50; while ( --timeout > 0 ) { if ( !Fls_Write( startAddress + i, alignedData, FLASH_WRITE_UNIT ) ) { break; } if ( timeout == 0 ) { return false; // 写入失败 } }; // 超时等待 Flash 空闲 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == 0 ) { return false; } } memset( readBuffer, 0, FLASH_WRITE_UNIT ); // 确保读取缓冲区清空 timeout = 50; while ( --timeout > 0 ) { if ( !Fls_Read( startAddress + i, readBuffer, FLASH_WRITE_UNIT ) ) { break; } if ( timeout == 0 ) { return false; // 写入失败 } }; // 等待读取完成 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == 0 ) { return false; } } // 进行数据比对 if ( memcmp( readBuffer, alignedData, FLASH_WRITE_UNIT ) != 0 ) { return false; // 校验失败 } } return true; }
以上是我写MCU内部FLS的功能代码,作用是把数据写入1mb的pflash1,以实现A/B SWAP。
现在发现,在调用完Fls_Read后,在等待读取完成代码段中,Fls_GetStatus( )一直不返回MEMIF_IDLE,导致任务卡顿。timeout = 50; while ( --timeout > 0 ) { if ( !Fls_Read( startAddress + i, readBuffer, FLASH_WRITE_UNIT ) ) { break; } if ( timeout == 0 ) { return false; // 写入失败 } }; // 等待读取完成 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == 0 ) { return false; } }
请问是我这个函数代码的逻辑有问题吗?
-
正常情况下这个逻辑是没有问题的,但是你们这个写之前有擦除操作吗?代码里面没有体现擦除的动作,这个一定要求有。另外就是你可以在读取之前增加一个GetResult, 检查一下前面一次写的操作是否成功。按照你的现象描述,可能是因为写flash过程掉电,从而产生了ECC错误,上电之后如果没有擦除再写,那么所有写都是失败的,再读到产生ECC错误的位置,就会导致MCU进入busfault.
-
houjun_xiao 每次写之前都有擦除操作,也会判断是否空闲。现在代码逻辑上,读取之前已经有一个GetResult了。这个ECC错误,是只能通过擦除操作来清除吗?
-
单Bit的错误硬件自动纠错;多Bit的ECC错误,只能通过擦除才能清除。你可以先排查是不是因为这个问题导致的,建议在擦除和写完成后都增加一个GetResult判断操作结果。理论上正常的写入或擦除是不会出现ECC错误。
-
houjun_xiao 好的,我们按这个方法试试,谢谢!
-
houjun_xiao 经过我们的测试,发现有部分MCU在执行完Fls_Erase、Fls_Write、Fls_Read后,就是特别容易出现Fls_GetStatus返回一直为BUSY,实际上,通过读取memory看到,擦写操作是已经完成的了。即使增加很多的延时等待,重复读Fls_GetStatus也一直没法解决。现在我们的操作是,增加一个Fls_Cancel操作,异常情况暂时是没再复现,但是不知道是不是已经根本性解决问题。
/** * @brief 擦除pflash1 1MB */ bool bsw_fls_erase_pflash1_1mb( void ) { uint32_t startAddress = 0; uint32_t sectorSize = 0; uint32_t timeout = FLASH_TIMEOUT; startAddress = Fls_Config.SectorList[ FlsConf_FlsConfigSet_PFLASH1_1MB ].SectorStartAddress; sectorSize = Fls_Config.SectorList[ FlsConf_FlsConfigSet_PFLASH1_1MB ].SectorSize; // 超时等待 Flash 空闲 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == 0 ) { return false; } } Fls_Erase( startAddress, sectorSize ); // 开始擦除 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { return true; // 擦除成功 } if ( timeout == FLASH_TIMEOUT / 2 ) { Fls_Cancel( ); Fls_Erase( startAddress, sectorSize ); // 开始擦除 } } return false; // 擦除失败(超时) } bool bsw_fls_write_func( uint32_t Addr, uint8_t* data, uint32_t len ) { uint32_t timeout = 0; // 超时等待 Flash 空闲 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == 0 ) { return false; } } // 写入 Flash timeout = 50; while ( --timeout > 0 ) { if ( !Fls_Write( Addr, data, FLASH_WRITE_UNIT ) ) { break; } if ( timeout == 0 ) { return false; // 写入失败 } }; return true; } /** * @brief 写pflash1 1MB * @param AddrOffset 地址偏移 * @param data 数据指针 * @param len 数据长度 */ bool bsw_fls_write_pflash1_1mb( uint32_t AddrOffset, const uint8_t* data, uint32_t len ) { uint32_t startAddress = 0; uint32_t alignedLen = 0; uint32_t i = 0; uint32_t chunkSize = 0; uint32_t timeout = 0; uint8_t alignedData[ FLASH_WRITE_UNIT ] = { 0xFF }; // 预填充 0xFF uint8_t readBuffer[ FLASH_WRITE_UNIT ] = { 0 }; // 读取缓存初始化 if ( data == NULL || len == 0 ) { return false; } startAddress = Fls_Config.SectorList[ FlsConf_FlsConfigSet_PFLASH1_1MB ].SectorStartAddress + AddrOffset; alignedLen = ( ( len + ( FLASH_WRITE_UNIT - 1 ) ) / FLASH_WRITE_UNIT ) * FLASH_WRITE_UNIT; // 向上取整到 32 字节对齐 // 按 32 字节批量写入 for ( i = 0; i < alignedLen; i += FLASH_WRITE_UNIT ) { chunkSize = ( i + FLASH_WRITE_UNIT <= len ) ? FLASH_WRITE_UNIT : ( len - i ); memset( alignedData, 0xFF, FLASH_WRITE_UNIT ); // 先填充 0xFF memcpy( alignedData, data + i, chunkSize ); // 复制实际数据 // 写入 Flash if ( !bsw_fls_write_func( startAddress + i, alignedData, FLASH_WRITE_UNIT ) ) { return false; } // 超时等待 Flash 空闲 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == FLASH_TIMEOUT / 2 ) { Fls_Cancel( ); bsw_fls_write_func( startAddress + i, alignedData, FLASH_WRITE_UNIT ); } } Fls_Cancel( ); memset( readBuffer, 0, FLASH_WRITE_UNIT ); // 确保读取缓冲区清空 timeout = 50; while ( --timeout > 0 ) { if ( !Fls_Read( startAddress + i, readBuffer, FLASH_WRITE_UNIT ) ) { break; } if ( timeout == 0 ) { return false; // 写入失败 } }; // 等待读取完成 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == 0 ) { return false; } } bsw_wdg_feed_func( ); // 进行数据比对 if ( memcmp( readBuffer, alignedData, FLASH_WRITE_UNIT ) != 0 ) { return false; // 校验失败 } } return true; } /** * @brief 读pflash1 1MB * @param AddrOffset 地址偏移 * @param data 数据指针 * @param len 数据长度 */ bool bsw_fls_read_pflash1_1mb( uint32_t AddrOffset, uint8_t* data, uint32_t len ) { uint32_t startAddress = 0; uint32_t timeout = 0; if ( data == NULL || len == 0 ) { return false; } startAddress = Fls_Config.SectorList[ FlsConf_FlsConfigSet_PFLASH1_1MB ].SectorStartAddress + AddrOffset; // 超时等待 Flash 空闲 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == 0 ) { return false; } } timeout = 50; while ( --timeout > 0 ) { if ( !Fls_Read( startAddress, data, len ) ) { break; } if ( timeout == 0 ) { return false; // 写入失败 } }; timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { return true; // 读取成功 } } return false; // 超时或读取失败 }
-
在调用Fls_Erase,Write,Read之后,原则上是不需要调用Fls_Cancel(除非是应用本身程序有需求,如需要终止当前操作以执行优先级更高的新操作)。方便展示一下你们的YCT配置文件吗?
-
houjun_xiao 需要展示YCT哪部分
-
Fls模块配置的那一部分
-
-
你们出现问题的频次大概是多久一次呢,我用你们测试的代码和配置测了一下,没有复现现象。我测试的时候删除了一部分无效调用Fls_MainFunction的代码,你可以对比一下。测试的MCU PLL为200MHz. 另外需要注意,测试写入和读取的时候,长度都是1M, 你需要确认一下这两个测试里面用到的指针的有效范围。
/* USER CODE BEGIN Header */ /* you can remove the copyright */ /* * Copyright 2020-2023 Yuntu Microelectronics co.,ltd * All rights reserved. * * YUNTU Confidential. This software is owned or controlled by YUNTU and may only be * used strictly in accordance with the applicable license terms. By expressly * accepting such terms or by downloading, installing, activating and/or otherwise * using the software, you are agreeing that you have read, and that you agree to * comply with and are bound by, such license terms. If you do not agree to be * bound by the applicable license terms, then you may not retain, install, * activate or otherwise use the software. The production use license in * Section 2.3 is expressly granted for this software. * * @file main.c * @brief * */ /* USER CODE END Header */ #include "Mcal.h" /* Includes ------------------------------------------------------------------*/ /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ uint8 Fls_WriteData[2048] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; uint8 Fls_ReadData[8] = {0}; volatile uint8 Fls_EraseFailedCnt = 0; volatile uint8 Fls_WriteFailedCnt = 0; volatile uint32 Fls_EraseSusCnt = 0; volatile uint32 Fls_WriteSusCnt = 0; /* USER CODE END PV */ /* Private function declare --------------------------------------------------*/ /* USER CODE BEGIN PFDC */ #define FLASH_TIMEOUT 10000000 #define FLASH_WRITE_UNIT 32 /* USER CODE END PFDC */ static void Board_Init(void); /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /** * @brief Custom implementation of memset * @param dest Pointer to the destination memory * @param value Value to set (converted to unsigned char) * @param len Number of bytes to set * @return Pointer to the destination memory */ void memset(uint8 * dest, uint8 value, uint32 len) { uint8 * ptr = dest; while (len--) { *ptr++ = value; } } /** * @brief Custom implementation of memcpy * @param dest Pointer to the destination memory * @param src Pointer to the source memory * @param len Number of bytes to copy * @return Pointer to the destination memory */ void memcpy(uint8 * dest, const uint8 * src, uint32 len) { uint8 * d = dest; const uint8 * s = src; while (len--) { *d++ = *s++; } } /** * @brief Custom implementation of memcmp * @param ptr1 Pointer to the first memory block * @param ptr2 Pointer to the second memory block * @param len Number of bytes to compare * @return An integer less than, equal to, or greater than zero if the first n bytes of ptr1 * are found to be less than, to match, or be greater than the first n bytes of ptr2. */ boolean memcmp(const uint8 * ptr1, const uint8 * ptr2, uint32 len) { const uint8 * p1 = ptr1; const uint8 * p2 = ptr2; while (len--) { if (*p1 != *p2) { return FALSE; } p1++; p2++; } return TRUE; } /** * @brief 擦除pflash1 1MB */ boolean bsw_fls_erase_pflash1_1mb( void ) { uint32 startAddress = 0; uint32 sectorSize = 0; uint32 timeout = FLASH_TIMEOUT; startAddress = Fls_Config.SectorList[ FlsConf_FlsConfigSet_PFLASH_0 ].SectorStartAddress; sectorSize = Fls_Config.SectorList[ FlsConf_FlsConfigSet_PFLASH_0 ].SectorSize; Fls_Erase( startAddress, sectorSize ); // 开始擦除 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { return TRUE; // 擦除成功 } } return FALSE; // 擦除失败(超时) } boolean bsw_fls_write_func( uint32 Addr, uint8* data, uint32 len ) { uint32 timeout = 0; // 写入 Flash timeout = 50; while ( --timeout > 0 ) { if ( Fls_Write( Addr, data, FLASH_WRITE_UNIT ) != E_OK) { break; } if ( timeout == 0 ) { return FALSE; // 写入失败 } }; // 超时等待 Flash 空闲 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == 0 ) { return FALSE; } } return TRUE; } /** * @brief 写pflash1 1MB * @param AddrOffset 地址偏移 * @param data 数据指针 * @param len 数据长度 */ boolean bsw_fls_write_pflash1_1mb( uint32 AddrOffset, const uint8* data, uint32 len ) { uint32 startAddress = 0; uint32 alignedLen = 0; uint32 i = 0; uint32 chunkSize = 0; uint32 timeout = 0; uint8 alignedData[ FLASH_WRITE_UNIT ] = { 0xFF }; // 预填充 0xFF uint8 readBuffer[ FLASH_WRITE_UNIT ] = { 0 }; // 读取缓存初始化 if ( data == NULL_PTR || len == 0 ) { return FALSE; } startAddress = Fls_Config.SectorList[ FlsConf_FlsConfigSet_PFLASH_0 ].SectorStartAddress + AddrOffset; alignedLen = ( ( len + ( FLASH_WRITE_UNIT - 1 ) ) / FLASH_WRITE_UNIT ) * FLASH_WRITE_UNIT; // 向上取整到 32 字节对齐 // 按 32 字节批量写入 for ( i = 0; i < alignedLen; i += FLASH_WRITE_UNIT ) { chunkSize = ( i + FLASH_WRITE_UNIT <= len ) ? FLASH_WRITE_UNIT : ( len - i ); memset( alignedData, 0xFF, FLASH_WRITE_UNIT ); // 先填充 0xFF memcpy( alignedData, data + i, chunkSize ); // 复制实际数据 // 写入 Flash if ( !bsw_fls_write_func( startAddress + i, alignedData, FLASH_WRITE_UNIT ) ) { return FALSE; } memset( readBuffer, 0, FLASH_WRITE_UNIT ); // 确保读取缓冲区清空 timeout = 50; if ( Fls_Read( startAddress + i, readBuffer, FLASH_WRITE_UNIT ) != E_OK) { return FALSE; } // 等待读取完成 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == 0 ) { return FALSE; } } // 进行数据比对 if ( memcmp( readBuffer, alignedData, FLASH_WRITE_UNIT ) != TRUE ) { return FALSE; // 校验失败 } } return TRUE; } /** * @brief 读pflash1 1MB * @param AddrOffset 地址偏移 * @param data 数据指针 * @param len 数据长度 */ boolean bsw_fls_read_pflash1_1mb( uint32 AddrOffset, uint8* data, uint32 len ) { uint32 startAddress = 0; uint32 timeout = 0; if ( data == NULL_PTR || len == 0 ) { return FALSE; } startAddress = Fls_Config.SectorList[ FlsConf_FlsConfigSet_PFLASH_0 ].SectorStartAddress + AddrOffset; // 超时等待 Flash 空闲 timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { break; } if ( timeout == 0 ) { return FALSE; } } timeout = 50; while ( --timeout > 0 ) { if ( !Fls_Read( startAddress, data, len ) ) { break; } if ( timeout == 0 ) { return FALSE; // 写入失败 } }; timeout = FLASH_TIMEOUT; while ( --timeout > 0 ) { Fls_MainFunction( ); if ( Fls_GetStatus( ) == MEMIF_IDLE ) { return TRUE; // 读取成功 } } return FALSE; // 超时或读取失败 } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ Mcu_Init(&Mcu_Config); Mcu_InitClock(0); #if (MCU_NO_PLL == STD_OFF) while (MCU_PLL_LOCKED != Mcu_GetPllStatus()) { /* Wait until PLL is locked */ } Mcu_DistributePllClock(); #endif /* USER CODE END 1 */ Board_Init(); /* USER CODE BEGIN 2 */ /*Operate the sector FlsConf_FlsConfigSet_DFLASH_0,the step is as follows: *step I :erase the sector, *step II :write Fls_WriteData to the sector 0 to 7 byte, *step III:read the sector 0 to 7 byte to Fls_ReadData,and check the data, *step IV :compare the data in flash, *step V :if the Fls_WriteData and Fls_ReadData are equal,then the test is pass and erase the sector. *step VI :verify the Flash blank or not */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { if(bsw_fls_erase_pflash1_1mb() == TRUE) { Fls_EraseSusCnt++; } else { Fls_EraseFailedCnt++; } if(Fls_GetStatus()== MEMIF_BUSY) { while(1) { __ASM("NOP"); } } if(bsw_fls_write_pflash1_1mb(0, (uint8 *)0x2000000, 0x100000) == TRUE) { Fls_WriteSusCnt++; } else { Fls_WriteFailedCnt++; } if(Fls_GetStatus()== MEMIF_BUSY) { while(1) { __ASM("NOP"); } } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } static void Board_Init(void) { Fls_Init(&Fls_Config); } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */
-
houjun_xiao 部分板子的复现概率还是挺高的,刚试了一下你这份改动的代码,也会出现错误:
查看了一下memory,写操作返回了成功,但是实际上没有写入数据 -
在AUTOSAR中,Fls的Write,Read,Erase操作API都是异步函数,实际执行这几个操作都是要靠Fls_MainFunction来调度和执行的。 -
houjun_xiao 这个理解的。我们设计代码时,参考了Fls_Demo以及FLS-FEE MCAL应用介绍_20241127.pptx的指导。发现写完之后读不出来或者就没写进去,但是底层写/读接口返回值都是正常,Fls_GetStatus接口返回为Busy,才加上了先Fls_Cancel,再进行重新读写的方案
-
Fee的操作跟Fls是一样的,他的操作函数都是异步函数,需要调用Fee_MainFunction来进行调度和状态管理,同时由于Fee需要调用Fls的操作才能实现数据存储与读取,因此调用Fee_MainFunction的同时,也需要调用Fls_MainFunction
发帖前请查看
帮助没办法联网的电脑使用YCT
帮助改进和优化YT CONFIG TOOL,有机会抽取YTM32B1ME0 EVB哦...