1954|1

230

帖子

1

资源

一粒金砂(高级)

【GD32L233C-START评测】15. flash擦写操作,将FLASH当做EEPROM使用 [复制链接]

本帖最后由 hehung 于 2022-2-19 16:24 编辑

之前的帖子可以参考:

 

【GD32L233C-START评测】1.开箱

【GD32L233C-START评测】2.手把手创建新工程

【GD32L233C-START评测】3.移植FreeRTOS到GD32L233

【GD32L233C-START评测】4. 移植RT-Thread到GD32L233

【GD32L233C-START评测】5. IIC驱动OLED

【GD32L233C-START评测】6. 获取RTC时间并通过OLED显示

【GD32L233C-START评测】7. PWM驱动LED

【GD32L233C-START评测】8. TRNG真随机数生成

【GD32L233C-START评测】9. CRC检验

【GD32L233C-START评测】10. ADC读取芯片内部温度

【GD32L233C-START评测】11.DAC输出电压值_ADC读取外部电压值

【GD32L233C-START评测】12. 硬件IIC驱动OLED

【GD32L233C-START评测】13. CAU加密算法之DES/TDES

【GD32L233C-START评测】14. CAU加密算法之AES

 

一、前言

        我们平时在开发单片机程序的时候经常会有这样的情况:需要将某些数据保存起来掉电不丢失,我们一般会选择使用EEPROM来保存数据,但是有些单片机没有提供内部EEPROM,则需要我们外接EEPROM,这样增大了开发成本,如果我们不接外设EEPROM,需要如何操作呢?

 

        这就需要用到我们的FLASH,将FLASH当做一个EEPROM一样进行操作,但是FLASH操作的时候也有一个缺点,就是FLASH在擦除的时候只能一页一页的擦除,不能单独擦除几个字节,所以我们在使用FLASH保存数据的时候需要注意不要将我们的代码程序擦除了,或者我们不期望擦除的程序擦除了。

 

        一般使用FLASH来模拟EEPROM的时候,我们在擦除FLASH页的时候都需要现将数据备份,然后将待写入的数据更新,然后将擦除的整个页写入到指定的地址中去。

 

        FLASH也是我们保存代码的地方,代码一般存储在FLASH的前面,后面一部分用不到,我们就可以用来保存我们的数据,但是如何确保我们的数据不会在下载代码的时候被覆盖呢,我们需要修改一下地址范围,在下面说明

 

