GD32F303调试小记(六)之片内FLASH读写

前言

用单片机做开发时,为了实现产品的某些功能,我们通常都需要记录一些数据并放在单片机掉电不丢失的区域中。在现在的主流8位机中,一般为EEPROM和FLASH存储区。这些存储区一般与我们的代码存储区是分开来的且一般不大(多数在1KB以内)。EEPROM是可以按字节读、写的,而FLASH则是以块为单位擦除、写入的。而在32位单片机中,一般不会为单独的数据存储再开辟一个区域,即存代码的和存这些数据的是同一个区域。所以,如果你看过其他有关32位机内部FLASH读写操作的,它们基本都是从FLASH最后一个或几个区块操作的,本文给出的示例也是如此。

RAM与ROM

讲内部FLASH读、写之前,咱们先简单聊聊单片机的RAM与ROM。RAM就是我们所说的内存,具有读写快的特点,但是掉电数据丢失。ROM为只读存储器,在这里指的就是FLASH,具有掉电不丢失的特性。选择FLASH型MCU的好处在于便于前中期调试与后期维护,与之相对的还有OTP与MTP型MCU。它们之间最大的区别在于可重复烧录次数的限制,这也导致了在整个代码调试上有很多区别。

各模块程序编写

在配置前,请确保你已经有一个GD32F303包含其对应标准库的keil工程,工程可使用官方的例程或可按照GD32F303调试小记(零)之工程创建与编译创建。

一、片内FLASH映射关系

请添加图片描述
请添加图片描述

  • 根据上面的描述,我们知道整个FLASH区的地址范围。整个FALSH分为两个BANK区,对于FLASH容量不大于512K的都在BANK0区,每页大小为2KB。对于FLASH容量大于512的除去前512KB在BANK0以外,后面的都在BANK1中,每页大小为4KB。
  • 由于FLASH区更多是用来存储代码的,你编写烧录的代码会从FLASH的第0页,也就是从0x-0800-0000开始占用一定的FLASH页数。要是你操作到这些地方,我们可以想象会发生什么样的后果。所以,我们一般根据你手上的芯片大小,往往都从最后一个或最后几个FLASH页上操作:
/*
 * bank0--2kB一页  bank1--4KB一页
 * 前512K在Bank0中,后面在Bank1中
 * 前256K CPU执行指令零等待
 */

/* 256KB FLASH */ 
//#define FMC_PAGE_SIZE           ((uint16_t)0x800U)		
//#define FMC_WRITE_START_ADDR    ((uint32_t)0x0803F800U)
//#define FMC_WRITE_END_ADDR      ((uint32_t)0x0803FFFFU)	

/* 1MB FLASH */ 
#define FMC_PAGE_SIZE           ((uint16_t)0x1000U)			/* bank1 4kB each page */
#define FMC_WRITE_START_ADDR    ((uint32_t)0x080FF000U)
#define FMC_WRITE_END_ADDR      ((uint32_t)0x080FFFFFU)	

二、FLASH擦除

  • 直接套用官方例程中的擦除函数,这里我使用了BANK1中的FALSH页,对应FMC_FLAG_BANK1_END、FMC_FLAG_BANK1_WPERR、和FMC_FLAG_BANK1_PGERR这三个标志位。BANK0的话修改这三个即可。
  • 这里是多页擦除,PageNum 这个变量算的就是页数,这里我是擦除一页。
/* calculate the number of page to be programmed/erased */
uint32_t PageNum = (FMC_WRITE_END_ADDR - FMC_WRITE_START_ADDR + 1) / FMC_PAGE_SIZE;

