一个STM32中Flash操作时uint64_t指针访问导致hardfault的问题和解决办法
一、 问题描述
在一次用STM32G4的项目中,遇到一个问题,因Flash的操作函数:
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);
需要用uint64_t指针去访问uint8_t的数组中的数据。程序每次运行到这里就会hardfault,如下面的程序所示:
unsigned char App_WriteFlash(unsigned int addr,unsigned char *mpt,unsigned int len)
{
uint32_t Address = 0, PageError = 0;
uint32_t i,cnt;
uint64_t *pt = NULL;
uint64_t temp;
Address = addr;
pt = (uint64_t *)mpt;
i = 0;
cnt = 0;
while (cnt < len)
{
temp = *pt; //这里会hardfault
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, Address, temp) == HAL_OK)
{
Address = Address + 8;
pt++;
cnt += 8;
}
else
{
/* Error occurred while writing data in Flash memory.
User can add here some code to deal with this error */
return 1;
}
}
return 0;
}
二、问题原因:
在单步执行时,运行到这里看汇编代码,在执行到如下一条语句时发生hardfault:
0x08000A9E E9DB4500 LDRD r4,r5,[r11,#0]
这里是读取DWORD的数据到R4和R5中保存,这是的R11中地址是0x20000F1D
,分析主要的原因是这里传入的起始地址没有对齐到word(字),LDRD指令操作就会产生hardfault。
M4内核文档中有这部分相关的描述,下面这里描述了对齐要求和无需对齐要求的指令:
另外,也指出了LDRD指令在未对其的情况下,一定会产生usagefault,且与设置无关:
三、解决办法:
1、不使用uint64_t 指针访问
这种方法可以将8个字节的数据一个个左移加或运算赋值到uint64_t 的局部变量中,然后再进行HAL_FLASH_Program
的操作。
2、将输入数组定义在对齐的内存地址上
可以对输入数组定义后面用__attribute__((at(0x08000200)))
修饰,绝对地址为4字节对齐的地址;例如:
userpara app_para __attribute__((at(0x08000200)));
3、定义输入数组为指定字节对齐方式
这种方式更好一点,输入数组定义后面用__attribute__((__aligned__(4)))
修饰,定义为4字节对齐的数据;例如:
userpara app_para __attribute__((__aligned__(4)));
经过实测,以上三种方式任意一种都能解决该问题。