二、FLASH操作原理

        在前言中,我们了解了FLASH作为EEPROM使用的一些基本知识之后,我们还需要知道怎么在GD32L233C中实现这个操作。

        GD32L233C的FMC的操作在《用户手册》第2章 2. 闪存控制器( 闪存控制器(FMC )

        

1.  FLASH大小

        我们需要了解GD32L233C的FLASH的大小,在用户手册中没有描述,参考《数据手册》第2章 2.1. Device information

        如下所示,GD32L233C的FLASH大小位256KB

         1.png

2. FLASH结构

        了解FLASH结构,对代码编辑必不可少,如下图,256KB大小的FLASH被分为了64页,每页4KBytes。

        FLASH在擦除的时候一次性只能擦除4KBytes。

        如下截图有一处笔误? 第63页,地址范围应该是0x0803F000 - 0x0803FFFF? 2.png 3. FLASH解锁

        在对FLASH进行操作之前需要对Flash进行解锁,操作完毕之后需要上锁。

        参考《数据手册》2.3.3. FMC_CTL 寄存器解锁

        本帖不在做详细描述,因为GD32L233x的库文件已经提供了解锁和上锁的函数,可以直接调用,不需要我们自己再去操作寄存器

 

4. FLASH页擦除

        FLASH页擦除参考《用户手册》2.3.4. 页擦除

        本帖不做详细描述,库文件中提供的相应的接口,直接调用即可。

        注意:针对GD32L233C这款芯片,一页是4KByes,在擦除的时候谨慎操作,不要将代码区域的代码夜擦除了。

 

5. FLASH写入

        这是本文的重点内容,可以参考《用户手册》2.3.6. 主存储闪存块编程  以及  2.3.7. 主存储闪存块 快速编程

        GD32L233x提供了两种写FLASH的方式,需要注意这两种写入方式的操作逻辑以及一次写得字节大小。

        对于寄存器相关的操作逻辑了解即可,GD32官方库文件已经提供了写FLASH接口,直接调用即可。

 

        下面重要说一下这两种写入方式的区别:

        (1)FLASH普通写入

                使用这种方式,一次写入的数据是32位或者16位,也就是4字节或者2字节,对于C语言的unsigned int类型和unsigned short类型,在编程的时候一定要注意

                本帖中提供的函数接口只适用了32位的形式,为了方便操作。    

                看《用户手册》中已经写得比较清楚了,如果用在写32位的时候你传入的数据不是32位的,flash不会写进去,到时候不要出现了问题不知道为什么。

                模拟EEPROM使用一般就是用的这种方式,可以写入一个16位或者32位的数据

                 3.png

        (2)FLASH快速写入

                对于快速写入方式,一次性写一行,一行是32个双字,一定要注意是32个双字,不能是64个字

                应为GD32L233x是32位的单片机,所以一个字是4个字节,双字也就是8个字节,对应的数据类型是unsigned long int

                所以在使用快速方式写入flash的时候,需要定义一个32个元素的数据,每个数据是8字节的,这种方式一般是用来刷新FLASH的,可以快速的写入,比如我们编写bootloader程序来更新我们的程序就可以使用这种方式来编写下载flash算法。            

                对于使用中的注意事项,《用户手册》中也有比较详细的描述,如下:

                 4.png

三、如何防止代码更新擦除了我们写在FLASH中的数据

        看了《用户手册》以及《数据手册》之后,我们知道了FLASH的范围,也知道的FLASH擦写的操作逻辑以及一些注意事项,同时为了避免我们写在flash中的数据被下载代码的时候更新,我们需要对编译器进行设置一下。

        前言中已经说了代码一般保存在FLASH的前面,我们要保存数据在FLASH中就写在FLASH的后边,再加上FLASH擦除是以页为单位的,所以至少也要用到一页的FLASH空间,本帖中的代码使用了2页,操作涉及到的地址范围是:0x0803E000~0x0803FFFF。

        所以修改FLASH代码区域的长度修改为0x3E000,原来是0x40000,如下:

5.png

四、代码实现

1. 宏定义

        宏定义中定义了FLASH的起始地址与结束地址,以及一页的数据大小,以及两种FLASH写入方式

        E_OK以及E_NOT_OK是FLASH擦除校验是否成功的返回值

        FMC_PROGRAM_TYPE_WORD表示FLASH的普通写入模式

        FMC_PROGRAM_TYPE_FAST表示FLASH的快速写入模式

        FMC_PAGE_SIZE表示FLASH一页的大小,GD32L233C是4KBytes,也就是4096,对应16进制的0x1000

        FMC_START_ADDRESS是FLASH开始地址,为0x08000000

        FMC_END_ADDRESS是FLASH结束地址,为0x0803FFFF

#define E_OK                         ((uint8_t)0U)
#define E_NOT_OK                     ((uint8_t)1U)

#define FMC_PROGRAM_TYPE_WORD        ((uint8_t)0x00U)
#define FMC_PROGRAM_TYPE_FAST        ((uint8_t)0x01U)

#define FMC_PAGE_SIZE                ((uint16_t)0x1000U)

#define FMC_START_ADDRESS            ((uint32_t)0x08000000U)
#define FMC_END_ADDRESS              ((uint32_t)0x0803FFFFU)

 

2. 全局变量定义

        全局变量定义了我们期望写到FLASH中的数据

        data0和data1是使用普通模式写FLASH的32位数据

        data_buffer是使用快速模式写入的32个双字的数组

uint32_t data0   = 0x01234567U;
uint32_t data1   = 0xd583179bU;

/* data buffer for fast programming */
static uint64_t data_buffer[DOUBLE_WORDS_CNT_IN_ROW] = {
    0x0000000000000000U, 0x1111111111111111U, 0x2222222222222222U, 0x3333333333333333U,
    0x4444444444444444U, 0x5555555555555555U, 0x6666666666666666U, 0x7777777777777777U,
    0x8888888888888888U, 0x9999999999999999U, 0xAAAAAAAAAAAAAAAAU, 0xBBBBBBBBBBBBBBBBU,
    0xCCCCCCCCCCCCCCCCU, 0xDDDDDDDDDDDDDDDDU, 0xEEEEEEEEEEEEEEEEU, 0xFFFFFFFFFFFFFFFFU,
    0x0011001100110011U, 0x2233223322332233U, 0x4455445544554455U, 0x6677667766776677U,
    0x8899889988998899U, 0xAABBAABBAABBAABBU, 0xCCDDCCDDCCDDCCDDU, 0xEEFFEEFFEEFFEEFFU,
    0x2200220022002200U, 0x3311331133113311U, 0x6644664466446644U, 0x7755775577557755U,
    0xAA88AA88AA88AA88U, 0xBB99BB99BB99BB99U, 0xEECCEECCEECCEECCU, 0xFFDDFFDDFFDDFFDDU
};

 

3. 擦FLASH

        FLASH擦除是以页位单位的,所以传入的参数就是第几页,从数据手册中我们知道把FLASH区域分为了64页,所以可传入的参数就是0-63

        该函数会擦除指定页,当传入参数是63的时候,debug会报错,见最后。

uint8_t fmc_erase_pages(uint8_t page_num)
{
	uint32_t s_addr = 0;
	
	if(page_num < 64)
	{
		s_addr = FMC_START_ADDRESS + (FMC_PAGE_SIZE * page_num);

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

		/* clear all pending flags */
		fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);

		/* erase the flash pages */
		fmc_page_erase(s_addr);
		fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);

		/* lock the main FMC after the erase operation */
		fmc_lock();
		
		/* check whether erased successful */
		return fmc_erase_pages_check(s_addr);
	}
	return E_NOT_OK;
}

 

