qiufengsd 发表于 2024-7-22 02:58

读写内部FLASH

本文介绍N32G030K8L7(以下简称N32)的内部flash读写操作。https://img-blog.csdnimg.cn/0aeaeb07bc544c748d6f752d52d42d6f.png在N32的芯片手册可以看到,N32的数据字节是以小端格式储存的,即数据低位存储在地址低位,数据高位则存放在地址高位,位权匹配。下面是N32的flash规格https://img-blog.csdnimg.cn/4e3b3b586cb74ccfbfdf957ad99514ca.png 本文是介绍flash的读写操作,所以重点放在主存储区。可以看到主存储区容量大小为64K,平均分为128个页,每个页就是0.5KB,用于用户程序的存放和运行。也就是是说我们写的程序是存储在flash的主存储区的。https://img-blog.csdnimg.cn/83d32be858c140aeb4b92be96cf8561a.png对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器管理。闪存存储器有两种保护方式防止非法的访问(读、写、擦除):
[*]页写入保护(WRP)
[*]读出保护(RDP)
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;即在进行写或擦除操作时,不能进行代码或数据的读取操作。进行闪存编程操作时(写或擦除),必须打开内部的 RC 振荡器(HSI)。注: 在低功耗模式下,所有闪存存储器的操作都被中止。flash的规格就介绍完了,接下来开始flash的读写操作,先查阅一下芯片手册对读写操作的介绍:https://img-blog.csdnimg.cn/1fa8929dee4b4d95bc97b4511d0caf54.png通过芯片手册我们可以得知,flash在进行读操作前需要先进行flash的擦除(擦除的最小块为一个页,即0.5KB),而再擦除前还需要先进行flash模块的解锁。还有一个很关键的点为,flash的写操作仅支持32位操作,即在进行写操作时,是一次性写入32位数据的,那么在进行写操作的时候需注意地址偏移。即写操作的大致流程为:
[*]flash模块的解锁(写入特定的键值序列到FLASH_KEY寄存器中,即可解锁flash模块);
[*]对要写入数据的页进行擦除(擦除为1);
[*]写入数据到已经擦除的页;
我们来看下芯片手册中官方给出的具体的擦除(页擦除)写入操作流程:https://img-blog.csdnimg.cn/7df0fac4bdf449e2a4f29a1a1c2d9ecc.pnghttps://img-blog.csdnimg.cn/ba1c269626cd440bb4b30888dc68b959.png擦除和写入的第一步,在上边都有提到,应该先检查此时是否在进行闪存的读取操作,通过FLASH_STS.BUSY位来进行判断。即该位为0时才能进行闪存操作。在进行主存储区写入数据操作时,需注意一些细节。https://img-blog.csdnimg.cn/b36e3808dfca4bfb890fdf84cdbf25e6.png
[*]每次编程写入32位(即1个字)的数据,写入其他位数的数据,将产生总线错误,触发HardFault_Handler硬件错误中断;
[*]在进行任何读写闪存操作时,都会使CPU暂停,直至本次闪存操作结束,即在一次闪存操作没有结束前不能进行下一次闪存操作;
清楚了的flash的读写流程,下面我们看一下官方给的例程源码:#include "main.h"#include <stdio.h>#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操作无关的操作。可以看到,官方在进行写操作时,也是按照流程来的:
[*]进行flash模块的解锁,通过调用flash解锁函数,FLASH_Unlock();
[*]在需要写入的页进行页擦除,通过调用页擦除函数,FLASH_EraseOnePage(uint32_t Page_Address),参数为需擦除页的起始地址;
[*]在已经擦除的页进行写操作,通过调用字编程函数,FLASH_ProgramWord(uint32_t Address, uint32_t Data),第一个参数为要写入的起始地址,第二个参数为要写入的数据(注意为32位);
[*]对写入的数据进行检查,将写入的数据读出再与源数据进行对比,读取flash数据可以使用下面的形式
Data = (*(__IO uint32_t*)(DataAddr));      //将DataAddr所指向的内存地址中的值作为一个32位无符号整数(uint32_t)类型返回。想要得到正确的数据,在flash的写入和读取时,地址要相匹配。N32的FLASH读写操作到此就讲解完了,讲的有些啰嗦了。另外提一点作者在flash读写操作中出现的问题:将数组通过指针进行数据传递的问题。比如有一个数组,uint32_t data; data表示的就是该数组首元素的地址,所以该数组首元素的可以用 *data 来表示,而第二个元素可以用 *(data + 1)的形式来表示,这样,*data 和 *(data + 1)的地址之间是有一个字长的偏移量的。而普通或者说一般的地址数据传递是这样:例如有一个指针,void *pdata = 0x08008000;*pdata 表示就是地址 0x08008000 中存储的数据,而 *(pdata + 1)就是就是地址 0x08008001 存储的数据,*pdata 和 *(pdata + 1)的地址之间的偏移量就是一个节字。
页: [1]
查看完整版本: 读写内部FLASH