简体   繁体   English

STM32 Flash 写入导致多个 HardFault 错误

[英]STM32 Flash Write causes multiple HardFault Errors

I am trying to write several bytes of data onto the STM32F410CBT3 Flash Memory Sector 4 (size of 64KB), and I chose this sector and assume it is safe to use as the code is approximately 30KB (which would probably reside in sectors 1 and 2).我正在尝试将几个字节的数据写入 STM32F410CBT3 Flash Memory 扇区 4(大小为 64KB),我选择了这个扇区并假设它可以安全使用,因为代码约为 30KB(可能位于扇区中) )。 The microcontroller's clock speed is clocked at 100MHz (through PLL).微控制器的时钟速度为 100MHz(通过 PLL)。

Here is my Flash Write code:这是我的 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;
}

About the code above, the global struct's members' are either 8-bit or 16-bit.关于上面的代码,全局结构的成员是 8 位或 16 位的。 I stopped the LPTIM1 interrupts before going into the FLASH Write sequence because I thought that the LPTIM1 interrupts could cause issues if it occurs exactly when the microcontroller is overwritting its Flash memory.我在进入 FLASH 写入序列之前停止了 LPTIM1 中断,因为我认为 LPTIM1 中断如果恰好在微控制器覆盖其 Flash ZCD69B4957F06CD818D7BFEDD291 时发生,可能会导致问题

The Issue问题

I have been having HardFault errors on this code even though a really similar code (with the difference being the data written) works on a different STM32F410CBT3 microcontroller.即使在不同的 STM32F410CBT3 微控制器上运行非常相似的代码(不同之处在于写入的数据),我也一直在此代码上遇到 HardFault 错误。 I also matched the Linker Settings on MDK-ARM IROM1: Start 0x8000000, Size 0x1C000 , previously it was at IROM1: Start 0x8000000, Size 0x20000 .我还匹配了 MDK-ARM IROM1: Start 0x8000000, Size 0x1C000 ,之前是在IROM1: Start 0x8000000, Size 0x20000 The common pattern that I am observing is, based on MDK-ARM's Memory Viewer, the Flash gets erased where ??我观察到的常见模式是,基于 MDK-ARM 的 Memory 查看器,Flash 在哪里被擦除?? appears on all the memory block.出现在所有 memory 块上。 Afterwards, it goes to 0xFF again indicating that none of the blocks/sections were written.之后,它再次变为0xFF ,表示没有写入任何块/部分。 At this point, the code is already in its HardFault Handler.此时,代码已经在其 HardFault Handler 中。 If the code supposedly works, the sectors would turn from ??如果代码应该有效,那么这些部门将从?? to whatever value was written to the Flash memory.写入 Flash memory 的任何值。

Regarding the HardFault Error, it sometimes shows as a Memory Manage Fault , where the IACCVIOL (Instruction access violation flag) was raised, and SCB->MMFAR not containing any address.关于 HardFault 错误,它有时显示为Memory 管理故障,其中引发了IACCVIOL (指令访问冲突标志),并且SCB->MMFAR不包含任何地址。 On most times, it shows up as a Bus Fault , where SCB->BFAR = 0xFFFFFFFF with the BFARVALID flag high, and PRECISERR flag was also raised.在大多数情况下,它显示为总线故障,其中SCB->BFAR = 0xFFFFFFFFBFARVALID标志为高,并且PRECISERR标志也被提高。 I looked through the STM32F410 Memory Map and the address 0xFFFFFFFF points to a 512-Mbyte block 7 Cortex-M4's internal peripherals.我查看了 STM32F410 Memory Map,地址0xFFFFFFFF指向 512 MB 块 7 Cortex-M4 的内部外围设备。 On rare occasions, it shows as a Usage Fault , where the UNDEFINSTR bit is high.在极少数情况下,它显示为使用错误,其中UNDEFINSTR位为高。 Sometimes, the Flash Write sequence does work, but only if breakpoints were used.有时,Flash 写入序列确实有效,但前提是使用了断点。

总线故障错误的一个例子

