当前位置: 首页 > news >正文

STM32学习笔记19-FLASH

FLASH简介

  • STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程,读取指定寄存器直接使用指针读即可

  • 读写FLASH的用途:

              利用程序存储器的剩余空间来保存掉电不丢失的用户数据

              通过在程序中编程(IAP),实现程序的自我更新,类似于OTA

  • 在线编程(In-Circuit Programming – ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序
  • 在程序中编程(In-Application Programming – IAP)可以使用微控制器支持的任一种通信接口下载程序

STM32F10xxx闪存编程参考手册

闪存模块组织

        特征与W25Q64类似

       这里的存储器分化:只有一个页,每页大小都是1K字节。地址范围:起始地址:只要以000、400、800、C00结尾的。

FLASH基本结构

FLASH解锁

  • FPEC共有三个键值:

               RDPRT键 = 0x000000A5——解除读保护的密钥

                KEY1 = 0x45670123——这是自定义的密码

                KEY2 = 0xCDEF89AB

  • 解锁:

               复位后,FPEC被保护,不能写入FLASH_CR

               在FLASH_KEYR先写入KEY1,再写入KEY2,解锁

               错误的操作序列会在下次复位前锁死FPEC和FLASH_CR

  • 加锁:

               设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR

读取:

使用指针访问存储器,因为STM32的内部存储器是挂载在总线上的

  • 使用指针读指定地址下的存储器:
       uint16_t Data = *((__IO uint16_t *)(0x08000000));
  • 使用指针写指定地址下的存储器:——需要解锁等提高权限
     *((__IO uint16_t *)(0x08000000)) = 0x1234;
  • 其中:
   #define  __IO  volatile      //防止编译器优化

程序存储器编程

程序存储器页擦除

程序存储器全擦除

选项字节

  • RDP:写入RDPRT键(0x000000A5)后解除读保护
  • USER:配置硬件看门狗和进入停机/待机模式是否产生复位
  • Data0/1:用户可自定义使用
  • WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)
  • 带n,当写入USER时要同时在nUSET写入数据的反码,这样才是有效的——一般函数会自动执行

选项字节编程

  • 解锁闪存
  • 检查FLASH_SR的BSY位,以确认没有其他正在进行的编程操作
  • 解锁FLASH_CR的OPTWRE位
  • 设置FLASH_CR的OPTPG位为1
  • 写入要编程的半字到指定的地址
  • 等待BSY位变为0
  • 读出写入的地址并验证数据

选项字节擦除

  • 解锁闪存
  • 检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作
  • 解锁FLASH_CR的OPTWRE位
  • 设置FLASH_CR的OPTER位为1
  • 设置FLASH_CR的STRT位为1
  • 等待BSY位变为0
  • 读出被擦除的选择字节并做验证

器件电子签名

  • 电子签名存放在闪存存储器模块的系统存储区域,包含的芯片识别信息在出厂时编写,不可更改,使用指针读指定地址下的存储器可获取电子签名
  • 闪存容量寄存器:

              基地址:0x1FFF F7E0

              大小:16位

  • 产品唯一身份标识寄存器:

              基地址: 0x1FFF F7E8

              大小:96位

接线图:

15-1 读写内部FLASH

最底层模块MyFALSH实现读取、擦除和编程

再此模块上建Store模块,实现参数数据的读写和存储管理:任意读写参数,并且这些参数是掉电不丢失的-定义SRAM数组,把需要掉电不丢失的数据放入到SRAM中,之后调用保存的函数使SRAM数组自动备份到闪存里;上电后,Store初始化,自动会把闪存里的数据读回SRAM数组里——闪存管理策略

使用STM32 ST-LINK Utility进行调试:可以看到闪存里的数据;

       注:使用完,记得断开连接不然设备占用,导致程序运行不了

思路:

闪存不需要初始化

读取:直接使用指针

擦除:先解锁,调用全擦除函数或页擦除函数,之后加锁

编程:先解锁,调用函数,之后加锁

选项字节的操作与闪存类似,有对应的函数,但是同时是用软件进行图形化修改

FLASH相关函数:系统分为三大部分的库函数——对应不同的配置,此实验只用第一部分

void FLASH_Unlock(void);           //解锁

void FLASH_Lock(void);              //加锁

//对主闪存和选项字节的操作配置,返回值FLASH_Status一个执行状态

FLASH_Status FLASH_ErasePage(uint32_t Page_Address);             //闪存擦除某一页

FLASH_Status FLASH_EraseAllPages(void);              //全擦除

