新唐 cortex - m0 FMC的使用

在有联网需求的嵌入式项目中经常有场景需要本地能够保存一些掉电不丢失的消息,比如区分冷热启动或者保存离线数据等等。 在需要保存数据的量比较小的时候为了节约成本,可以不用外部FLASH或EEPROM,使用主控的内部空间。

下面介绍周星星在新唐单片机开发之旅中的景点FMC。

基础操作 打开FMC驱动的头文件,找到我需要的四个基础函数: 1、使能函数; 2、读函数; 3、写函数; 4、擦除函数。 然后我先封装了一个任意地址写任意数量数据再验证。 嗯,失败了。 应该是封装的函数有问题吧。 这里提醒某些小伙伴一句千万不要像阿星一样驱动函数都没验证OK,先把应用代码敲完了。 有这个习惯的小伙伴可能认为反正驱动函数有问题后面再调,开发顺序正反都一样,况且官方的库函数怎么可能有问题呢。但是领导看见你搞了半天没有进度,他可是会着急的哦。 封装的函数验证失败后,我用库文件的3个元函数重新验证。伪代码如下: fmc_open(); fmc_write(addr,data); if(data == fmc_read(addr)) printf(“ok ! ”); else printf(“err ! ”); 打印结果是err。 当时阿星如下图所述 使能,写,读。 这个流程有问题吗? 我找到官方的例程,下载进去,验证OK。 我把自己的代码放到例程的初始化后面,验证OK。 有问题,初始化的问题! 可是例程初始化跟FMC相关的只有一个使能,而使能我也用了。 难道这个IC有自己的想法。 就在阿星胡思乱想的时候,公司的老司机指点了一句。 有没有去除写保护呢? 这个时候我再看例程的初始化。

我找到了问题的关键: 在写操作之前没有去除写保护。

任意数量写函数代码 水泥砖没问题了我们再来砌墙。 下面贴出我封装函数的代码。

void fmc_wr(uint32_t start_addr,uint32_t num_to_wr,void *pbuf) { uint32_t *buf = pbuf; uint32_t w_start_addr = start_addr; uint32_t page_offset = start_addr&0x1ff; uint32_t page_start = start_addr - page_offset; uint32_t page_end = page_start + FMC_FLASH_PAGE_SIZE; uint32_t w_end_addr = 0; uint32_t page_remain =FMC_PAGE_WORDS - (page_offset>>2); uint32_t buf_loop = 0,adr_loop = 0; if(page_remain > num_to_wr) page_remain = num_to_wr; w_end_addr = w_start_addr + (page_remain<<2); SYS_UnlockReg(); while(1) { if(VerifyData( w_start_addr, w_end_addr, 0xffffffff)) { buf_loop = 0; for(adr_loop = 0; adr_loop < page_offset; adr_loop += 4) { fmc_page_buf[buf_loop++] = FMC_Read(page_start+adr_loop); } for(adr_loop = w_start_addr; adr_loop < w_end_addr; adr_loop+=4) { fmc_page_buf[buf_loop++] = *buf; buf++; } for(adr_loop = w_end_addr; adr_loop < page_end; adr_loop+=4) { fmc_page_buf[buf_loop++] = FMC_Read(adr_loop); } FMC_Erase(page_start); fmc_wr_nochk(page_start,page_end,fmc_page_buf); } else { fmc_wr_nochk(w_start_addr,w_end_addr,buf); buf += page_remain; } if(num_to_wr == page_remain) break; page_start += FMC_FLASH_PAGE_SIZE; page_end += FMC_FLASH_PAGE_SIZE; w_start_addr += (page_remain<<2); page_offset = 0; num_to_wr -= page_remain; if(FMC_PAGE_WORDS >= num_to_wr) page_remain = num_to_wr; else page_remain = FMC_PAGE_WORDS; w_end_addr = w_start_addr + (page_remain<<2); } 在测试这个函数的时候阿星又犯了一个低级错误。 现象是做可靠性测试时,每过一段时间验证会失败一次,而且一旦失败,程序就在下图函数中死掉了。

后来发现测试函数中有个变量表示验证数组的元素位置,而这个变量在定时中断里有赋值的地方。结果出现了越界错误。 最后还有一点值得小伙伴在涉及FLASH开发中考虑,就是FLASH的擦写次数。一般FLASH的擦写次数在1万~10万次左右。如果可以的话尽量在低电或重启等情况在写入FLASH减少FLASH的擦写次数。

经验分享 程序员 微信小程序 职场和发展