void fmc_erase_pages(void)
{
    
    
    uint32_t EraseCounter;

    /* unlock the flash program/erase controller */
    fmc_unlock();

    /* clear all pending flags */
    fmc_flag_clear(FMC_FLAG_BANK1_END);
    fmc_flag_clear(FMC_FLAG_BANK1_WPERR);
    fmc_flag_clear(FMC_FLAG_BANK1_PGERR);
    
    /* erase the flash pages */
    for(EraseCounter = 0; EraseCounter < PageNum; EraseCounter++)
    {
    
    
 		fmc_page_erase(FMC_WRITE_START_ADDR + (FMC_PAGE_SIZE * EraseCounter));
		fmc_flag_clear(FMC_FLAG_BANK1_END | FMC_FLAG_BANK1_WPERR | FMC_FLAG_BANK1_PGERR);
    }

    /* lock the main FMC after the erase operation */
    fmc_lock();
}

二、FLASH写入

  • 这个也是沿用官方例程的写入函数,底层我用的是按字写入的方式,本想封装成多字写入的,后面因为忙忘了改了。注意清所在页对应的BANK标志位。
void fmc_program(uint32_t * data,uint32_t addressx)
{
    
    
    /* unlock the flash program/erase controller */
    fmc_unlock();
 
	uint32_t address = addressx;
    /* program flash */
    fmc_word_program(address, *data);
    fmc_flag_clear(FMC_FLAG_BANK1_END | FMC_FLAG_BANK1_WPERR | FMC_FLAG_BANK1_PGERR);
    /* lock the main FMC after the program operation */
    fmc_lock();
}
  • 你可以尝试改成这样,变成每次单字写入:
void fmc_program(uint32_t data,uint32_t addressx)
{
    
    
    /* unlock the flash program/erase controller */
    fmc_unlock();
    
    /* program flash */
    fmc_word_program(addressx, data);
    fmc_flag_clear(FMC_FLAG_BANK1_END | FMC_FLAG_BANK1_WPERR | FMC_FLAG_BANK1_PGERR);
    /* lock the main FMC after the program operation */
    fmc_lock();
}

三、FLASH读取

  • 这个是最简单的,定义一个指针类型,指向之前记忆的地址,就能获取其中的内容,与官方封装寄存器地址一样,我们这么写:
/* 读FLASH */
#define FMC_READ(addrx) ( *(volatile uint32_t*)(uint32_t)(addrx) )
  • 我们再定义一些我们要存数据的地址:
/* 定义地址(32位机总线一次访问4个字节地址) */
#define CLIP12V_AD_ADDR ((uint32_t)(FMC_WRITE_START_ADDR + 0x000U))
#define LANGUAGE_ADDR 	((uint32_t)(FMC_WRITE_START_ADDR + 0x004U))
#define BRIGHT_ADDR 	((uint32_t)(FMC_WRITE_START_ADDR + 0x008U))

四、主函数

  • 这里其实就没什么好展示的了,先给出写入的伪代码:

  • 当某个按键按下,或什么条件触发,先擦除FLASH页,再写入要存的数据。

void task_key_event(void)
{
    
    
	if(key_flag == 3)
	{
    
    
		fmc_erase_pages();
		fmc_program(&ADV_Clip12V_AD,CLIP12V_AD_ADDR);			
		fmc_program(&Language_current,LANGUAGE_ADDR);	
	}
}
  • 读取则是更随意了,在任何你需要的地方添加你要的这个即可:
	ADV_Clip12V_AD = FMC_READ(CLIP12V_AD_ADDR);
	Language_current = FMC_READ(LANGUAGE_ADDR);
	Bright_Level = FMC_READ(BRIGHT_ADDR);

五、总结

  • 总的来所,32位机的操作更简单,尤其是在读取上。
  • 另外插播一个消息,郭天祥老师和他的团队将会在全平台出一套关于GD32F303的教程(就是那个10天教会51单片机的那个),涵盖的知识面会更加全面,点击此处
  • 接下来我个人关于GD32F303的文章还剩下两个部分,窗口看门狗和定时器出PWM(部分功能),之后关于GD32的内容基本就要画上句号了,感谢各位的点击。

!!!本文为欢喜6666在CSDN原创发布,复制或转载请注明出处:)!!!

猜你喜欢

转载自blog.csdn.net/qq_37554315/article/details/123447334
今日推荐