12.1.FMC 基础知识
闪存控制器(FMC),提供了片上闪存需要的所有功能。FMC 也提供了页擦除,整片擦除,以及32 位整字或 16 位半字编程闪存等操作。 GD32 MCU 支持不同类型编程的具体说明如下表 GD32 MCU 不同系列编程区别所示。
12.2.FMC 功能
支持 32 位整字或 16 位半字编程,页擦除和整片擦除操作;
支持 CPU 执行指令零等待区域(code area)和非零等待区域(data area); 大小为 16 字节的可选字节块可根据用户需求配置;
具有安全保护状态,可阻止对代码或数据的非法读访问;
相关术语说明
GD32F10x 和 F30x 分别有 MD(中容量) 、HD(大容量)、XD(超大容量) 、CL(互联型)系列,不同的系列外设资源有差异,使用固件库(Firmware)也要作相应的选择和定义。 中容量产品指Flash 容量为小于 256K 字节的系列;
大容量产品指 Flash 容量为 256K 至 512K 字节之间的 系列; 超大容量产品指 Flash 容量为 768K 至 3072K 字节之间的系列。
GD32F10X/F30X 系列容量汇总
不同容量工程选择说明,固件库是通过宏定义来区分的。
不同的系列 MCU flash 架构不一样,其中 GD32F 系列的 MCU flash 分 code area 和 data area, code area CPU 执行指令是零等待, GD32E 系列 flash 不分 code area 和 data are,但都要插入等待周期。 GD32 系列 Code area&Data area 汇总
读操作 闪存可以像普通存储空间一样直接寻址访问。对闪存取指令和取数据分别使用 CPU 的 IBUS 或 DBUS总线。
代码操作如:
uint32_t readflash[3] readflash [0] = *(__IO uint32_t*)(0x8004000); readflash [1] = *(__IO uint32_t*)((0x8004004); readflash [2] = *(__IO uint32_t*)((0x8004008);
页擦除
FMC 的页擦除功能使得主存储闪存的页内容初始化为高电平。 每一页都可以被独立擦除,而不影响其他页内容。
FMC 擦除页步骤如下:
◼ 确保 FMC_CTLx 寄存器不处于锁定状态;
◼ 检查 FMC_STATx 寄存器的 BUSY 位来判定闪存是否正处于擦写访问状态,若 BUSY 位为 1,则需等待该操作结束, BUSY 位变为 0;
◼ 置位 FMC_CTLx 寄存器的 PER 位;
◼ 将待擦除页的绝对地址( 0x08XX XXXX)写到 FMC_ADDRx 寄存器;
◼ 通过将 FMC_CTLx 寄存器的 START 位置 1 来发送页擦除命令到 FMC;
◼ 等待擦除指令执行完毕, FMC_STATx 寄存器的 BUSY 位清 0;
◼ 如果需要,使用 DBUS 读并验证该页是否擦除成功。
整片擦除
FMC 提供了整片擦除功能可以初始化主存储闪存块的内容。当设置 MER0 为 1 时,擦除过程仅作用于 Bank0,当设置 MER1 为 1 时,擦除过程仅作用于 Bank1,当设置 MER0 和 MER1 为 1 时,擦除过程作用于整片闪存。
整片擦除操作,寄存器设置具体步骤如下:
◼ 确保 FMC_CTLx 寄存器不处于锁定状态;
◼ 等待 FMC_STATx 寄存器的 BUSY 位变为 0;
◼ 如果单独擦除 Bank0,置位 FMC_CTL0 寄存器的 MER 位。如果单独擦除 Bank1,置位 FMC_CTL1 寄存器的 MER 位。如果整片擦除闪存,同时置位 FMC_CTL0 和 FMC_CTL1 寄存器的 MER 位;
◼ 通过将 FMC_CTLx 寄存器的 START 位置 1 来发送整片擦除命令到 FMC;
◼ 等待擦除指令执行完毕, FMC_STATx 寄存器的 BUSY 位清 0;
◼ 如果需要,使用 DBUS 读并验证是否擦除成功。
字编程操作
FMC 提供了一个 64 位、32 位整字/16 位半字编程功能,用来修改主存储闪存块内容。
编程操作使用各寄存器流程如下。
◼ 确保 FMC_CTLx 寄存器不处于锁定状态;
◼ 等待 FMC_STATx 寄存器的 BUSY 位变为 0;
◼ 置位 FMC_CTLx 寄存器的 PG 位;
◼ DBUS 写一个 32 位整字/16 位半字到目的绝对地址(0x08XXXXXX);
◼ 等待编程指令执行完毕, FMC_STATx 寄存器的 BUSY 位清 0;
◼ 如果需要,使用 DBUS 读并验证是否编程成功。
可选字节块擦除
FMC 提供了一个擦除功能用来初始化闪存中的可选字节块。
可选字节块擦除过程如下所示。
◼ 确保 FMC_CTL0 寄存器不处于锁定状态;
◼ 等待 FMC_STAT0 寄存器的 BUSY 位变为 0;
◼ 解锁 FMC_CTL0 寄存器的可选字节操作位;
◼ 等待 FMC_CTL0 寄存器的 OBWEN 位置 1;
◼ 置位 FMC_CTL0 寄存器的 OBER 位;
◼ 通过将 FMC_CTL0 寄存器的 START 位置 1 来发送可选字节块擦除命令到 FMC;
◼ 等待擦除指令执行完毕, FMC_STAT 寄存器的 BUSY 位清 0;
◼ 如果需要,使用 DBUS 读并验证是否擦除成功。当可选字节块擦除成功执行, FMC_STAT 寄存器的 ENDF 位置位。若 FMC_CTL0 寄存器的 ENDIE 位被置 1, FMC 将触发一个中断。
可选字节块编程
FMC 提供了一个 32 位整字/16 位半字编程功能,可用来修改可选字节块内容。可选字节块共有8 对可选字节。每对可选字节的高字节是低字节的补。当低字节被修改时, FMC 自动生成该选项字节的高字节。
字节块编程操作过程如下。
◼ 确保 FMC_CTL0 寄存器不处于锁定状态;
◼ 等待 FMC_STAT0 寄存器的 BUSY 位变为 0;
◼ 解锁 FMC_CTL0 寄存器的可选字节操作位;
◼ 等待 FMC_CTL0 寄存器的 OBWEN 位置 1;
◼ 置位 FMC_CTL0 寄存器的 OBPG 位;
◼ DBUS 写一个 32 位整字/16 位半字到目的地址;
◼ 等待编程指令执行完毕, FMC_STAT 寄存器的 BUSY 位清 0;
可选字节块说明
每次系统复位后,闪存的可选字节块被重加载到 FMC_OBSTAT 和 FMC_WP 寄存器,可选字节生效。可选字节的补字节具体为可选字节取反。当可选字节被重装载时,如果可选字节的补字节和可选字节不匹配, FMC_OBSTAT 寄存器的 OBERR 位将被置 1,可选字节被强制设置为 0xFF。若可选字节和其补字节同为 0xFF,则 OBERR 位不置位。
页擦除/编程保护
FMC 的页擦除/编程保护功能可以阻止对闪存的意外操作。当 FMC 对被保护页进行页擦除或编程操作时,操作本身无效且 FMC_STAT 寄存器的 WPERR 位将被置 1。如果 WPERR 位被置 1 且 FMC_CTL 寄存器的 ERRIE 位也被置 1 来使能相应的中断, FMC 将触发闪存操作出错中断,等待 CPU 处理。配置可选字节块的 WP [31:0]某位为 0 可以单独使能某几页的保护功能。如果在可选字节块执行了擦除操作,所有的闪存页擦除和编程保护功能都将失效。当可选字节的 WP 被改变时,需要系统复位使之生效。
12.3.软件配置说明
FMC 以 Program 配置为例来说明
Demo 一 (flash 编程)
demo 功能说明: MCU 上电启动后,对 MCU flash 的 0x8004000~ 0x08004800 的 2K 的区域写 0x01234567 数据,当编写错误的时候,LED 会亮。
软件配置步骤如下:
1)配置 led 指示灯;
gd_eval_led_init(LED2); gd_eval_led_init(LED3);
2)进行 page erase
void fmc_erase_pages(void) { uint32_t EraseCounter; fmc_unlock(); //FMC 解锁 fmc_flag_clear(FMC_FLAG_BANK0_END | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_PGERR ); //清除标志 for(EraseCounter = 0; EraseCounter < PageNum; EraseCounter++){ fmc_page_erase(FMC_WRITE_START_ADDR + (FMC_PAGE_SIZE * EraseCounter)); //page 擦除 fmc_flag_clear(FMC_FLAG_BANK0_END | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_PGERR ); //清除标志 } fmc_lock(); //FMC 加锁 }
- page erase 检查
void fmc_erase_pages_check(void) { uint32_t i; ptrd = (uint32_t *)FMC_WRITE_START_ADDR;//将写地址赋值指针 for(i = 0; i < WordNum; i++) { if(0xFFFFFFFF != (*ptrd)) //判断地址是否擦除成功 { lednum = LED2; gd_eval_led_on(lednum); //不成功的时候 LED2 会亮 break; }else { ptrd++; // 指针地址++ 再进行下一个地址判断 } } }
- flash 编程
void fmc_program(void) { fmc_unlock(); //FMC 解锁 address = FMC_WRITE_START_ADDR; while(address < FMC_WRITE_END_ADDR){ fmc_word_program(address, data0); //word 编程 address += 4; fmc_flag_clear(FMC_FLAG_BANK0_END | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_PGERR );//清除标志 } fmc_lock(); //FMC 加锁 }
- 字编程检查
void fmc_program_check(void) { uint32_t i; ptrd = (uint32_t *)FMC_WRITE_START_ADDR; //将写地址赋值指针 for(i = 0; i < WordNum; i++) { if((*ptrd) != data0) //判断写入的数据是否一致 { lednum = LED3; gd_eval_led_on(lednum); //不相等的时候 LED3 会亮 break; }else { ptrd++; // 指针地址++ 再进行下一个地址判断 } } }
12.4.FMC 使用注意事项
(1)操作 flash 之前需要 fmc_unlock();
(2)flash 编程之前需要 page 擦除;
(3)Page erase 和 program 之前需要先清空标志位;
(4)避免在 erase 或者 program 过程中出现掉电情况,用 flash 作为 eeprom 来用时,需要做好数据备份;
(5)在擦除 code data flash 过程中,擦除的优先级最高,此时任何中断都不响应。这时需要注意 MCU 与外界通讯时,数据丢失情况。
本
教程
由GD32 MCU方案商聚沃
科技原创
发布,了解更多GD32 MCU教程,关注聚沃科技官网