本文介绍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_STS.BUSY位来进行判断。即该位为0时才能进行闪存操作。
在进行主存储区写入数据操作时,需注意一些细节。
清楚了的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操作无关的操作。
可以看到,官方在进行写操作时,也是按照流程来的:
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,还一直没有发现,很难受。