What I tried我试过的

  • Initially, I immediately placed the struct members inside the HAL_FLASH_Program APIs.最初,我立即将结构成员放在HAL_FLASH_Program API 中。 Based on the UsageFault error, I thought that the struct member access was too slow, so I declared those unsigned 16-bit tempX variables to hold the struct members' values.基于 UsageFault 错误,我认为结构成员访问速度太慢,所以我声明了那些无符号的 16 位tempX变量来保存结构成员的值。 The error still persists, and maybe, it is not the cause as the compiler would probably optimize that anyways.错误仍然存在,也许这不是原因,因为编译器可能会优化它。 I also tried using delays and the error still persists.我也尝试使用延迟,但错误仍然存在。
  • I changed the 16-bit tempX variable declarations to uint32_t because I read somewhere that the input needs to be either 32-bits or 64-bits.我将 16 位tempX变量声明更改为uint32_t ,因为我在某处读到输入需要是 32 位或 64 位。 I changed it to uint32_t declarations and the error still persists.我将其更改为uint32_t声明,但错误仍然存在。
  • I also tried using FLASH_TYPEPROGRAM_WORD instead of FLASH_TYPEPROGRAM_HALFWORD , and the error still persists.我也尝试使用FLASH_TYPEPROGRAM_WORD而不是FLASH_TYPEPROGRAM_HALFWORD ,但错误仍然存在。 On the different microcontroller, I noticed that this should not have much effect, because if I were to write a 16-bit value (for ex. 0xAAAA ), in the Flash section itself, it would either appear as 0x0000AAAA if FLASH_TYPEPROGRAM_WORD was used, or 0xFFFFAAAA if FLASH_TYPEPROGRAM_HALFWORD was used because the left 16-bits were cleared to 0xFFFF but not re-written to 0x0000 since only the least 16-bits were overwritten.在不同的微控制器上,我注意到这应该没有太大影响,因为如果我要在 Flash 部分本身中写入一个 16 位值(例如0xAAAA ),如果使用FLASH_TYPEPROGRAM_WORD ,它将显示为0x0000AAAA ,如果使用了0xFFFFAAAA ,则为FLASH_TYPEPROGRAM_HALFWORD ,因为左侧 16 位被清除为0xFFFF但未重写为0x0000 ,因为仅覆盖了最少的 16 位。
  • Initially, I thought that writing a 0xFFFF would cause an issue, but after I ensured that all the struct members were nonzero or nonFFFF, the error still persists.最初,我认为写一个0xFFFF会导致问题,但在我确保所有结构成员都是非零或非FFFF 之后,错误仍然存在。 On the different microcontroller, I could also still write 0xFFFF to the Flash without causing any errors.在不同的微控制器上,我仍然可以将0xFFFF写入 Flash 而不会导致任何错误。
  • I also tried using a different location (although still located in Sector 4), and the error still persists.我还尝试使用不同的位置(尽管仍位于第 4 区),但错误仍然存在。
  • I verified that whenever a HardFault error occurs, FlashStatus[x], x = 0...12 contains only HAL_OK .我验证了每当发生 HardFault 错误时, FlashStatus[x], x = 0...12仅包含HAL_OK The FLASH->SR register usually shows nothing too (no error status). FLASH->SR寄存器通常也不显示任何内容(无错误状态)。
  • The variable s_SectorError usually has 0xFFFFFFFF , indicating that all the sectors (Sector 4) was successfully erased变量s_SectorError通常有0xFFFFFFFF ,表示所有扇区(Sector 4)都被成功擦除

Question What am I missing here?问题我在这里错过了什么? Any help would be greatly appreciated.任何帮助将不胜感激。 I also don't know how to debug this issue deeper and any tips on debugging this problem would be appreciated too.我也不知道如何更深入地调试这个问题,任何调试这个问题的提示也将不胜感激。 This is the Keil uVision's page on Fault Reporting Guide , which I used as a reference for identifying the HardFault Errors. 这是 Keil uVision 的 Fault Reporting Guide 页面,我将其用作识别 HardFault 错误的参考。 Thank you!谢谢!

I was looking through the HAL files and I made a mistake of using VOLTAGE_RANGE_4 or FLASH_VOLTAGE_RANGE_4 instead of FLASH_VOLTAGE_RANGE_3 .我正在查看 HAL 文件,但我错误地使用VOLTAGE_RANGE_4FLASH_VOLTAGE_RANGE_4而不是FLASH_VOLTAGE_RANGE_3 The HAL files showed that both options are for microcontrollers powered by a 2.7V - 3.6V, but the difference between the two options are the external Vpp for voltage range 4. Using FLASH_VOLTAGE_RANGE_3 actually fixed my issue , meaning that the original problem I had was caused by erasing the Flash memory incorrectly, and not because I programmed the Flash Memory incorrectly. HAL 文件显示这两个选项均适用于由 2.7V - 3.6V 供电的微控制器,但两个选项之间的区别在于电压范围 4 的外部 Vpp。使用FLASH_VOLTAGE_RANGE_3实际上解决了我的问题,这意味着我遇到的原始问题是由错误擦除 Flash memory 引起的,而不是因为我对 Flash Z4789F23283A619EFAZB8 编程不正确。

HAL 代码中的电压范围选项

I initially chose VOLTAGE_RANGE_4 because I misunderstood the voltage range when reading this page found in the Reference Manual:我最初选择VOLTAGE_RANGE_4是因为我在阅读参考手册中的此页面时误解了电压范围:

旧 STM32F410 参考手册 on FLASH

While this section below actually tells me which voltage range I should be using since the voltage range referred in the HAL libraries corresponds to FLASH_CR->PSIZE bits:虽然下面的这一部分实际上告诉我应该使用哪个电压范围,因为 HAL 库中提到的电压范围对应于FLASH_CR->PSIZE位:

新的 STM32F410 闪存参考手册

It is still a mystery to me as to why different HardFault errors can be raised at each tries, and why erasing the Flash Memory needs to be done in a certain way (64-bit erase/32-bit erase/16-bit erase/8-bit erase) depending on the supply voltage.为什么每次尝试都会引发不同的 HardFault 错误,以及为什么擦除 Flash Memory 需要以某种方式完成(64 位擦除/32 位擦除/16 位擦除/ 8位擦除)取决于电源电压。 It is also weird how it can work sometimes, but not work most of the time with the wrong configuration.有时它如何工作也很奇怪,但大多数时候在错误的配置下无法工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM