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

STM32学习笔记13-FLASH闪存

FLASH简介

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

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

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

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

闪存模块组织

FLASH基本结构

FLASH解锁 

FPEC共有三个键值:

  • 1)RDPRT= 0x000000A5
  • 2)KEY1 = 0x45670123
  • 3)KEY2 = 0xCDEF89AB
  • 解锁的方式:
  • 复位后,FPEC被保护,不能写入FLASH_CR,也就是复位后FLASH默认是锁着的,然后在FLASH_KEYR先写入KEY1,再写入KEY2,解锁。错误的操作序列会在下次复位前锁死FPECFLASH_CR。

  • 解锁之后如何加锁呢?我们操作完成之后要尽快把FLASH重新加锁,以防止意外情况。

  • 加锁:

  • 设置FLASH_CR中的LOCK位锁住FPECFLASH_CR。

  • 接下来看一下如何使用C语言指针访问存储器:

 

程序存储器全擦除

程序存储器擦除

 

程序存储器编程

擦除之后我们就可以执行写入的流程了,STM32的闪存在写入之前会检查指定地址有没有擦除,如果没有擦除就写入STM32则不执行写入操作,除非写入的全是0,这一个数据是个例外。

 

选项字节

简单介绍一下,了解即可。

  • RDP:写入RDPRT键(0x000000A5)后解除读保护
  • USER:配置硬件看门狗和进入停机/待机模式是否产生复位
  • Data0/1:用户可自定义使用
  • WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)

选项字节编程

  1. 检查FLASH_SRBSY位,以确认没有其他正在进行的编程操作
  2. 解锁FLASH_CROPTWRE
  3. 设置FLASH_CROPTPG位为1
  4. 写入要编程的半字到指定的地址
  5. 等待BSY位变为0
  6. 读出写入的地址并验证数据

选项字节擦除

  1. 检查FLASH_SRBSY位,以确认没有其他正在进行的闪存操作
  2. 解锁FLASH_CROPTWRE
  3. 设置FLASH_CROPTER位为1
  4. 设置FLASH_CRSTRT位为1
  5. 等待BSY位变为0
  6. 读出被擦除的选择字节并做验证

器件电子签名

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

1)闪存容量寄存器:

          基地址:0x1FFF F7E0

          大小:16

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

          基地址: 0x1FFF F7E8

          大小:96

FLASH应用

读写内部FLASH

代码整体规划如下:

 

接下来看一下库函数:

void FLASH_Unlock(void);//用来解锁
void FLASH_Lock(void);//加锁
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);//指定地址写入半字

 完整代码:

MyFLASH.c:

#include "stm32f10x.h"                  // Device header/*读取字(32位)
*/
uint32_t MyFLASH_ReadWord(uint32_t Address)
{return *((__IO uint32_t *)(Address));
}/*读取半字(16位)
*/
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{return *((__IO uint16_t *)(Address));
}/*读取字节(8位)
*/
uint8_t MyFLASH_ReadByte(uint32_t Address)
{return *((__IO uint8_t *)(Address));
}/*全擦除
*/
void MyFLASH_EraseAllPages(void)
{FLASH_Unlock();//第一步对FLASH解锁FLASH_EraseAllPages();//第二步直接调库函数FLASH_Lock();//第三步锁上FLASH
}/*页擦除
* 参数 PageAddress:要擦除的页地址
*/
void MyFLASH_ErasePage(uint32_t PageAddress)
{FLASH_Unlock();//第一步对FLASH解锁FLASH_ErasePage(PageAddress);//第二步直接调库函数FLASH_Lock();//第三步锁上FLASH
}/*编程,写入一个字
* 参数1 Address:要写入的地址
* 参数2 Data:32位的数据
*/
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{FLASH_Unlock();//第一步对FLASH解锁FLASH_ProgramWord(Address, Data);//第二步直接调库函数FLASH_Lock();//第三步锁上FLASH
}/*编程,写入半字
* 参数1 Address:要写入的地址
* 参数2 Data:16位的数据
*/
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{FLASH_Unlock();//第一步对FLASH解锁FLASH_ProgramHalfWord(Address, Data);//第二步直接调库函数FLASH_Lock();//第三步锁上FLASH
}