4. 擦除数据校验

        该函数用于检查数据指定页擦除之后数据是否全部都是0xFF,如果不是,说明没有擦除完整。

static uint8_t fmc_erase_pages_check(uint32_t s_addr)
{
    uint32_t i;
    uint32_t *ptrd;
	uint8_t ret = E_OK;

    ptrd = (uint32_t *)s_addr;

    /* check flash whether has been erased */
    for(i = 0; i < (FMC_PAGE_SIZE >> 2); i++) 
	{
		/* check 4Bytse every time */
        if(0xFFFFFFFF != (*ptrd)) 
		{
			ret = E_NOT_OK;
            break;
        } 
		else 
		{
            ptrd++;
        }
    }
	
	return ret;
}

 

5. FLASH普通模式写入数据

        普通模式写入上面已经说过了,一次性写入一个32位的数据,也就是UInt32_t类型的数据,参数data就是期望写入的32位数据。

        s_addr是指定写入的地址,地址范围最好使用fmc_erase_pages() 擦除过的页地址范围里面,避免没有擦除就写数据上报总线错误。

static void fmc_program_word(uint32_t s_addr, uint32_t data)
{
	
    /* unlock the flash program/erase controller */
    fmc_unlock();

    /* program flash */
	fmc_word_program(s_addr, (uint32_t)data);    

	fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);

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

 

6. FLASH快速写入模式写入数据

        快速模式上面已经说过了,一次性写入32个双字,参数data就是期望写入的32个双字数据

        s_addr是指定写入的地址,地址范围最好使用fmc_erase_pages() 擦除过的页地址范围里面,避免没有擦除就写数据上报总线错误。

static void fmc_program_fast(uint32_t s_addr, uint64_t* data)
{
	
    /* unlock the flash program/erase controller */
    fmc_unlock();

    /* program flash */
	fmc_fast_program(s_addr, data_buffer);

	fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGAERR | FMC_FLAG_PGERR);

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

 

7. main函数

        如下:

        先擦除63页,地址范围是0x0803E000 ~ 0x0803EFFF

        然后将data0写入地址0x0803E000

        将data1写入地址0x0803E010

        将数组data_buffer写入地址0x0803E014

int main(void)
{
 //   fmc_erase_pages(63);    /* Bubug will error */

    fmc_erase_pages(62);
    fmc_program_word(0x0803E000, data0);
	fmc_program_word(0x0803E010, data1);
    fmc_program_fast(0x0803E014, data_buffer);

    while(1)
	{
	
	}
}

 

 

五、检验效果

        使用在线Debug的方式来运行我们的代码,然后再memory窗口中查看flash地址的数据,如下,测试成功

        0x0803E000地址被写入了0x01234567,数据是反着的是因为编码方式是小端模式

        0x0803E010地址被写入了0xd583179b,数据是反着的是因为编码方式是小端模式

        0x0803E014地址被写入了数组data_buffer的内容
6.png

六、代码bug

        在擦除最后一页flash,也就是64页的时候debug程序会报错,不知道为什么?有知道的小伙伴可以解答一下,谢谢

        如下图,也不知道是我的代码bug还是官方库的bug,等我后边再细细研究下。

     7.png

 

此帖出自GD32 MCU论坛

回复

8

帖子

0

资源

一粒金砂(中级)

会不会是在第三步那里,防止flash刷那一片内存,然后编译器以为64页不是它本身的东西,所以debug报错,我没修改第三步,debug好像没啥问题

此帖出自GD32 MCU论坛

回复
您需要登录后才可以回帖 登录 | 注册

相关帖子
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
    关闭
    站长推荐上一条 1/6 下一条

    About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

    站点相关: 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

    北京市海淀区知春路23号集成电路设计园量子银座1305 电话:(010)82350740 邮编:100191

    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2022 EEWORLD.com.cn, Inc. All rights reserved
    快速回复 返回顶部 返回列表