FLASH_Status FLASH_EraseOptionBytes(void);        //擦除选项字节

FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);        //在指定地址写入字

FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);        //在指定地址写入半字

//选项字节的写入

FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);            //自定义的Data0和Data1

FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages);          //写保护

FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState);         //读保护

FLASH_Status FLASH_UserOptionByteConfig(uint16_t OB_IWDG, uint16_t OB_STOP, uint16_t OB_STDBY);         //用户选项的三个配置位

//获取选项字节当前的状态

uint32_t FLASH_GetUserOptionByte(void);       //获取用户选项的三个配置位

uint32_t FLASH_GetWriteProtectionOptionByte(void);           //获取写保护

FlagStatus FLASH_GetReadOutProtectionStatus(void);          //获取写保护

//状态位

FlagStatus FLASH_GetFlagStatus(uint32_t FLASH_FLAG);

void FLASH_ClearFlag(uint32_t FLASH_FLAG);

FLASH_Status FLASH_GetStatus(void);             //获取状态

FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);           等待上一次操作——在前面的函数中自调用了,一般不需要单独调用


配置闪存地址空间——keil魔术棒里Debug对应设备的接口

手动分配闪存尾部的数据空间,给自己用;

擦除模式


判断程序大小

分别是:代码;只读数据、读写数据、零初始化数据


main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Store.h"
#include "Key.h"uint8_t KeyNum;					//定义用于接收按键键码的变量int main(void)
{/*模块初始化*/OLED_Init();				//OLED初始化Key_Init();					//按键初始化Store_Init();				//参数存储模块初始化,在上电的时候将闪存的数据加载回Store_Data,实现掉电不丢失/*显示静态字符串*/OLED_ShowString(1, 1, "Flag:");OLED_ShowString(2, 1, "Data:");while (1){KeyNum = Key_GetNum();		//获取按键键码if (KeyNum == 1)			//按键1按下{Store_Data[1] ++;		//变换测试数据Store_Data[2] += 2;Store_Data[3] += 3;Store_Data[4] += 4;Store_Save();			//将Store_Data的数据备份保存到闪存,实现掉电不丢失}if (KeyNum == 2)			//按键2按下{Store_Clear();			//将Store_Data的数据全部清0}OLED_ShowHexNum(1, 6, Store_Data[0], 4);	//显示Store_Data的第一位标志位OLED_ShowHexNum(3, 1, Store_Data[1], 4);	//显示Store_Data的有效存储数据OLED_ShowHexNum(3, 6, Store_Data[2], 4);OLED_ShowHexNum(4, 1, Store_Data[3], 4);OLED_ShowHexNum(4, 6, Store_Data[4], 4);}
}
Store.c
#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"#define STORE_START_ADDRESS		0x0800FC00		//存储的起始地址
#define STORE_COUNT				512				//存储数据的个数uint16_t Store_Data[STORE_COUNT];				//定义SRAM数组/*** 函    数:参数存储模块初始化* 参    数:无* 返 回 值:无*/
void Store_Init(void)
{/*判断是不是第一次使用*/if (MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5)	//读取第一个半字的标志位,if成立,则执行第一次使用的初始化{MyFLASH_ErasePage(STORE_START_ADDRESS);					//擦除指定页MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);	//在第一个半字写入自己规定的标志位,用于判断是不是第一次使用for (uint16_t i = 1; i < STORE_COUNT; i ++)				//循环STORE_COUNT次,除了第一个标志位{MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000);		//除了标志位的有效数据全部清0}}/*上电时,将闪存数据加载回SRAM数组,实现SRAM数组的掉电不丢失*/for (uint16_t i = 0; i < STORE_COUNT; i ++)					//循环STORE_COUNT次,包括第一个标志位{Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2);		//将闪存的数据加载回SRAM数组}
}/*** 函    数:参数存储模块保存数据到闪存* 参    数:无* 返 回 值:无*/
void Store_Save(void)
{MyFLASH_ErasePage(STORE_START_ADDRESS);				//擦除指定页for (uint16_t i = 0; i < STORE_COUNT; i ++)			//循环STORE_COUNT次,包括第一个标志位{MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]);	//将SRAM数组的数据备份保存到闪存}
}/*** 函    数:参数存储模块将所有有效数据清0* 参    数:无* 返 回 值:无*/
void Store_Clear(void)
{for (uint16_t i = 1; i < STORE_COUNT; i ++)			//循环STORE_COUNT次,除了第一个标志位{Store_Data[i] = 0x0000;							//SRAM数组有效数据清0}Store_Save();										//保存数据到闪存
}MyFLASH.c
#include "stm32f10x.h"                  // Device header/*** 函    数:FLASH读取一个32位的字* 参    数:Address 要读取数据的字地址* 返 回 值:指定地址下的数据*/
uint32_t MyFLASH_ReadWord(uint32_t Address)
{return *((__IO uint32_t *)(Address));	//使用指针访问指定地址下的数据并返回
}/*** 函    数:FLASH读取一个16位的半字* 参    数:Address 要读取数据的半字地址* 返 回 值:指定地址下的数据*/
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{return *((__IO uint16_t *)(Address));	//使用指针访问指定地址下的数据并返回
}/*** 函    数:FLASH读取一个8位的字节* 参    数:Address 要读取数据的字节地址* 返 回 值:指定地址下的数据*/
uint8_t MyFLASH_ReadByte(uint32_t Address)
{return *((__IO uint8_t *)(Address));	//使用指针访问指定地址下的数据并返回
}/*** 函    数:FLASH全擦除* 参    数:无* 返 回 值:无* 说    明:调用此函数后,FLASH的所有页都会被擦除,包括程序文件本身,擦除后,程序将不复存在*/
void MyFLASH_EraseAllPages(void)
{FLASH_Unlock();					//解锁FLASH_EraseAllPages();			//全擦除FLASH_Lock();					//加锁
}/*** 函    数:FLASH页擦除* 参    数:PageAddress 要擦除页的页地址* 返 回 值:无*/
void MyFLASH_ErasePage(uint32_t PageAddress)
{FLASH_Unlock();					//解锁FLASH_ErasePage(PageAddress);	//页擦除FLASH_Lock();					//加锁
}/*** 函    数:FLASH编程字* 参    数:Address 要写入数据的字地址* 参    数:Data 要写入的32位数据* 返 回 值:无*/
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{FLASH_Unlock();							//解锁FLASH_ProgramWord(Address, Data);		//编程字FLASH_Lock();							//加锁
}/*** 函    数:FLASH编程半字* 参    数:Address 要写入数据的半字地址* 参    数:Data 要写入的16位数据* 返 回 值:无*/
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{FLASH_Unlock();							//解锁FLASH_ProgramHalfWord(Address, Data);	//编程半字FLASH_Lock();							//加锁
}

