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

STM32单片机-FLASH闪存

STM32单片机-FLASH闪存

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

一、FLASH简介

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

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

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

  下图为中容量闪存模块组织
  主存储器:用来存放程序代码,主要且容量最大的部分,基本单位是1K的页,C8T6只有64K
  信息块启动程序代码-系统存储器,存放原厂写入的Bootloader,用于串口下载。用户选择字节-存放独立的参数
  闪存存储器接口寄存器:普通外设,存储介质是SRAM,控制擦除和编程

在这里插入图片描述

二、FLASH工作原理

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

在这里插入图片描述

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是擦除后再写入,擦除后还容易丢数据,可以在RSAM里新建一个数组,FLASH的数据复制到SRAM,实现掉电不丢失,往FLASH里写入SRAM里的数据,实现参数的任意读写和保存
  首次上电,FLASH数据复制给SRAM数组时,需要判断第一个数据-标志位(任意设置),之后断电上电或者复位时,存在FLASH的数据(不丢失)继续复制给SRAM
  下面为Store.c

uint16_t Store_Data[512];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);}}//设置闪存最后一页第一个半字为A5A5,剩下全部为0for(uint16_t i = 0;i<512;i++){Store_Data[i] = MyFLASH_ReadHalfWord(0x0800FC00+i*2);}//上电时保证数据不丢失,将FLASH数据复制到SRAM数组中
}
/*
@brief:往闪存里面写数据(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){}
}

相关文章:

  • LC15.三数之和、LC22括号生成
  • OpenCV--滤波器(一)
  • Redis缓存的一些概念性问题
  • Milvus跨集群数据迁移
  • MySQL 保姆级教程(八):创建计算字段
  • 【Ubuntu通用压力测试】Ubuntu16.04 CPU压力测试
  • 传统后端SQL数据层替代解决方案: 内置数据源+JdbcTemplate+H2数据库 详解
  • YOLOv10改进 | Conv篇 |YOLOv10引入SPD-Conv卷积
  • 【前端技巧】css篇
  • React.ReactElement 与 React.ReactNode
  • Effective C++ 改善程序与设计的55个具体做法笔记与心得 3
  • SonarQube集成Jenkins平台搭建
  • 【Python】一文向您详细解析内置装饰器 @lru_cache
  • 【Android面试八股文】Kotlin内置标准函数let的原理是什么?
  • 初识C++ · 继承(1)
  • JS笔记四:作用域、变量(函数)提升
  • k个最大的数及变种小结
  • Object.assign方法不能实现深复制
  • TypeScript实现数据结构(一)栈,队列,链表
  • v-if和v-for连用出现的问题
  • Vultr 教程目录
  • 当SetTimeout遇到了字符串
  • 订阅Forge Viewer所有的事件
  • 浏览器缓存机制分析
  • 你真的知道 == 和 equals 的区别吗?
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 实现简单的正则表达式引擎
  • 使用putty远程连接linux
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • # 数据结构
  • #QT(串口助手-界面)
  • #前后端分离# 头条发布系统
  • (07)Hive——窗口函数详解
  • (AngularJS)Angular 控制器之间通信初探
  • (Windows环境)FFMPEG编译,包含编译x264以及x265
  • (八)Flink Join 连接
  • (二)换源+apt-get基础配置+搜狗拼音
  • (力扣)循环队列的实现与详解(C语言)
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)
  • .htaccess配置重写url引擎
  • .Net 8.0 新的变化
  • .NET Core WebAPI中封装Swagger配置
  • .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)
  • .Net6支持的操作系统版本(.net8已来,你还在用.netframework4.5吗)
  • @EnableWebMvc介绍和使用详细demo
  • @KafkaListener注解详解(一)| 常用参数详解
  • @value 静态变量_Python彻底搞懂:变量、对象、赋值、引用、拷贝
  • [ NOI 2001 ] 食物链
  • [].shift.call( arguments ) 和 [].slice.call( arguments )
  • [Android] Implementation vs API dependency
  • [android] 手机卫士黑名单功能(ListView优化)