Store.c:

#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"#define STORE_START_ADDRESS		0x0800FC00
#define STORE_COUNT 			512uint16_t Store_Data[STORE_COUNT];void Store_Init(void)
{/* 初始化闪存,最后一页第一个半字是标志位A5A5,剩下数据全是0 *///0xA5A5是随便定义的标志位,如果第一个半字不是A5A5就说明是第一次使用if(MyFLASH_ReadHalfWord(0x08000000) != 0xA5A5){MyFLASH_ErasePage(STORE_START_ADDRESS);//擦除最后一页MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);//在第一个半字的位置写入规定的标志位for(uint16_t i = 1;i < STORE_COUNT;i++)//把剩余的存储空间全都置为默认值0{MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i*2, 0x0000);}}/* 上电时把闪存数据转存到SRAM数组 */for(uint16_t i = 0;i < STORE_COUNT;i++){Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i*2);}
}/*SRAM数组备份保存到闪存
*/
void Stort_Save(void)
{/* 第一步擦除最后一页 */MyFLASH_ErasePage(STORE_START_ADDRESS);/* 第二步把数组完全备份保存到闪存最后一页 */for(uint16_t i = 0;i < STORE_COUNT;i++){MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i*2, Store_Data[i]);}
}/*数据清零
*/
void Store_Clear(void)
{for(uint16_t i = 1;i < STORE_COUNT;i++){Store_Data[i] = 0x0000;}Stort_Save();//把更改更新到闪存
}

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();Key_Init();Store_Init();//第一次使用的时候初始化闪存,把闪存备份的数据加载回SRAM数组OLED_ShowString(1, 1, "Flag:");OLED_ShowString(2, 1, "Data:");while(1){KeyNum = Key_GetNum();if(KeyNum == 1){Store_Data[1] ++;//第0个位置是标志位,不能用Store_Data[2] += 2;Store_Data[3] += 3;Store_Data[4] += 4;Stort_Save();//把SRAM数组备份到闪存}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);}
}
http://www.lryc.cn/news/424892.html

相关文章:

  • UIButton的UIEdgeInsetsMake属性(setTitleEdgeInsets,setImageEdgeInsets)
  • 子网掩码是什么?
  • SQLALchemy 数据的 CRUD 操作
  • reactFiberLane
  • Hackademic.RTB1靶场实战【超详细】
  • 让3岁小孩都能理解LeetCode每日一题_3148.矩阵中的最大得分
  • 8.15日学习打卡---Spring Cloud Alibaba(三)
  • 2024下半年EI学术会议一览表
  • 【海奇HC-RTOS平台E100-问题点】
  • 性能测试之Mysql数据库调优
  • 使用 RestHighLevelClient 进行 Elasticsearch 高亮查询及解析
  • Java基础入门15:算法、正则表达式、异常
  • SpringBoot响应式编程 WebFlux入门教程
  • LeetCode 383. 赎金信
  • python绘制电路图
  • Vue3 Suspense 和 defineAsyncComponent 结合使用方法
  • GitHub中Codespace怎么使用;LLM模拟初始化;MLP:全连接神经网络的并行执行
  • 【rh】rh项目部署
  • VoxelNet: End-to-End Learning for Point Cloud Based 3D Object Detection
  • 结构开发笔记(三):solidworks软件(二):小试牛刀,绘制一个立方体
  • LLM 量化算法AutoRound 0.3 发布及原理浅析
  • 汽车免拆诊断案例 | 2013款北京现代悦动车发动机偶尔无法起动
  • React、AntD,封装动态表单
  • 【Linux基础】Linux中的开发工具(3)--make/makefile和git的使用
  • 过滤了字母、数字、_、$的webshell命令执行技巧
  • python-A+B again
  • C语言—函数递归
  • 结构开发笔记(四):solidworks软件(三):绘制36x36方块摄像头示意体
  • 【机器学习】Caltech-101的基本概念和使用方法以及Caltech-101和ImageNet的联系和区别
  • mysql Ubuntu安装与远程连接配置