FLASH闪存

news2024/11/27 6:42:29

一、FLASH简介

1、FLASH简介

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

(2)读写FLASH的用途:     

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

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

(3)在线编程(In-Circuit Programming – ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议系统加载程序(Bootloader)下载程序

(4)在程序中编程(In-Application Programming – IAP)可以使用微控制器支持的任一种通信接口下载程序

2、闪存模块组织

3、FLASH基本结构

4、FLASH解锁

(1)FPEC共有三个键值:     

                RDPRT键 = 0x000000A5(解除读保护的秘钥)

                KEY1 = 0x45670123     

                KEY2 = 0xCDEF89AB

(2)解锁:     

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

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

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

(3)加锁:     

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

5、使用指针访问存储器

(1)使用指针读指定地址下的存储器:     

                uint16_t Data = *((__IO uint16_t *)(0x08000000));

(2)使用指针指定地址下的存储器:     

                *((__IO uint16_t *)(0x08000000)) = 0x1234;

(3)其中:     

                #define    __IO    volatile (易变的数据,防止编译器优化)

6、程序存储器

(1)编程

(2)页擦除

(3)全擦除

7、选项字节

(1)选项字节

        a.RDP:写入RDPRT键(0x000000A5)后解除读保护

        b.USER:配置硬件看门狗和进入停机/待机模式是否产生复位

        c.Data0/1:用户可自定义使用

        d.WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)

(2)选项字节编程

        a.检查FLASH_SR的BSY位,以确认没有其他正在进行的编程操作

        b.解锁FLASH_CR的OPTWRE位

        c.设置FLASH_CR的OPTPG位为1(即将写入选项字节)

        d.写入要编程的半字到指定的地址(指针写入操作)

        e.等待BSY位变为0

        f.读出写入的地址并验证数据

(3)选项字节擦除

        a.检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作

        b.解锁FLASH_CR的OPTWRE位(选项字节里面有一个单独的解锁)

        c.设置FLASH_CR的OPTER位为1(即将擦除选项字节)

        d.设置FLASH_CR的STRT位为1(触发芯片,开始干活)

        e.等待BSY位变为0

        f.读出被擦除的选择字节并做验证

8、器件电子签名

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

(2)闪存容量寄存器:     

                基地址:0x1FFF F7E0     

                大小:16位

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

                基地址: 0x1FFF F7E8     

                大小:96位

二、读写内部FLASH

1、按照以下接线方式连接,并将STLINK插到电脑上

2、FLASH函数驱动模块

(1)FLASH库函数的功能

(2)最底层:MyFLASH层

MyFLASH.c

#include "stm32f10x.h"                  // Device header

/*
	读取32位的字
*/
uint32_t MyFLASH_ReadWord(uint32_t Address)//地址必须是32位的
{
	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_EraseAllPages();//全擦除
	FLASH_Lock();//锁上
}

/*
	页擦除
*/
void MyFLASH_ErasePage(uint32_t PageAddress)
{
	FLASH_Unlock();//解锁
	FLASH_ErasePage(PageAddress);//页擦除
	FLASH_Lock();//锁上
}
	
/*
	编程写入一个字
*/
void MyFLASH_ProgramWord(uint32_t Address,uint32_t Data)
{
	FLASH_Unlock();//解锁
	FLASH_ProgramWord(Address,Data);//指定地址写字
	FLASH_Lock();//锁上
}	

/*
	编程写入一个半字
*/
void MyFLASH_ProgramHalfWord(uint32_t Address,uint16_t Data)
{
	FLASH_Unlock();//解锁
	FLASH_ProgramHalfWord(Address,Data);//指定地址写半字
	FLASH_Lock();//锁上
}

MyFLASH.h

#ifndef __MYFLASH_H
#define __MYFLASH_H

uint32_t MyFLASH_ReadWord(uint32_t Address);
uint16_t MyFLASH_ReadHalfWord(uint32_t Address);
uint8_t MyFLASH_ReadByte(uint32_t Address);

void MyFLASH_EraseAllPages(void);
void MyFLASH_ErasePage(uint32_t PageAddress);

void MyFLASH_ProgramWord(uint32_t Address,uint32_t Data);
void MyFLASH_ProgramHalfWord(uint32_t Address,uint16_t Data);

#endif

(3)MyFLASH层之上

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];

void Store_Init(void)
{
	if(MyFLASH_ReadHalfWord(STORE_START_ADDRESS) !=0xA5A5)
	{
		MyFLASH_ErasePage(STORE_START_ADDRESS);//擦除最后一页
		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS,0xA5A5);//写入规定的标志位0xA5A5
		for(uint16_t i=1;i<STORE_COUNT;i++)
		{
			MyFLASH_ProgramHalfWord(STORE_START_ADDRESS +i*2,0x0000);//写0
		}
	}
	
	for(uint16_t i=0;i<STORE_COUNT;i++)//在上电的时候,把闪存备份的地址的数据,恢复到SRAM数组里
	{
		Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS +i*2);//读闪存数据并存到SRAM数组里
	}

}

