一、前言
在只有芯片没有外部存储时需要存储一些配置信息,使用flash进行存储是很好的选择了,在GD32E23x中以flash扇区为单位进行擦写,一个扇区大小为1K,需要时优先拿出最后几个扇区来作为存储区。
二、GD32E23x flash扇区地址划分
一般来,GD32E23x的flash第一个扇区的首地址为0x0800_0000,以我使用的这一款64k的flash为例,扇区划分为:
| 页(Page)编号 | 起始地址(Flash Address) | 结束地址(Flash Address) |
|---|---|---|
| Page 0 | 0x0800_0000 | 0x0800_03FF |
| Page 1 | 0x0800_0400 | 0x0800_07FF |
| Page 2 | 0x0800_0800 | 0x0800_0BFF |
| Page 3 | 0x0800_0C00 | 0x0800_0FFF |
| Page 4 | 0x0800_1000 | 0x0800_13FF |
| Page 5 | 0x0800_1400 | 0x0800_17FF |
| Page 6 | 0x0800_1800 | 0x0800_1BFF |
| Page 7 | 0x0800_1C00 | 0x0800_1FFF |
| Page 8 | 0x0800_2000 | 0x0800_23FF |
| Page 9 | 0x0800_2400 | 0x0800_27FF |
| Page 10 | 0x0800_2800 | 0x0800_2BFF |
| Page 11 | 0x0800_2C00 | 0x0800_2FFF |
| Page 12 | 0x0800_3000 | 0x0800_33FF |
| Page 13 | 0x0800_3400 | 0x0800_37FF |
| Page 14 | 0x0800_3800 | 0x0800_3BFF |
| Page 15 | 0x0800_3C00 | 0x0800_3FFF |
| Page 16 | 0x0800_4000 | 0x0800_43FF |
| Page 17 | 0x0800_4400 | 0x0800_47FF |
| Page 18 | 0x0800_4800 | 0x0800_4BFF |
| Page 19 | 0x0800_4C00 | 0x0800_4FFF |
| Page 20 | 0x0800_5000 | 0x0800_53FF |
| Page 21 | 0x0800_5400 | 0x0800_57FF |
| Page 22 | 0x0800_5800 | 0x0800_5BFF |
| Page 23 | 0x0800_5C00 | 0x0800_5FFF |
| Page 24 | 0x0800_6000 | 0x0800_63FF |
| Page 25 | 0x0800_6400 | 0x0800_67FF |
| Page 26 | 0x0800_6800 | 0x0800_6BFF |
| Page 27 | 0x0800_6C00 | 0x0800_6FFF |
| Page 28 | 0x0800_7000 | 0x0800_73FF |
| Page 29 | 0x0800_7400 | 0x0800_77FF |
| Page 30 | 0x0800_7800 | 0x0800_7BFF |
| Page 31 | 0x0800_7C00 | 0x0800_7FFF |
| Page 32 | 0x0800_8000 | 0x0800_83FF |
| Page 33 | 0x0800_8400 | 0x0800_87FF |
| Page 34 | 0x0800_8800 | 0x0800_8BFF |
| Page 35 | 0x0800_8C00 | 0x0800_8FFF |
| Page 36 | 0x0800_9000 | 0x0800_93FF |
| Page 37 | 0x0800_9400 | 0x0800_97FF |
| Page 38 | 0x0800_9800 | 0x0800_9BFF |
| Page 39 | 0x0800_9C00 | 0x0800_9FFF |
| Page 40 | 0x0800_A000 | 0x0800_A3FF |
| Page 41 | 0x0800_A400 | 0x0800_A7FF |
| Page 42 | 0x0800_A800 | 0x0800_ABFF |
| Page 43 | 0x0800_AC00 | 0x0800_AFFF |
| Page 44 | 0x0800_B000 | 0x0800_B3FF |
| Page 45 | 0x0800_B400 | 0x0800_B7FF |
| Page 46 | 0x0800_B800 | 0x0800_BBFF |
| Page 47 | 0x0800_BC00 | 0x0800_BFFF |
| Page 48 | 0x0800_C000 | 0x0800_C3FF |
| Page 49 | 0x0800_C400 | 0x0800_C7FF |
| Page 50 | 0x0800_C800 | 0x0800_CBFF |
| Page 51 | 0x0800_CC00 | 0x0800_CFFF |
| Page 52 | 0x0800_D000 | 0x0800_D3FF |
| Page 53 | 0x0800_D400 | 0x0800_D7FF |
| Page 54 | 0x0800_D800 | 0x0800_DBFF |
| Page 55 | 0x0800_DC00 | 0x0800_DFFF |
| Page 56 | 0x0800_E000 | 0x0800_E3FF |
| Page 57 | 0x0800_E400 | 0x0800_E7FF |
| Page 58 | 0x0800_E800 | 0x0800_EBFF |
| Page 59 | 0x0800_EC00 | 0x0800_EFFF |
| Page 60 | 0x0800_F000 | 0x0800_F3FF |
| Page 61 | 0x0800_F400 | 0x0800_F7FF |
| Page 62 | 0x0800_F800 | 0x0800_FBFF |
| Page 63 | 0x0800_FC00 | 0x0800_FFFF |
三、GD32E23x 固件库下载
我这里使用的是GD32E23x标准固件库,下载地址为: (兆易创新GigaDevice-资料下载兆易创新GD32 MCU)
如图,如果只需要固件库下载GD32E23x Firmware Library即可,如果需要固件库+示例则下载GD32E23x Demo Suites:
四、GD32E23x falsh擦写操作函数封装
直接给示例吧,引用头文件gd32e23x_fmc.h的库函数,稍加封装方便调用:
//写入数据到目标扇区 参数依次:扇区首地址、数据首地址、数据大小
void flash_write_multi(uint32_t start_addr, uint32_t *data, uint32_t word_count) {
fmc_unlock();//解锁
fmc_page_erase(start_addr);//擦掉
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR );
for (uint32_t i = 0; i < word_count; i++) {//写入新数据
fmc_word_program(start_addr + (i * 4), data[i]);
}
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR );
fmc_lock();//加锁
}
//读取扇区数据 参数依次:扇区首地址、读取缓存区首地址、读取大小
void flash_read_multi(uint32_t start_addr, uint32_t *buffer, uint32_t word_count) {
for (uint32_t i = 0; i < word_count; i++) {
buffer[i] = *(volatile uint32_t*)(start_addr + (i * 4));
}
}
五,引用示例
这里结合结构体进行一个配置信息的保存,可以很方便的保存数据,示例代码:
#define FLASH_PAGE_SIZE 1024 //一个扇区的大小
#define FLASH_SAVE_ADDR (0x0800FC00) //使用最后一个扇区作为静态区
#define CONFIG_SIZE (sizeof(Config_data)) //配置信息实际大小
#define CONFIG_WORDS ((CONFIG_SIZE + 3) / 4) //注意最小单位是32位
#pragma pack(push, 1)
//配置信息结构体 总大小小于1K
typedef struct Config_data
{
uint8_t rtuid[5];
uint8_t password[2];
uint8_t othen[20];
} Config_data;
#pragma pack(pop)
//写入配置信息:
uint32_t buffer[CONFIG_WORDS];
memset(buffer, 0xFF, sizeof(buffer));
memcpy((uint8_t*)buffer, config, CONFIG_SIZE);
flash_write_multi(FLASH_SAVE_ADDR, buffer, CONFIG_WORDS);
//读取配置信息:
uint32_t buffer[CONFIG_WORDS];
flash_read_multi(FLASH_SAVE_ADDR, buffer, CONFIG_WORDS);
memcpy(config, (uint8_t*)buffer, CONFIG_SIZE);