STM32F4 nucleo 上的 HAL 驱动程序 erase/read/write 闪存
HAL drivers erase/read/write flash on STM32F4 nucleo
uint32_t PAGEError = 0;
FLASH_EraseInitTypeDef EraseInitStruct;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS ;
EraseInitStruct.Sector = FLASH_SECTOR_0;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x08000000, counter)
HAL_FLASH_Lock();
counter2 = *(__IO uint32_t *)0x08000000;
counter3 = *(__IO uint32_t *)0x08000001;
counter4 = *(__IO uint32_t *)0x08000002;
sprintf(buf, "%d", counter2); //gets send to the OLED with I2C
sprintf(buf2, "%d", counter3);
sprintf(buf3, "%d", counter4);
我想将变量counter写入flash,然后读取为counter2
。
第一个闪存扇区从 0x08000000
.
开始
counter2
、3
和 4
是通过 OLED 屏幕显示的。
显示 counter2
有效并向我显示 counter-1
的值,但它只有效一次。如果我再次写入闪存,似乎什么也没有发生。
counter3
和 counter4
根本不起作用。
擦除闪存但未写入任何内容后counter=0x00000008
时OLED上的输出:
counter2: 536873624
counter3: -652214262
counter4: 31006720
写入和重置后:
counter2: 8
counter3: -654311424
counter4: 30998528
这是怎么回事?有人能告诉我为什么所有变量都会改变吗?
我必须配置链接器吗?
现在我会把你当成初学者,如果你不是初学者,我会说对不起。
STM32 设备在 0x08000000
上有闪存,通过擦除此扇区,您在启动时失败了,因为您从 CPU 加载指令的地方擦除了实际部分。
当您尝试擦除扇区时,您没有指定要擦除多少 个扇区。
计数器读取错误。因为你有 uint32_t
变量,你必须在读数之间做 4 个字节,比如:
counter2 = *(__IO uint32_t *)0x08000000;
counter3 = *(__IO uint32_t *)0x08000004;
counter4 = *(__IO uint32_t *)0x08000008;
正确的擦除如下图
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
EraseInitStruct.Sector = FLASH_SECTOR_0; //Specify sector number
EraseInitStruct.NbSectors = 1; //This is also important!
if(HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
//Erase error!
}
所以,找出你的程序有多长,然后在你的程序之后的扇区中进行操作。
您可以在 STM32CubeF4 包中找到 EraseProgram 的示例。
STM32Cube_FW_F4_V1.16.0\Projects\STM324x9I_EVAL\Examples\FLASH\FLASH_EraseProgram\Src\main.c
概念也适用于您的 nucleo,只要确保您设置正确的闪存擦除地址即可。
最好的方法是在 linkescriptr 中创建新的 flash 段并将数据放在那里。这是最安全的。
如果不知道链接器脚本创建一个 table 一个段大小,并使用您的编译器指令将其放在闪存的末尾。
如果您不知道两者,我建议您使用 Cube
中现成的 STM eeprom 仿真示例
感谢@phoenix!
在Stm32CubF3参考擦除flash的工作原理如下:
这里提到内存页面地址的样子:
/* Base address of the Flash sectors */
#define ADDR_FLASH_PAGE_0 ((uint32_t)0x08000000) /* Base @ of Page 0, 2 Kbytes */
#define ADDR_FLASH_PAGE_1 ((uint32_t)0x08000800) /* Base @ of Page 1, 2 Kbytes */
#define ADDR_FLASH_PAGE_2 ((uint32_t)0x08001000) /* Base @ of Page 2, 2 Kbytes */
#define ADDR_FLASH_PAGE_3 ((uint32_t)0x08001800) /* Base @ of Page 3, 2 Kbytes */
#define ADDR_FLASH_PAGE_4 ((uint32_t)0x08002000) /* Base @ of Page 4, 2 Kbytes */
#define ADDR_FLASH_PAGE_5 ((uint32_t)0x08002800) /* Base @ of Page 5, 2 Kbytes */
#define ADDR_FLASH_PAGE_6 ((uint32_t)0x08003000) /* Base @ of Page 6, 2 Kbytes */
因此您的最小代码如下所示:
我想删除我的应用程序 @ 0x08003000 --> FLASH_PAGE_6。擦除以 2kB 的大小完成:
/* EEPROM start address in Flash */
#define EEPROM_START_ADDRESS ((uint32_t)ADDR_FLASH_PAGE_32) /* EEPROM emulation start address */
/* Pages 0 and 1 base and end addresses */
#define PAGE0_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x0000))
#define PAGE0_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))
//#define PAGE0_ID ADDR_FLASH_PAGE_32
/* Clear flash flags */
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP |
FLASH_FLAG_WRPERR |
FLASH_FLAG_PGERR);
uint32_t *flash_ptr = 0x8003000;
uint32_t page_error = 0;
HAL_StatusTypeDef flashstatus;
FLASH_EraseInitTypeDef s_eraseinit;
/* Fill EraseInit structure*/
s_eraseinit.TypeErase = FLASH_TYPEERASE_PAGES;
s_eraseinit.NbPages = 1; // seems to be 1 !!!!
/* I want to delete 54kB in my flash --> 52kB / 2kB (per erase) = 26 iterations
After erasing one page, increment page by 0x800 = 2048 byte = 2kB = pagesize */
for(int pageCount = 0; pageCount < 26; pageCount++){
s_eraseinit.PageAddress = ADDR_FLASH_PAGE_6 + pageCount * 0x800;
flashstatus = HAL_FLASHEx_Erase(&s_eraseinit, &page_error);
}
uint32_t PAGEError = 0;
FLASH_EraseInitTypeDef EraseInitStruct;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS ;
EraseInitStruct.Sector = FLASH_SECTOR_0;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x08000000, counter)
HAL_FLASH_Lock();
counter2 = *(__IO uint32_t *)0x08000000;
counter3 = *(__IO uint32_t *)0x08000001;
counter4 = *(__IO uint32_t *)0x08000002;
sprintf(buf, "%d", counter2); //gets send to the OLED with I2C
sprintf(buf2, "%d", counter3);
sprintf(buf3, "%d", counter4);
我想将变量counter写入flash,然后读取为counter2
。
第一个闪存扇区从 0x08000000
.
counter2
、3
和 4
是通过 OLED 屏幕显示的。
显示 counter2
有效并向我显示 counter-1
的值,但它只有效一次。如果我再次写入闪存,似乎什么也没有发生。
counter3
和 counter4
根本不起作用。
擦除闪存但未写入任何内容后counter=0x00000008
时OLED上的输出:
counter2: 536873624
counter3: -652214262
counter4: 31006720
写入和重置后:
counter2: 8
counter3: -654311424
counter4: 30998528
这是怎么回事?有人能告诉我为什么所有变量都会改变吗? 我必须配置链接器吗?
现在我会把你当成初学者,如果你不是初学者,我会说对不起。
STM32 设备在 0x08000000
上有闪存,通过擦除此扇区,您在启动时失败了,因为您从 CPU 加载指令的地方擦除了实际部分。
当您尝试擦除扇区时,您没有指定要擦除多少 个扇区。
计数器读取错误。因为你有 uint32_t
变量,你必须在读数之间做 4 个字节,比如:
counter2 = *(__IO uint32_t *)0x08000000;
counter3 = *(__IO uint32_t *)0x08000004;
counter4 = *(__IO uint32_t *)0x08000008;
正确的擦除如下图
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
EraseInitStruct.Sector = FLASH_SECTOR_0; //Specify sector number
EraseInitStruct.NbSectors = 1; //This is also important!
if(HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
//Erase error!
}
所以,找出你的程序有多长,然后在你的程序之后的扇区中进行操作。
您可以在 STM32CubeF4 包中找到 EraseProgram 的示例。
STM32Cube_FW_F4_V1.16.0\Projects\STM324x9I_EVAL\Examples\FLASH\FLASH_EraseProgram\Src\main.c
概念也适用于您的 nucleo,只要确保您设置正确的闪存擦除地址即可。
最好的方法是在 linkescriptr 中创建新的 flash 段并将数据放在那里。这是最安全的。
如果不知道链接器脚本创建一个 table 一个段大小,并使用您的编译器指令将其放在闪存的末尾。
如果您不知道两者,我建议您使用 Cube
中现成的 STM eeprom 仿真示例
感谢@phoenix!
在Stm32CubF3参考擦除flash的工作原理如下:
这里提到内存页面地址的样子:
/* Base address of the Flash sectors */
#define ADDR_FLASH_PAGE_0 ((uint32_t)0x08000000) /* Base @ of Page 0, 2 Kbytes */
#define ADDR_FLASH_PAGE_1 ((uint32_t)0x08000800) /* Base @ of Page 1, 2 Kbytes */
#define ADDR_FLASH_PAGE_2 ((uint32_t)0x08001000) /* Base @ of Page 2, 2 Kbytes */
#define ADDR_FLASH_PAGE_3 ((uint32_t)0x08001800) /* Base @ of Page 3, 2 Kbytes */
#define ADDR_FLASH_PAGE_4 ((uint32_t)0x08002000) /* Base @ of Page 4, 2 Kbytes */
#define ADDR_FLASH_PAGE_5 ((uint32_t)0x08002800) /* Base @ of Page 5, 2 Kbytes */
#define ADDR_FLASH_PAGE_6 ((uint32_t)0x08003000) /* Base @ of Page 6, 2 Kbytes */
因此您的最小代码如下所示:
我想删除我的应用程序 @ 0x08003000 --> FLASH_PAGE_6。擦除以 2kB 的大小完成:
/* EEPROM start address in Flash */
#define EEPROM_START_ADDRESS ((uint32_t)ADDR_FLASH_PAGE_32) /* EEPROM emulation start address */
/* Pages 0 and 1 base and end addresses */
#define PAGE0_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x0000))
#define PAGE0_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))
//#define PAGE0_ID ADDR_FLASH_PAGE_32
/* Clear flash flags */
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP |
FLASH_FLAG_WRPERR |
FLASH_FLAG_PGERR);
uint32_t *flash_ptr = 0x8003000;
uint32_t page_error = 0;
HAL_StatusTypeDef flashstatus;
FLASH_EraseInitTypeDef s_eraseinit;
/* Fill EraseInit structure*/
s_eraseinit.TypeErase = FLASH_TYPEERASE_PAGES;
s_eraseinit.NbPages = 1; // seems to be 1 !!!!
/* I want to delete 54kB in my flash --> 52kB / 2kB (per erase) = 26 iterations
After erasing one page, increment page by 0x800 = 2048 byte = 2kB = pagesize */
for(int pageCount = 0; pageCount < 26; pageCount++){
s_eraseinit.PageAddress = ADDR_FLASH_PAGE_6 + pageCount * 0x800;
flashstatus = HAL_FLASHEx_Erase(&s_eraseinit, &page_error);
}