STM32F103C8T6使用内部Flash的第63页保存参数

1.概述

       ~~~~~~        STM32的FLASH是用来存储主程序的,ST公司为了节约成本,没有加入 EEPROM,但是许多场合下我们需要用EEPROM;不过FLASH的容量还是可观的,我们可以利用FLASH模拟EEPROM。

       ~~~~~~       根据《STM32F10X闪存编程》中的介绍,FLASH除了保存用户代码的部分,其余部分我们是可以利用其作为数据存储使用的。stm32的FLASH分为主存储块和信息块。主存储块用于保存具体的程序代码和用户数据,信息块用于负责由stm32出厂是放置2KB的启动程序(Bootloader)并锁死,用户无法更改。选项字节存储芯片的配置信息及对主存储块的保护信息。STM32的FLASH主存储块按页组织,有的产品每页1KB,有的产品每页2KB。页面典型的用途就是用于按页擦除FLASH。从这点来看,页面有点像通用FLASH的扇区。

       ~~~~~~       通常情况下程序也不会把FLASH写满;在没满的时候我们可以把最后一或两页用来模拟EEPROM;这样我们就可以不用在外部另外来加EEPROM了。下面是STM32F103中文手册关于FLASH的截图;由于我用的是STM32C8T6做的实验;手册中没有给STM32C8T6的FLASH的地址信息,容量是64K,可以算出第63页地址是0X800FC00-0X800FFFF。

       ~~~~~~       在我们应用开发时,经常会有一些程序运行参数需要保存,如一些修正系数。这些数据的特点是:数量少而且不需要经常修改,但又不能定义为常量,因为每台设备可能不一样而且在以后还有修改的可能。将这类数据存在指定的位置,需要修改时直接修改存储位置的数值,需要使用时则直接读取,会是一种方便的做法。考虑到这些数据量比较少,使用专门的存储单元既不经济,也没有必要,而STM32F103内部的Flash容量较大,而且ST的库函数中还提供了基本的Flash操作函数,实现起来也比较方便。

2.Flash组织

       ~~~~~~       以中容量产品STM32C8T6为例,其Flash容量达到64K,可以将其中一部分用作数据存储。如下是小容量,中容量,大容量的Flash组织模式:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.实现代码

1.宏定义

       ~~~~~~       根据上面的Flash组织模式,我们可以根据自己的使用方便来作相应的定义。因为大容量每个扇区定义为2K,而小容量和中容量都定义为1K,所以我们做如下宏定义

#define FLASH_SIZE 64          //所选MCU的FLASH容量大小(单位为K)

#if FLASH_SIZE<256
  #define SECTOR_SIZE           1024    //字节
#else 
  #define SECTOR_SIZE           2048    //字节
#endif

2.读操作

       ~~~~~~       虽然ST的库函数比较全面,但都是基本操作,为了使用方面,根据我们自己的需要对其进行再次封装。

       ~~~~~~       对于读操作相对比较简单,内置闪存模块可以在通用地址空间直接寻址,就像读取变量一样。

//从指定地址开始读取多个数据
void FLASH_ReadMoreData(uint32_t startAddress,uint16_t *readData,uint16_t countToRead)
{
  uint16_t dataIndex;
  for(dataIndex=0;dataIndex<countToRead;dataIndex++)
  {
    readData[dataIndex]=FLASH_ReadHalfWord(startAddress+dataIndex*2);
  }
}

//读取指定地址的半字(16位数据)
uint16_t FLASH_ReadHalfWord(uint32_t address)
{
  return *(__IO uint16_t*)address; 
}

//读取指定地址的全字(32位数据)
uint32_t FLASH_ReadWord(uint32_t address)
{
  uint32_t temp1,temp2;
  temp1=*(__IO uint16_t*)address; 
  temp2=*(__IO uint16_t*)(address+2); 
  return (temp2<<16)+temp1;
}

2.写操作

       ~~~~~~       对于写操作相对来说要复杂得多,写操作包括对用户数据的写入和擦除。为了防止误操作还有写保护锁。但这些基本的操作ST的库函数已经为我们写好了,我们只需要调用即可。

       ~~~~~~       STM32复位后,FPEC模块是被保护的,只有在写保护被解除后,我们才能操作相关寄存器。STM32闪存的编程每次必须写入16位,任何不是半字的操作都会造成错误。如下图是Flash写的过程。
在这里插入图片描述
       ~~~~~~        STM32的FLASH在编程的时候,也必须要求其写入地址的FLASH 是被擦除了的(也就是其值必须是0XFFFF),否则无法写入。Flash的擦除要求必须整页擦除,所以也必须整页写入,否则可能会丢失数据。如下图是Flash页擦除过程:
在这里插入图片描述
       ~~~~~~       如下为Flash全擦除过程,
在这里插入图片描述
       ~~~~~~       根据以上图示我们便写数据写入函数如下:

//从指定地址开始写入多个数据
void FLASH_WriteMoreData(uint32_t startAddress,uint16_t *writeData,uint16_t countToWrite)
{
  if(startAddress<FLASH_BASE||((startAddress+countToWrite*2)>=(FLASH_BASE+1024*FLASH_SIZE)))
  {
    return;//非法地址
  }
  FLASH_Unlock();         //解锁写保护
  uint32_t offsetAddress=startAddress-FLASH_BASE;               //计算去掉0X08000000后的实际偏移地址
  uint32_t sectorPosition=offsetAddress/SECTOR_SIZE;            //计算扇区地址,对于STM32F103VET6为0~255
  
  uint32_t sectorStartAddress=sectorPosition*SECTOR_SIZE+FLASH_BASE;    //对应扇区的首地址

  FLASH_ErasePage(sectorStartAddress);//擦除这个扇区
  
  uint16_t dataIndex;
  for(dataIndex=0;dataIndex<countToWrite;dataIndex++)
  {
    FLASH_ProgramHalfWord(startAddress+dataIndex*2,writeData[dataIndex]);
  }
  
  FLASH_Lock();//上锁写保护
}

       ~~~~~~       在擦除之前应该将页面上的数据读取出来与要写入的数据合并,待擦除后再写入,但这样数据量很大(大容量是2K一个扇区),所以考虑到是少量数据存储,所以每次都将全部数据同时写入,简化操作,也减少数据处理量。经测试以上程序写入和读出数据均正确,可以实现内部Flash的读写操作。需要更深入了解可以参考《STM32F10xxx 闪存编程参考手册》。

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