Memory STM32闪存写入导致多个硬故障错误
我试图将几个字节的数据写入STM32F410CBT3闪存扇区4(大小为64KB),我选择了这个扇区,并假设它可以安全使用,因为代码大约为30KB(可能位于扇区1和扇区2中)。微控制器的时钟速度为100MHz(通过PLL) 以下是我的Flash写入代码:Memory STM32闪存写入导致多个硬故障错误,memory,arm,embedded,stm32,microcontroller,Memory,Arm,Embedded,Stm32,Microcontroller,我试图将几个字节的数据写入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
/* 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微控制器上工作,但我在这段代码上一直存在硬故障错误。我还匹配了MDK-ARMIROM1:Start 0x8000000,大小0x1C000
上的链接器设置,之前它位于IROM1:Start 0x8000000,大小0x20000
。我观察到的常见模式是,基于MDK-ARM的内存查看器,当所有内存块上出现?
时,闪存会被擦除。之后,它再次转到0xFF
,表示没有写入任何块/节。此时,代码已经在其HardFault处理程序中。如果代码正常工作,扇区将从?
变为写入闪存的任何值
关于硬故障错误,它有时显示为内存管理故障,其中引发了IACCVIOL
(指令访问冲突标志),并且SCB->MMFAR
不包含任何地址。在大多数情况下,它显示为总线故障,其中SCB->BFAR=0xFFFFFFFF
,且BFARVALID
标志处于高位,并且preciser
标志也被提升。我查看了STM32F410内存映射,地址0xFFFFFFFF
指向512Mbyte块7 Cortex-M4的内部外围设备。在极少数情况下,它显示为使用故障,其中取消指示
位较高。有时,闪存写入序列确实有效,但仅在使用断点时有效
我尝试的
- 最初,我立即将struct成员放在
api中。基于UsageFault错误,我认为结构成员访问太慢,所以我声明了那些无符号的16位HAL\u FLASH\u程序
变量来保存结构成员的值。错误仍然存在,并且可能不是原因,因为编译器可能会以任何方式对其进行优化。我还尝试使用延迟,但错误仍然存在tempX
- 我将16位
变量声明更改为tempX
,因为我在某个地方读到输入需要是32位或64位。我将其更改为uint32\u t
declarations,错误仍然存在uint32\t
- 我还尝试使用
而不是FLASH\u-TYPEPROGRAM\u-WORD
,错误仍然存在。在不同的微控制器上,我注意到这不会有太大影响,因为如果我要在Flash部分本身中写入一个16位值(例如,FLASH\u-TYPEPROGRAM\u-HALFWORD
),如果使用0xAAAA
,它会显示为Flash\u TYPEPROGRAM\u WORD
,或0x0000aaa
如果使用了0xffffaaa
,因为左16位被清除为FLASH\u TYPEPROGRAM\u半字
,但没有被重新写入0xFFFF
,因为只有至少16位被覆盖0x0000
- 起初,我认为编写
会引起问题,但在确保所有结构成员都是非零或非fff之后,错误仍然存在。在不同的微控制器上,我仍然可以将0xFFFF
写入闪存,而不会导致任何错误0xFFFF
- 我还尝试使用不同的位置(尽管仍然位于扇区4),但错误仍然存在
- 我验证了每当发生硬故障错误时,
仅包含FlashStatus[x],x=0…12
。HAL\u OK
寄存器通常也不显示任何内容(无错误状态)FLASH->SR
- 变量
通常具有s_Sector error
,表示所有扇区(扇区4)都已成功擦除0xFFFFFFFF
我错过了什么?任何帮助都将不胜感激。我也不知道如何更深入地调试这个问题,如果有任何关于调试这个问题的提示,我也将不胜感激,我将其用作识别硬故障错误的参考。谢谢大家! 我在查看HAL文件时犯了一个错误,使用了
电压范围范围范围范围范围范围4
或闪光电压范围范围范围范围4
而不是闪光电压范围范围范围范围范围3
。HAL文件显示,这两个选项都适用于由2.7V-3.6V供电的微控制器,但两个选项之间的差异是电压范围4的外部Vpp使用FLASH\u VOLTAGE\u RANGE\u 3
实际上解决了我的问题,这意味着我最初的问题是由于错误地擦除闪存而引起的,而不是因为我对闪存编程错误
我最初选择了电压范围\u 4
,因为我在阅读参考手册中的这一页时误解了电压范围:
下面这一节实际上告诉我应该使用哪个电压范围,因为HAL库中引用的电压范围对应于FLASH\u CR->PSIZE
位:
为什么每次尝试都会出现不同的硬故障错误,为什么需要以某种方式(64位擦除/32位擦除/16位擦除/8位擦除)擦除闪存,这对我来说仍然是个谜