15-2 读取芯片ID

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
//#include "OLED_Font.h"
//4-1 OLED显示屏
int main(void){OLED_Init();OLED_ShowString(1,1,"F_SIZE:");OLED_ShowHexNum(1,8,*((__IO uint16_t *)(0x1FFFF7E0)),4);//ID地址:0x1FFFF7E0//显示UIDOLED_ShowString(2,1,"UID:");//偏移量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){}
}

http://www.xdnf.cn/news/1353565.html

相关文章:

  • [Mysql数据库] 选择备份策略选择题
  • 工业场景烟雾识别误报率↓82%!陌讯多模态融合算法实战解析
  • 水泉村信息化服务小程序的设计与实验
  • 54 C++ 现代C++编程艺术3-移动构造函数
  • 用 Go + GitHub Models API 打造一个免费的 ChatBot
  • 全面解析JVM预热:原理、价值与实践指南
  • MYSQL-约束
  • 【数据结构】线性表——链表
  • 微服务的编程测评系统15-头像上传-OSS
  • 高阶数据结构---ST表
  • kafaka知识要点
  • VLOOKUP专题训练
  • UE C++ 堆化
  • windows中bat脚本的一些操作(三)
  • 算法第五十五天:图论part05(第十一章)
  • 图论与最短路学习笔记
  • 【数据结构】跳表的概率模型详解与其 C 代码实现
  • 深度学习开篇
  • `strlen` 字符串长度函数
  • python 字典有序性的实现和OrderedDict
  • 计算机网络 各版本TLS握手的详细过程
  • 电脑零广告快响应提速(一)之卸载搜狗输入法使用RIME—东方仙盟
  • python re模块常用方法
  • MySQL详细介绍指南
  • 蓝牙aoa仓库管理系统功能介绍
  • [e3nn] 归一化 | BatchNorm normalize2mom
  • 【技术突破】动态目标误检率↓83.5%!陌讯多模态融合算法在智慧城管的实战优化
  • 基于电力电子变压器的高压脉冲电源方案复现
  • 使用 Certbot 申请 Apache 证书配置棘手问题
  • 【数据结构】计数排序:有时比快排还快的整数排序法