【发布时间】:2021-07-29 08:24:51
【问题描述】:
我正在尝试将几个字节的数据写入 STM32F410CBT3 闪存扇区 4(大小为 64KB),我选择了这个扇区并假设它可以安全使用,因为代码约为 30KB(可能位于扇区中) 1 和 2)。微控制器的时钟速度为 100MHz(通过 PLL)。
这是我的 Flash 写入代码:
/* Programming in 16-bit words, so offset address of 0x04 should be enough */
#define FLASH_SECTOR4_BASEADDRESS ((uint32_t)0x8011000)
#define OFFSET ((uint8_t)0x04)
#define ADDR0 FLASH_SECTOR4_BASEADDRESS
#define ADDR1 ((uint32_t)(ADDR0 + OFFSET))
#define ADDR2 ((uint32_t)(ADDR1 + OFFSET))
#define ADDR3 ((uint32_t)(ADDR2 + OFFSET))
/* and so on... */
void FLASH_Init(void)
{
/* Only use FLASH Sector 4 for storing configuration/calibration data. s_EraseInit is stored as a static variable. This function called first because s_EraseInit needs to have values before any FLASH functions/routines are called. */
s_EraseInit.TypeErase = TYPEERASE_SECTORS;
s_EraseInit.Sector = FLASH_SECTOR_4;
s_EraseInit.NbSectors = 1;
s_EraseInit.VoltageRange = VOLTAGE_RANGE_4; /* Input voltage to mcu is around 3.3V */
}
void FLASH_Write(void)
{
/* Stop LPTIM1 interrupts prior to modifying FLASH region */
HAL_LPTIM_Counter_Stop_IT(&hlptim1);
/* Assign temp_x values of struct members stored globally. temp_x will be the variable used for the FlashProgram function */
uint16_t temp0 = GlobalStruct[0].Member1;
uint16_t temp1 = GlobalStruct[0].Member2;
uint16_t temp2 = GlobalStruct[0].Member3;
uint16_t temp3 = GlobalStruct[1].Member1;
uint16_t temp4 = GlobalStruct[1].Member2;
uint16_t temp5 = GlobalStruct[1].Member3;
uint16_t temp6 = GlobalStruct[2].Member1;
uint16_t temp7 = GlobalStruct[2].Member2;
uint16_t temp8 = GlobalStruct[2].Member3;
uint16_t temp9 = GlobalStruct[3].Member1;
uint16_t temp10 = GlobalStruct[3].Member2;
uint16_t temp11 = GlobalStruct[3].Member3;
/* Unlock FLASH peripheral and clear FLASH status register (error) flags */
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP|FLASH_FLAG_OPERR|FLASH_FLAG_WRPERR|FLASH_FLAG_PGAERR|FLASH_FLAG_PGSERR);
/* Mass erase FLASH sector 4 */
FlashStatus[12] = HAL_FLASHEx_Erase(&s_EraseInit, &s_SectorError);
/* Write into FLASH sector 4 and lock the FLASH peripheral after using it */
FlashStatus[0] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR0, temp0);
FlashStatus[1] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR1, temp1);
FlashStatus[2] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR2, temp2);
FlashStatus[3] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR3, temp3);
FlashStatus[4] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR4, temp4);
FlashStatus[5] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR5, temp5);
FlashStatus[6] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR6, temp6);
FlashStatus[7] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR7, temp7);
FlashStatus[8] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR8, temp8);
FlashStatus[9] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR9, temp9);
FlashStatus[10] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR10, temp10);
FlashStatus[11] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR11, temp11);
HAL_FLASH_Lock();
/* Resume LPTIM1 interrupts after modifying FLASH region */
HAL_LPTIM_Counter_Start_IT(&hlptim1, LPTIM1_AUTORELOAD_NUMBER);
}
/*********************** Relevant code from the HAL Library *******************************/
/**
* @brief Program byte, halfword, word or double word at a specified address
* @param TypeProgram Indicate the way to program at a specified address.
* This parameter can be a value of @ref FLASH_Type_Program
* @param Address specifies the address to be programmed.
* @param Data specifies the data to be programmed
*
* @retval HAL_StatusTypeDef HAL Status
*/
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
HAL_StatusTypeDef status = HAL_ERROR;
/* Process Locked */
__HAL_LOCK(&pFlash);
/* Check the parameters */
assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
if(status == HAL_OK)
{
if(TypeProgram == FLASH_TYPEPROGRAM_BYTE)
{
/*Program byte (8-bit) at a specified address.*/
FLASH_Program_Byte(Address, (uint8_t) Data);
}
else if(TypeProgram == FLASH_TYPEPROGRAM_HALFWORD)
{
/*Program halfword (16-bit) at a specified address.*/
FLASH_Program_HalfWord(Address, (uint16_t) Data);
}
else if(TypeProgram == FLASH_TYPEPROGRAM_WORD)
{
/*Program word (32-bit) at a specified address.*/
FLASH_Program_Word(Address, (uint32_t) Data);
}
else
{
/*Program double word (64-bit) at a specified address.*/
FLASH_Program_DoubleWord(Address, Data);
}
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
/* If the program operation is completed, disable the PG Bit */
FLASH->CR &= (~FLASH_CR_PG);
}
/* Process Unlocked */
__HAL_UNLOCK(&pFlash);
return status;
}
关于上面的代码,全局结构的成员要么是 8 位的,要么是 16 位的。我在进入闪存写入序列之前停止了 LPTIM1 中断,因为我认为如果 LPTIM1 中断恰好在微控制器覆盖其闪存时发生,它可能会导致问题。
问题
即使一个非常相似的代码(不同之处在于写入的数据)在不同的 STM32F410CBT3 微控制器上工作,我也一直在此代码上遇到 HardFault 错误。我还匹配了 MDK-ARM IROM1: Start 0x8000000, Size 0x1C000 上的链接器设置,之前它位于 IROM1: Start 0x8000000, Size 0x20000。我观察到的常见模式是,基于 MDK-ARM 的内存查看器,闪存被擦除,其中?? 出现在所有内存块上。之后,它再次转到0xFF,表明没有写入任何块/节。此时,代码已经在其 HardFault Handler 中。如果代码正常工作,扇区将从?? 变为写入闪存的任何值。
关于 HardFault 错误,它有时显示为 内存管理错误,其中引发了 IACCVIOL(指令访问冲突标志),而 SCB->MMFAR 不包含任何地址。在大多数情况下,它显示为 Bus Fault,其中 SCB->BFAR = 0xFFFFFFFF 的 BFARVALID 标志高,PRECISERR 标志也被提高。我查看了 STM32F410 内存映射,地址 0xFFFFFFFF 指向 512 MB 块 7 Cortex-M4 的内部外设。在极少数情况下,它显示为 Usage Fault,其中UNDEFINSTR 位为高。有时,Flash 写入序列确实有效,但前提是使用断点。
我尝试了什么
- 最初,我立即将结构成员放入
HAL_FLASH_ProgramAPI。基于 UsageFault 错误,我认为结构成员访问速度太慢,所以我声明了那些无符号 16 位tempX变量来保存结构成员的值。错误仍然存在,也许这不是原因,因为编译器可能会优化它。我也尝试过使用延迟,但错误仍然存在。 - 我将 16 位
tempX变量声明更改为uint32_t,因为我在某处读到输入需要是 32 位或 64 位。我将其更改为uint32_t声明,但错误仍然存在。 - 我也尝试使用
FLASH_TYPEPROGRAM_WORD代替FLASH_TYPEPROGRAM_HALFWORD,但错误仍然存在。在不同的微控制器上,我注意到这不会产生太大影响,因为如果我要在 Flash 部分本身写入一个 16 位值(例如0xAAAA),它会显示为0x0000AAAA,如果使用了FLASH_TYPEPROGRAM_WORD,如果使用了FLASH_TYPEPROGRAM_HALFWORD,则使用0xFFFFAAAA,因为左侧16 位被清除为0xFFFF,但没有重写为0x0000,因为只有最少的16 位被覆盖。 - 最初,我认为写
0xFFFF会导致问题,但在我确保所有结构成员都是非零或非FFFF 后,错误仍然存在。在不同的微控制器上,我仍然可以将0xFFFF写入闪存而不会导致任何错误。 - 我也尝试使用其他位置(尽管仍位于第 4 区),但错误仍然存在。
- 我验证了每当发生 HardFault 错误时,
FlashStatus[x], x = 0...12仅包含HAL_OK。FLASH->SR寄存器通常也不显示任何内容(无错误状态)。 - 变量
s_SectorError通常有0xFFFFFFFF,表示成功擦除所有扇区(Sector 4)
问题 我在这里想念什么?任何帮助将不胜感激。我也不知道如何更深入地调试这个问题,任何调试这个问题的提示也将不胜感激。 This is the Keil uVision's page on Fault Reporting Guide,我将其用作识别 HardFault 错误的参考。谢谢!
【问题讨论】:
-
您是否通过单步调试会话确定了导致故障的代码行?
-
是的 - 如果我确实在调用
HAL_FLASH_Program()的每一行设置断点,它似乎工作(虽然有时它确实工作,但将错误的值写入某些地址) .如果我只在HAL_FLASH_Lock()和HAL_FLASH_Unlock()设置断点,它有时会工作,有时不会,然后会引发 HardFault 错误之一。在我最近的尝试中,我只收到了 Bus Fault 错误,没有遇到 Usage Fault 错误或 Memory Manage Fault 错误。 -
@Tagli 我的意思是说不,我还没有找到导致它的确切行..
-
你可以使用 GPIO 代替断点来找到你得到硬故障的确切位置吗?
-
我不知道这部分,但我可以放弃一些编写闪存驱动程序的一般提示和技巧。禁用所有中断。不要从您正在编程的银行执行闪存驱动程序。确保闪存预分频时钟正确。禁用看门狗或将其设置得足够长,以免在擦除周期内发出声音。弄错任何一个,你都会得到非常微妙的错误。
标签: memory arm embedded stm32 microcontroller