4.1 实验内容 通过本实验主要学习以下内容: • FMC控制器原理; • FMC擦写读操作; 4.2 实验原理 4.2.1 FMC控制器原理 FMC即Flash控制器,其提供了片上Flash操作所需要的所有功能,在GD32F4xx系列MCU中,Flash前512KB字节空间内, CPU执行指令零等待,具有相同主频下最快的代码执行效率。FMC也提供了扇区擦除和整片擦除操作,以及32位整字/16位半字/字节编程等闪存操作。 另外GD32F470xx, GD32F427xx和GD32F425xx系列还额外提供了页(4KB)擦除操作 GD32F4xx系列MCU支持最大3M Flash空间。 GD32F4xx系列MCU可以支持最大3M的Flash空间,BANK0的空间为1MB,前4个扇区为16KB,第五个扇区为64KB,之后的扇区为128KB;BANK1的空间为2MB,前4个扇区为16KB,第五个扇区为64KB,之后七个扇区为128KB;剩下的扇区为256KB。 有关Flash擦写操作均需要先解锁Flash,然后进行擦写操作,擦写完成后再进行锁Flash,注意Flash特性只能由1写0,也就是Flash需要先擦除才能写入新的数据,如果确保写入地址的数据为全0xFF,也可以直接写入。读取Flash数据可以采取直接寻址的方式进行读取。 |
下面为各位读者介绍Flash擦写读的相关操作。 4.2.2 Flash擦除操作原理 Flash擦除可分为扇区擦除以及整片擦除,如下图所示,扇区擦除时间典型值为200-600ms(根据扇区大小进行区分),整片擦除也根据容量大小会有差异。 有关Flash的相关操作均在gd32f4xx_fmc.c中实现,下面介绍下擦除实现的函数,如下表所示。 4.2.3 Flash写入编程操作原理 GD32F4xx系列MCU可支持32位整字编程/16位半字以及字节编程,如下图所示,Flash 32位整字编程时间典型值为37.5us。 有关Flash编程实现函数如下表所示。 4.2.4 Flash读取操作原理 Flash读取可以采用直接寻址的方式进行操作,具体可参考以下示例代码。 C
uint32_t read_data;
read_data = *(uint32_t *)0x08001000; • 注意:有关Flash有以下参数读者需要了解,GD32F4xx系列MCU的内部Flash具有至少10万次的擦写次数以及20年的数据保持能力,但需注意,随着擦写次数的增加数据保持时间会下降。 |
4.3 硬件设计 本例程不涉及硬件电路。 4.4 代码解析 4.4.1 Flash写入多字节函数 Flash写入多字节操作函数如下所示,写入的过程主要分为擦写两个操作,由于Flash特有特性,需要先擦除才可以写入,因而需要确保写入地址的初识数据为0xFF。本函数可以实现根据地址识别对应页并进行擦除的功能,使用上非常方便,使用者只需要关心擦写的起始地址以及数据和长度即可,擦写的位置函数中会进行实现。 C
void fmc_write_data(uint32_t write_start_addr, uint8_t *data_buf, uint16_t data_lengh)
{
uint32_t write_addr,erase_addr;
uint16_t data_write_num=0;
int32_t data_earse_num;
fmc_unlock(); /* 解锁FMC */
/* 清除错误标志 */
fmc_flag_clear(FMC_FLAG_RDDERR|FMC_FLAG_PGSERR|FMC_FLAG_PGMERR|FMC_FLAG_WPERR|FMC_FLAG_OPERR);
erase_addr = write_start_addr;
data_earse_num = data_lengh;
if(write_start_addr%FLAG_PAGE_SIZE == 0) /* 若写入地址为页起始地址 */
{
for(;data_earse_num>0;)
{
fmc_page_erase(erase_addr);
/* 清除错误标志 */
fmc_flag_clear(FMC_FLAG_RDDERR|FMC_FLAG_PGSERR|FMC_FLAG_PGMERR|FMC_FLAG_WPERR|FMC_FLAG_OPERR);
erase_addr+=FLAG_PAGE_SIZE;
data_earse_num-=FLAG_PAGE_SIZE;
}
}else{
/*若写入地址不是页起始地址*/
for(;(data_earse_num>0||erase_addr>=write_start_addr+data_lengh);)
{
fmc_page_erase(erase_addr);
fmc_flag_clear(FMC_FLAG_RDDERR|FMC_FLAG_PGSERR|FMC_FLAG_PGMERR|FMC_FLAG_WPERR|FMC_FLAG_OPERR);
erase_addr+=FLAG_PAGE_SIZE;
data_earse_num-=FLAG_PAGE_SIZE;
}
}
/* 写入数据 */
write_addr = write_start_addr;
for(data_write_num = 0; data_write_num<data_lengh;data_write_num++)
{
fmc_byte_program(write_addr, data_buf[data_write_num]);
fmc_flag_clear(FMC_FLAG_RDDERR|FMC_FLAG_PGSERR|FMC_FLAG_PGMERR|FMC_FLAG_WPERR|FMC_FLAG_OPERR);
write_addr++;
}
fmc_lock();
} 4.4.2 Flash读取数据函数 Flash读取数据函数如下所示,采用直接寻址的方式,读取字节数据。 C
uint8_t fmc_read_data(uint32_t write_read_addr)
{
return *(uint8_t *)write_read_addr;
}
4.4.3 主函数 主函数如下所示,通过该函数实现对flash起始地址为0x080A0000的前10个字节擦写以及读取的验证。 C
int main(void)
{
uint16_t read_num =0;
uint8_t i_num;
driver_init();
bsp_led_group_init();
bsp_uart_init(&BOARD_UART); /* 板载UART初始化 */
printf_log("Example of internal Flash read-write demo.\r\n");
printf_log("Write data to internal Flash.\r\n");
fmc_write_data(WRITE_START_ADDR,write_data,sizeof(write_data)); /* 向WRITE_START_ADDR地址写入10个双字节数据 */
printf_log("Read data from internal Flash.\r\n");
for(read_num=0;read_num<sizeof(write_data);read_num++)
{
read_data[read_num] = fmc_read_data(WRITE_START_ADDR+read_num); /* 从WRITE_START_ADDR读取10个双字节数据 */
}
printf_log("Verify the written and read data.\r\n");
for(i_num=0;i_num<sizeof(write_data);i_num++)
{
/* 校验数据 */
if(read_data[i_num]!=write_data[i_num])
{
/* 校验数据出错 */
printf_log("Error in verifying data.\r\n");
printf_log("Turn on LED1.\r\n");
bsp_led_on(&LED1);
while(1);
}else{
}
}
/* 校验数据成功 */
printf_log("Turn on LED1.\r\n");
bsp_led_on(&LED1);
printf_log("Verify that the data is correct and that the written and read data are consistent.\r\n");
while (1)
{
}
}
4.5 实验结果 将本实验烧录到紫藤派实验板中,运行后可以观察到LED1常亮,表明擦写以及读取实验正常。 本教程由GD32 MCU方案商聚沃科技原创发布,了解更多GD32 MCU教程,关注聚沃科技官网,GD32MCU技术交流群:859440462
|