国民技术MCU(N32G0)-读写内部FLASH

本文介绍N32G030K8L7(以下简称N32)的内部flash读写操作。

国民技术MCU(N32G0)-读写内部FLASH_第1张图片

在N32的芯片手册可以看到,N32的数据字节是以小端格式储存的,即数据低位存储在地址低位,数据高位则存放在地址高位,位权匹配。

下面是N32的flash规格

国民技术MCU(N32G0)-读写内部FLASH_第2张图片

 本文是介绍flash的读写操作,所以重点放在主存储区。可以看到主存储区容量大小为64K,平均分为128个页,每个页就是0.5KB,用于用户程序的存放和运行。也就是是说我们写的程序是存储在flash的主存储区的。

国民技术MCU(N32G0)-读写内部FLASH_第3张图片

对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器管理。

闪存存储器有两种保护方式防止非法的访问(读、写、擦除):

  • 页写入保护(WRP)
  • 读出保护(RDP)

执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;

即在进行写或擦除操作时,不能进行代码或数据的读取操作。进行闪存编程操作时(写或擦除),必须打开内部的 RC 振荡器(HSI)

注: 在低功耗模式下,所有闪存存储器的操作都被中止

flash的规格就介绍完了,接下来开始flash的读写操作,先查阅一下芯片手册对读写操作的介绍:

国民技术MCU(N32G0)-读写内部FLASH_第4张图片

通过芯片手册我们可以得知,flash在进行读操作前需要先进行flash的擦除(擦除的最小块为一个页,即0.5KB),而再擦除前还需要先进行flash模块的解锁。还有一个很关键的点为,

flash的写操作仅支持32位操作,即在进行写操作时,是一次性写入32位数据的,那么在进行写操作的时候需注意地址偏移。即写操作的大致流程为:

  1. flash模块的解锁(写入特定的键值序列到FLASH_KEY寄存器中,即可解锁flash模块);
  2. 对要写入数据的页进行擦除(擦除为1);
  3. 写入数据到已经擦除的页;

我们来看下芯片手册中官方给出的具体的擦除(页擦除)写入操作流程:

国民技术MCU(N32G0)-读写内部FLASH_第5张图片

国民技术MCU(N32G0)-读写内部FLASH_第6张图片

擦除和写入的第一步,在上边都有提到,应该先检查此时是否在进行闪存的读取操作,通过FLASH_STS.BUSY位来进行判断。即该位为0时才能进行闪存操作。

在进行主存储区写入数据操作时,需注意一些细节。

  1. 每次编程写入32位(即1个字)的数据,写入其他位数的数据,将产生总线错误,触发HardFault_Handler硬件错误中断;
  2. 在进行任何读写闪存操作时,都会使CPU暂停,直至本次闪存操作结束,即在一次闪存操作没有结束前不能进行下一次闪存操作;

清楚了的flash的读写流程,下面我们看一下官方给的例程源码:

#include "main.h"
#include 

#define FLASH_PAGE_SIZE        ((uint16_t)0x200)        //512个字节
#define FLASH_WRITE_START_ADDR ((uint32_t)0x08008000)
#define FLASH_WRITE_END_ADDR   ((uint32_t)0x08010000)

int main(void)
{
    uint32_t Counter_Num = 0;
    uint32_t Erase_Data  = 0xCDEF89AB;
    
    /* Unlocks the FLASH Program Erase Controller */
    FLASH_Unlock();

    /* Erase */
    if (FLASH_COMPL != FLASH_EraseOnePage(FLASH_WRITE_START_ADDR))
    {
        //FLASH擦除错误处理
    }
    
    /* Program */
    for (Counter_Num = 0; Counter_Num < FLASH_PAGE_SIZE; Counter_Num += 4)
    {
        if (FLASH_COMPL != FLASH_ProgramWord(FLASH_WRITE_START_ADDR + Counter_Num, Erase_Data))
        {
            //写入操作错误处理
        }
    }
    
    /* Locks the FLASH Program Erase Controller */
    FLASH_Lock();

    /* Check */
    for (Counter_Num = 0; Counter_Num < FLASH_PAGE_SIZE; Counter_Num += 4)
    {
        if (Erase_Data != (*(__IO uint32_t*)(FLASH_WRITE_START_ADDR + Counter_Num)))
        {
            //检查错误处理
        }
    }
    
    while (1)
    {
    }
}
    

上面是官方给予的例程源码,我在此基础上删掉了一些与flash操作无关的操作。

可以看到,官方在进行写操作时,也是按照流程来的:

  1. 进行flash模块的解锁,通过调用flash解锁函数,FLASH_Unlock();
  2. 在需要写入的页进行页擦除,通过调用页擦除函数,FLASH_EraseOnePage(uint32_t Page_Address),参数为需擦除页的起始地址;
  3. 在已经擦除的页进行写操作,通过调用字编程函数,FLASH_ProgramWord(uint32_t Address, uint32_t Data),第一个参数为要写入的起始地址,第二个参数为要写入的数据(注意为32位);
  4. 对写入的数据进行检查,将写入的数据读出再与源数据进行对比,读取flash数据可以使用下面的形式
Data = (*(__IO uint32_t*)(DataAddr));        //将DataAddr所指向的内存地址中的值作为一个32位无符号整数(uint32_t)类型返回。

想要得到正确的数据,在flash的写入和读取时,地址要相匹配。N32的FLASH读写操作到此就讲解完了,讲的有些啰嗦了。

另外提一点作者在flash读写操作中出现的问题:

将数组通过指针进行数据传递的问题。

比如有一个数组,uint32_t data[4]; data表示的就是该数组首元素的地址,所以该数组首元素的可以用 *data 来表示,而第二个元素可以用 *(data + 1)的形式来表示,这样,*data*(data + 1)的地址之间是有一个字长的偏移量的。

而普通或者说一般的地址数据传递是这样:

例如有一个指针,void *pdata = 0x08008000*pdata 表示就是地址 0x08008000 中存储的数据,而 *(pdata + 1)就是就是地址 0x08008001 存储的数据,*pdata*(pdata + 1)的地址之间的偏移量就是一个节字。

作者在进行flash读写操作时就将这两种指针给混淆了,导致在一整天的时间里,存储的数据和读取的数据对应不上,很难受。当然,这是作者自己写的bug,还一直没有发现,很难受。

你可能感兴趣的:(国民技术(N32G030),单片机,mcu)