本文介绍N32G030K8L7(以下简称N32)的内部flash读写操作。 在N32的芯片手册可以看到,N32的数据字节是以小端格式储存的,即数据低位存储在地址低位,数据高位则存放在地址高位,位权匹配。 下面是N32的flash规格 本文是介绍flash的读写操作,所以重点放在主存储区。可以看到主存储区容量大小为64K,平均分为128个页,每个页就是0.5KB,用于用户程序的存放和运行。也就是是说我们写的程序是存储在flash的主存储区的。 对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器管理。 闪存存储器有两种保护方式防止非法的访问(读、写、擦除): 在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行; 即在进行写或擦除操作时,不能进行代码或数据的读取操作。进行闪存编程操作时(写或擦除),必须打开内部的 RC 振荡器(HSI)。 注: 在低功耗模式下,所有闪存存储器的操作都被中止。 flash的规格就介绍完了,接下来开始flash的读写操作,先查阅一下芯片手册对读写操作的介绍: 通过芯片手册我们可以得知,flash在进行读操作前需要先进行flash的擦除(擦除的最小块为一个页,即0.5KB),而再擦除前还需要先进行flash模块的解锁。还有一个很关键的点为, flash的写操作仅支持32位操作,即在进行写操作时,是一次性写入32位数据的,那么在进行写操作的时候需注意地址偏移。即写操作的大致流程为: - flash模块的解锁(写入特定的键值序列到FLASH_KEY寄存器中,即可解锁flash模块);
- 对要写入数据的页进行擦除(擦除为1);
- 写入数据到已经擦除的页;
我们来看下芯片手册中官方给出的具体的擦除(页擦除)写入操作流程: 擦除和写入的第一步,在上边都有提到,应该先检查此时是否在进行闪存的读取操作,通过FLASH_STS.BUSY位来进行判断。即该位为0时才能进行闪存操作。 在进行主存储区写入数据操作时,需注意一些细节。 - 每次编程写入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[4]; data表示的就是该数组首元素的地址,所以该数组首元素的可以用 *data 来表示,而第二个元素可以用 *(data + 1)的形式来表示,这样,*data 和 *(data + 1)的地址之间是有一个字长的偏移量的。 而普通或者说一般的地址数据传递是这样: 例如有一个指针,void *pdata = 0x08008000;*pdata 表示就是地址 0x08008000 中存储的数据,而 *(pdata + 1)就是就是地址 0x08008001 存储的数据,*pdata 和 *(pdata + 1)的地址之间的偏移量就是一个节字。
|