/*
	把SRAM的所有内容备份到闪存
*/
void Store_Save(void)
{
	MyFLASH_ErasePage(STORE_START_ADDRESS);//擦除最后一页
	for(uint16_t i=0;i<STORE_COUNT;i++)//在上电的时候,把闪存备份的地址的数据,恢复到SRAM数组里
	{
		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS +i*2,Store_Data[i]);//把SRAM的所有内容备份到闪存的最后一页
	}
}	

/*
	把SRAM的所有内容备份到闪存
*/
void Store_Clear(void)
{
	for(uint16_t i=1;i<STORE_COUNT;i++)
	{
		Store_Data[i] = 0x0000;
	}
	Store_Save();//把这个更改更新到闪存里
}

Store.h

#ifndef __STORE_H
#define __STORE_H

extern uint16_t Store_Data[];

void Store_Init(void);
void Store_Save(void);
void Store_Clear(void);


#endif

3、main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Store.h"
#include "button.h"

uint8_t KeyNum;

int main(void)
{
	OLED_Init();
	Button_Init();
	Store_Init();//第一次使用时,初始化闪存,把闪存备份的数据加载回SRAM数组,实现SRAM数组上电不丢失
	
	OLED_ShowString(1,1,"Flag:");
	OLED_ShowString(2,1,"Data:");

	while(1)
	{
		KeyNum = Button_GetNum();
		
		if(KeyNum ==1)
		{
			Store_Data[1] ++;
			Store_Data[2] +=2;
			Store_Data[3] +=3;
			Store_Data[4] +=4;
			Store_Save();
		}
		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);
	}
}

4、实现效果

读写内部FLASH

5、存在问题

(1)目前闪存的前面部分存储的是程序文件,最后一页存储的是用户数据,如果程序较大,触及到了最后一页,那程序和用户数据存储的位置就冲突了,这时就可以给程序文件限定一个存储范围,不让它分配到后面我们用户数据的空间来

(2)配置下载选项,擦除扇区,用到多少就擦除多少,下载速度更快

(3)想知道目前程序编译后占多大空间

全部编译一下

或者双击Target1查看.map文件

三、读取芯片ID

1、main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"


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)
	{
		
	}
}

2、实现效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1848772.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

postman国内外竞争者及使用详解分析

一、postman简介 Postman 是一款广泛使用的 API 开发和测试工具&#xff0c;适用于开发人员和测试人员。它提供了一个直观的界面&#xff0c;用于发送 HTTP 请求、查看响应、创建和管理 API 测试用例&#xff0c;以及自动化 API 测试工作流程。以下是 Postman 的主要功能和特点…

ShareX,屏幕截图、屏幕录制和文件共享,还提供了丰富的高级功能和自定义选项

ShareX是一个免费开源的Windows应用程序&#xff0c;用于屏幕截图、屏幕录制和文件共享。它不仅支持基本的屏幕截图功能&#xff0c;还提供了丰富的高级功能和自定义选项&#xff0c;使其成为提高工作效率和截图体验的利器。以下是ShareX v16.1.0便携版的主要功能和特色&#x…

蓝桥杯 经典算法题 求解完全背包问题

题目&#xff1a; 题解&#xff1a; 和01背包基本完全一样。小局部最优的策略也是一样&#xff1a;是否选当前局部的最后一项。唯一的不同点在于物品是无线的导致在表示选择当前物品的状态写法发生了改变&#xff1a;由dp[i-1][j-w[i]]变为了dp[i][j-w[i]]因为这样能够表示最后…

Java 10新特性介绍

Java 10是Java平台的一个重要更新&#xff0c;它引入了多项新特性和改进。以下是一些主要的新特性&#xff1a; 局部变量类型推断&#xff08;var关键字&#xff09; Java 10允许使用var关键字来声明局部变量&#xff0c;而无需显式指定变量的类型。编译器会根据变量赋值的上下…

探索产业园的独特产业定位与价值

数字影像产业园的产业定位独特且全面&#xff0c;涵盖了数字贸易、数字服务、数字文旅和数字基建四大主导产业方向&#xff0c;体现了园区在数字化转型和产业升级方面的前瞻性和创新性。 一、数字贸易的推动者 数字影像产业园致力于推动数字贸易的发展&#xff0c;搭建全球化、…

(深度学习记录)第TR5周:Transformer中的位置编码详解

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 &#x1f3e1;我的环境&#xff1a; 语言环境&#xff1a;Python3.11.4编译器&#xff1a;Jupyter Notebooktorcch版本&#xff1a;2.0.…

