STM32F1单片机-FLASH闪存

  • 一、FLASH简介
  • 二、FLASH工作原理
  • 三、读写内部FLASH
  • 四、读取芯片ID
  • 一、FLASH简介

  • STM32F1系列的FLASH(ROM掉电不丢失)包含程序存储器系统存储器选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程
  •   ROM掉电不丢失,存储介质FLASH,RAM掉电丢失,存储介质SRAM

  • 读写FLASH的用途:
  •   利用程序存储器的剩余空间来保存掉电不丢失的用户数据
      通过在程序中编程(IAP),实现程序的自我更新

  • 在线编程(ICP)用于更新存储器的全部内容,它通过JTAGSWD协议系统加载程序(Bootloader)下载程序
  • 程序中编程(IAP)可以使用微控制器支持的任一种通信接口下载程序
  •   下图为中容量闪存模块组织
      主存储器:用来存放程序代码,主要且容量最大的部分,基本单位是1K的页,C8T6只有64K
      信息块启动程序代码-系统存储器,存放原厂写入的Bootloader,用于串口下载。用户选择字节-存放独立的参数
      闪存存储器接口寄存器:普通外设,存储介质是SRAM,控制擦除和编程

    二、FLASH工作原理

      下图为FLASH基本结构图
      整个闪存分为程序存储器系统存储器选项字节
      以C8T6为例,程序存储器为64K,最后一页地址是0x0800 FC00。左边控制器是闪存管理员(FPEC),可以擦除和编程程序存储器选项字节。选项字节配置程序存储器的读写保护

    FPEC擦除和编程程序存储器和选项字节时需要前置操作

  • FLASH解锁:FLASH操作前需要解锁,在键寄存器写入指定的键值来实现
  •   FPEC有三个键值:RDPRT键 = 0x000000A5(解除读保护)、KEY1 = 0x45670123、KEY2 = 0xCDEF89AB
      复位后,FPEC被保护,不能写入FLASH_CR
      在FLASH_KEYR先写入KEY1,再写入KEY2解锁
      错误的操作序列会在下次复位前锁死FPEC和LASH_CR
      加锁:设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR

      下图为闪存页擦除过程
      判断LOCK锁没锁,需要解锁,随后置寄存器判断页地址、等待状态寄存器标志位擦除

      下图为闪存全擦除过程
      判断LOCK锁没锁,需要解锁,随后置寄存器、等待状态寄存器标志位擦除

      下图为闪存写入过程
      判断LOCK锁没锁,需要解锁,随后置寄存器、在指定地址写入数据、等待状态寄存器标志位

    三、读写内部FLASH

      读内部FLASH指定地址的数据,只需要调用读函数即可uint16_t Data = *((__IO uint16_t *)(0x8000000)),其中数据位数可以更改,地址可以更改
      指定地址写数据时,需要先擦除再写
      下面为MyFLASH.c,其中包括擦除写数据

    #include "stm32f10x.h"                  // Device header
    
    /*
    @brief:读取指定地址FLASH里的地址-32、16和8位
    */
    uint32_t MyFLASH_ReadWord(uint32_t Address)
    {
    	return *((__IO uint32_t *)(Address));//读取32位数据
    }
    uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
    {
    	return *((__IO uint16_t *)(Address));//读取16位数据
    }
    uint8_t MyFLASH_ReadByte(uint32_t Address)
    {
    	return *((__IO uint8_t *)(Address));//读取8位数据
    }
    
    /*
    @brief:全擦除
    */
    void MyFLASH_EraseAllPages()
    {
    	FLASH_Unlock();//解锁
    	FLASH_EraseAllPages();
    	FLASH_Lock();//锁上
    }
    /*
    @brief:指定页擦除
    */
    void MyFLASH_ErasePage(uint32_t PageAddress)
    {
    	FLASH_Unlock();//解锁
    	FLASH_ErasePage(PageAddress);
    	FLASH_Lock();//锁上
    }
    /*
    @brief:指定地址写32位数据
    */
    void MyFLASH_ProgramWord(uint32_t Address,uint32_t Data)
    {
    	FLASH_Unlock();//解锁
    	FLASH_ProgramWord(Address,Data);
    	FLASH_Lock();//锁上
    }
    /*
    @brief:指定地址写16位数据
    */
    void MyFLASH_ProgramHalfWord(uint32_t Address,uint16_t Data)
    {
    	FLASH_Unlock();//解锁
    	FLASH_ProgramHalfWord(Address,Data);
    	FLASH_Lock();//锁上
    }
    
    

      由于FLASH是擦除后再写入,擦除后还容易丢数据,在SRAM(掉电丢失)里新建一个数组,SRAM数组更改时,把数组更新到FLASH(掉电不丢失)里。上电时,再把FLASH里的数据初始化加载回到SRAM,SRAM数组相当于掉电不丢失
      首次上电,FLASH数据加载回到SRAM数组时,需要判断第一个数据-标志位(任意设置),之后断电上电或者复位时,FLASH里的数据(不丢失)继续复制给SRAM,标志位一直不变
      下面为Store.c

    uint16_t Store_Data[512];//存在了SRAM数组
    
    void Store_Init()
    {
    	//第一次上电需要判断标志位
    	if(MyFLASH_ReadHalfWord(0x0800FC00) != 0xA5A5)
    	{
    		MyFLASH_ErasePage(0x0800FC00);				//先擦除
    		MyFLASH_ProgramHalfWord(0x0800FC00,0xA5A5);	//首地址写入数据
    		for(uint16_t i = 1;i<512;i++)
    		{
    			MyFLASH_ProgramHalfWord(0x0800FC00+i*2,0x0000);//一个16位占两个地址
    		}
    	}//初始化后闪存最后一页第一个半字为A5A5,剩下全部为0
    	
    	for(uint16_t i = 0;i<512;i++)
    	{
    		Store_Data[i] = MyFLASH_ReadHalfWord(0x0800FC00+i*2);
    	}//上电时保证数据不丢失,将FLASH数据复制到SRAM数组中
    }
    /*
    @brief:往FLASH闪存里面写SRAM数组的数据(SRAM数组-FLASH)
    */
    void Store_Save()
    {
    	MyFLASH_ErasePage(0x0800FC00);//先擦除
    	for(uint16_t i = 0;i<512;i++)
    	{
    		MyFLASH_ProgramHalfWord(0x0800FC00+i*2,Store_Data[i]);//标志位又重新写入了
    	}
    }
    /*
    @brief:清除FLASH
    */
    void Store_Clear()
    {
    	for(uint16_t i = 1;i<512;i++)//标志位不擦除
    	{
    		Store_Data[i] = 0x0000;
    	}
    	Store_Save();
    }
    

      下面为main.c,按键1-SRAM数组值加1(除去标志位),按键2-清零数组

    uint8_t KeyNum;
    
    int main(void)
    {
    	OLED_Init();
    	Key_Init();
    	Store_Init();
    	
    	OLED_ShowString(1,1,"FLAG:");
    	OLED_ShowString(2,1,"Data:");
    	while(1)
    	{
    		KeyNum = Key_GetNum();
    		if(KeyNum == 1)
    		{
    			Store_Data[1] ++;
    			Store_Data[2] += 2;
    			Store_Data[3] += 3;
    			Store_Data[4] += 4;
    			Store_Save();//SRAM数组写入到FLASH
    		}
    		if(KeyNum == 2)
    		{
    			Store_Clear();
    		}
    		OLED_ShowHexNum(1,6,Store_Data[0],4);
    		OLED_ShowHexNum(3,1,Store_Data[1],4);
    		OLED_ShowHexNum(3,6,Store_Data[2],4);
    		OLED_ShowHexNum(4,1,Store_Data[3],4);
    		OLED_ShowHexNum(4,6,Store_Data[4],4);
    	}
    }
    

    四、读取芯片ID

      读取ID只需要读取指定地址的数据即可

    int main(void)
    {
    	OLED_Init();
    	OLED_ShowString(1,1,"F_SIZE:");
    	OLED_ShowHexNum(1,8,*((__IO uint16_t *)(0x1FFFF7E0)),4);
    	OLED_ShowString(2,1,"U_ID:");
    	OLED_ShowHexNum(2,6,*((__IO uint16_t *)(0x1FFFF7E8)),4);
    	OLED_ShowHexNum(2,11,*((__IO uint16_t *)(0x1FFFF7E8+0x02)),4);
    	OLED_ShowHexNum(3,1,*((__IO uint32_t *)(0x1FFFF7E8+0x04)),8);
    	OLED_ShowHexNum(4,1,*((__IO uint32_t *)(0x1FFFF7E8+0x08)),8);
    
    	while(1)
    	{
    		
    	}
    }
    

    作者:侥幸哥f

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32F1单片机-FLASH闪存

    发表回复