我正在尝试将几个字节的数据写入STM32F410CBT3闪存扇区4(大小为64 in ),我选择了这个扇区,并假定使用该扇区是安全的,因为代码大约为30 in(可能驻留在扇区1和2中)。微控制器的时钟速度为100 The (通过锁相环)。
以下是我的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中断恰好发生在微控制器重写其闪存时,则会引起问题。
The Issue
我在这段代码上出现了HardFault错误,尽管一个非常类似的代码(不同的是所编写的数据)在不同的STM32F410CBT3微控制器上工作。我还匹配MDK-ARM IROM1: Start 0x8000000, Size 0x1C000上的链接器设置,以前是在IROM1: Start 0x8000000, Size 0x20000。我观察到的常见模式是,基于MDK的内存查看器,在所有内存块上出现??的地方,闪存会被擦除。之后,它将再次转到0xFF,表示没有编写任何块/节。此时,代码已经在其HardFault处理程序中了。如果代码被认为是有效的,那么扇区将从??转到写入Flash的任何值。
关于HardFault错误,有时会显示为内存管理故障,其中会引发IACCVIOL (指令访问冲突标志),而SCB->MMFAR不包含任何地址。在大多数情况下,它显示为一个总线故障,其中SCB->BFAR = 0xFFFFFFFF和BFARVALID标志高,PRECISERR标志也被提升。我查看了STM32F410内存地图,地址0xFFFFFFFF指向一个512兆字节块7 Cortex-M4的内部外围设备。在很少的情况下,它显示为使用故障,其中UNDEFINSTR位很高。有时,Flash写入序列确实工作,但只在使用断点的情况下才能工作。

我试过什么
HAL_FLASH_Program API中。基于UsageFault错误,我认为struct成员访问太慢,所以我声明了那些未签名的16位tempX变量来保存struct成员的值。错误仍然存在,而且可能不是原因,因为编译器可能会对其进行优化。我也尝试使用延迟,但错误仍然存在。tempX变量声明更改为uint32_t,因为我在某个地方读到输入需要32位或64位。我将其更改为uint32_t声明,错误仍然存在。FLASH_TYPEPROGRAM_WORD而不是FLASH_TYPEPROGRAM_HALFWORD,但错误仍然存在。在不同的微控制器上,我注意到这不应该有太大的影响,因为如果我要写一个16位的值(对于ex )。( 0xAAAA),在Flash部分本身,如果使用FLASH_TYPEPROGRAM_WORD,它将显示为0x0000AAAA;如果使用FLASH_TYPEPROGRAM_HALFWORD,则会出现0xFFFFAAAA (如果使用FLASH_TYPEPROGRAM_HALFWORD),因为左16位被清除到0xFFFF,而不是重写到0x0000,因为只有至少16位被覆盖。0xFFFF会导致问题,但在确保所有结构成员都为非零或nonFFFF之后,错误仍然存在。在不同的微控制器上,我仍然可以将0xFFFF写入闪存,而不会导致任何错误。FlashStatus[x], x = 0...12只包含HAL_OK。FLASH->SR寄存器通常也没有显示任何内容(没有错误状态)。s_SectorError通常有0xFFFFFFFF,表示所有扇区(扇区4)都被成功擦除。问题,我在这里错过了什么?任何帮助都将不胜感激。我也不知道如何更深入地调试这个问题,任何关于调试这个问题的建议也会很感激。这是Keil uVision关于故障报告指南的页面,我使用它作为识别HardFault错误的参考。谢谢!
发布于 2021-05-08 06:34:25
我正在查看HAL文件,我犯了一个错误:使用VOLTAGE_RANGE_4或FLASH_VOLTAGE_RANGE_4而不是FLASH_VOLTAGE_RANGE_3。HAL文件显示,这两种选择都适用于由2.7V - 3.6V供电的微控制器,但这两个选项之间的区别是电压范围为4的外部Vpp。使用FLASH_VOLTAGE_RANGE_3 的实际上修复了我的问题,这意味着我最初的问题是由于错误地擦除了闪存,而不是因为我错误地编写了闪存。

我最初选择VOLTAGE_RANGE_4是因为在阅读“参考手册”中的这一页时,我误解了电压范围:

下面这一节实际上告诉我应该使用哪个电压范围,因为HAL库中引用的电压范围对应于FLASH_CR->PSIZE位:

对于我来说,为什么每次尝试都会产生不同的HardFault错误,以及为什么需要以某种方式擦除闪存(64位擦除/32位擦除/16位擦除/8位擦除),这对我来说仍然是个谜。有时,它的工作方式也很奇怪,但在大多数情况下,如果配置错误,则无法工作。
https://stackoverflow.com/questions/67427977
复制相似问题