AI播客下载:Machine Learning Street Talk(AI机器学习)

该频道由 Tim Scarfe 博士、Yannic Kilcher 博士和 Keith Duggar 博士管理。 他们做了出色的工作&#xff0c;对每个节目进行了彻底的研究&#xff0c;并与机器学习行业中一些受过最高教育、最全面的嘉宾进行了双向对话。 每一集都会教授一些新内容&#xff0c;并且提供未经过滤…

绽放光彩的小程序 UI 风格

绽放光彩的小程序 UI 风格

怎么处理整合了shiro的应用的RPC接口鉴权问题

这篇文章分享一下&#xff1a;当一个服务提供者整合了shiro安全框架来实现权限访问控制时&#xff0c;服务消费者通过feign请求服务提供者的接口时的鉴权不通过问题。 问题描述 博主有一个项目pms&#xff08;权限管理系统&#xff09;&#xff0c;使用了shiro框架来实现鉴权功…

【C语言】解决C语言报错:Stack Overflow

文章目录 简介什么是Stack OverflowStack Overflow的常见原因如何检测和调试Stack Overflow解决Stack Overflow的最佳实践详细实例解析示例1&#xff1a;递归调用过深示例2&#xff1a;分配过大的局部变量示例3&#xff1a;嵌套函数调用过多 进一步阅读和参考资料总结 简介 St…

Python轻松设置Excel单元格数字显示格式

Excel作为强大的数据处理与分析工具&#xff0c;不仅能够存储大量数据&#xff0c;还支持复杂的数据处理与可视化功能。而如何恰当地展示Excel表格中的数据是Excel文件制作的关键之一。这便涉及到Excel单元格数字格式的设置。数字格式不仅关乎数据的美学呈现&#xff0c;如货币…

开发中遇到的一个bug

遇到的报错信息是这样的&#xff1a; java: Annotation processing is not supported for module cycles. Please ensure that all modules from cycle [hm-api,hm-common,hm-service] are excluded from annotation processing 翻译过来就是存在循环引用的情况&#xff0c;导…

Golang | Leetcode Golang题解之第172题阶乘后的零

题目&#xff1a; 题解&#xff1a; func trailingZeroes(n int) (ans int) {for n > 0 {n / 5ans n}return }

MySQL的DDL语句

文章目录 ☃️概述☃️DDL&#xff08;数据定义语言&#xff09;☃️数据库操作☃️表操作☃️DDL的重要性 ☃️概述 MySQL 通用语法分类 ● DDL: 数据定义语言&#xff0c;用来 定义数据库对象&#xff08;数据库、表、字段&#xff09; ● DML: 数据操作语言&#xff0c;用…

天擎客户端卸载 自我保护异常

问题&#xff1a;客户端卸载失败提示“检测到自我保护状态异常&#xff0c;停止卸载” 下列操作&#xff0c;均在客户端进行&#xff0c;别改成服务端的了 进入天擎客户端主目录&#xff0c;默认路径为 C:\Program Files (x86)\Qianxin\Tianqing 将avsecbase.dll 重命名为 1…

JAVA NIO(二) Buffer和Channel

一&#xff0c;基本使用 1&#xff0c; 一个Socket连接使用一个Channel来表示&#xff0c;以前直接操作Socket文件描述符来对读写缓冲区操作&#xff0c;比如读数据到用户空间的一个byte数组&#xff0c;NIO中Channel对这个过程作了封装&#xff0c;其中用户空间的byte数组就类…

电商数据:准确性与覆盖率的关键意义

在当今的电商环境下&#xff0c;以电商数据为驱动&#xff0c;品牌能够开展诸多与渠道相关的工作&#xff0c;像是电商数据监测、数据分析以及低价治理等。然而&#xff0c;所有这些工作的首要前提便是数据的准确性必须达到较高水平。倘若准确性无法得到有效保障&#xff0c;那…

C语言 | Leetcode C语言题解之第172题阶乘后的零

题目&#xff1a; 题解&#xff1a; int trailingZeroes(int n) {int ans 0;while (n) {n / 5;ans n;}return ans; }

【STM32】时钟树系统

1.时钟树简介 1.1五个时钟源 LSI是低速内部时钟&#xff0c;RC振荡器&#xff0c;频率为32kHz左右。供独立看门狗和自动唤醒单元使用。 LSE是低速外部时钟&#xff0c;接频率为32.768kHz的石英晶体。这个主要是RTC的时钟源。 HSE是高速外部时钟&#xff0c;可接石英*/陶瓷谐振…

el-input-number 限制输入正整数

vue 页面 限制输入最小值为0 :min"0" <el-input-number v-model"scope.row.num" change"handleNumChange(scope)" keydown.enter.prevent style"width: 200px; " :min"0" />methods 里面限制输入的数字不为小数 使…