STM32笔记 Flash

这篇具有很好参考价值的文章主要介绍了STM32笔记 Flash。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

笔记来源于江科协议的视频

芯片采用与stm32F103C8T6

简介

•STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程,系统存储器里面存储的是BootLoader,是不允许修改的。

•读写FLASH的用途:

利用程序存储器的剩余空间来保存掉电不丢失的用户数据,一般是找最后一部分存储数据,以防覆盖程序本身代码。

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

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

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

FLASH

模块组织

STM32笔记 Flash,stm32,笔记,嵌入式硬件

这里分为三块,第一个是主存储器(程序存储器),用来存放程序代码的,第二个是信息块,里面分为启动程序代码和用户选择字节,启动程序代码就是刚才的系统存储器,存放的是原厂写入的BootLoader,用于串口下载,用户选择字节也就是选项字节,存放一些独立的参数。第三块是闪存存储器接口寄存器,这一块的存储器实际上并不是闪存,其实本质上是一个普通的外设,存储介质是SRAM,就是上面的闪存管理员,这些寄存器用来控制擦除和编程的过程,前两块是真正的闪存,第三块为闪存的管理员。

对于主存存储器,进行了分页,擦除和写保护都是以页为单位的,写入前必须擦除,擦除以最小单元进行。擦除后数据全为1,数据只能1写0,不能0写1,擦除和写入都需要等待忙,与W25Q64的特性是一致的,但是这里只有基本单位页,没有块和扇区,每页的大小都是1K,一共128页,容量为128K,对于C8T6来说,它只有64K,所以C8T6的页只有一半,0~63,总共64页,共64K,第一页的起始地址为0x0800 0000,之后就是一个字节一个地址,依次线性分配,规律:对低3位进行累加4,以400、800、C00结尾的就是起始地址,系统存储器的起始地址为0x1FFF F000,容量2K,选项字节起始地址为0x1FFF F800,容量16个字节,里面只有几个字节的配置参数。平时讲的32K、64K、128K,说的是主存储器容量,对信息块没有影响,都是固定的2K、16K。 最后就是一个闪存接口寄存器,里面包括键寄存器、SR状态寄存器、CR控制寄存器等待,外设的起始地址:0x4002 2000,每个寄存器4个字节,也就是32位。

逻辑框图

STM32笔记 Flash,stm32,笔记,嵌入式硬件

这里以C8T6为例,64K,一共64页,最后一页地址0x0800 FC00,左边是闪存存储器接口,也叫闪存编程和擦除控制器(FPEC),可以对程序存储器进行擦除和编程,也可以对选项字节进行擦除和编程,系统存储器只读,选项字节中很大一部分是配置主程序存储器的读写保护,右边黄色箭头的写入选项字节可以配置程序存储器的读写保护。

细节问题

Flash解锁

STM32笔记 Flash,stm32,笔记,嵌入式硬件

Flash操作之前需要解锁,目的是防止误操作,解锁方式是通过键寄存器写入指定的键值来实现,FPEC一共有三个键值,RDPRT解除读保护的密钥,值是0xA5,KEY1键是0x45670123,KEY2键是0xCDEF89AB。

解锁:复位后,FPEC被保护,不能写入FLASH_CR,复位后默认Flash是锁着的,在FLASH_KEYR先写入KEY1,再写入KEY2,解锁。错误的操作序列会在下次复位前锁死FPEC和FLASH_CR,一旦没有先写入KEY1再写入KEY2,整个模块就会完全锁死,除非复位。

•加锁:设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR

指针访问寄存器

STM32笔记 Flash,stm32,笔记,嵌入式硬件

*((IO uint16_t *)(0x08000000)):这个变量为地址,这里强制类型转换为uint16_t的指针类型,指针指向这个地址,最后一个✳号指针取内容,把这个指针指向的存储器取出来,值就是指定存储器的值,对于闪存是不需要解锁的,不对其进行更改。

*((IO uint16_t *)(0x08000000)) = 0x1234,指定地址写,先给定地址,再强转为指针,最后取内容,然后对其赋值,这里写数据时,要先对Flash进行解锁,还要套入写入的流程。如果这里写的是SRAM地址,如:0x2000 0000那么就不需要解锁,可以直接写入,因为SRAM在程序运行时是可读可写的,

这里——IO是一个宏定义,对应C语言的关键字volatile,意思是易变的数据,这里加上是一个保障措施,防止编译器优化,keil编译器默认优化是最低优化等级,如果要提高编译优先等级,这里就有问题了。编译器优化是为了去除无用的繁杂代码,降低代码空间,提升运行效率。这里就是告诉编译器无论怎么优化,这里不能动。

编译器还会利用缓存来加速代码,如:要频繁读写内存的某个变量,最常见的优化方式:先把变量转移到高速缓存里面,在stm32内核中,有一个类似的缓存的工作组寄存器,这类寄存器访问速度最快,把变量放缓存里,需要读写时,直接访问缓存就可以了,用完之后,再写回内存。但是程序有多线程,如:中断函数,在中断函数里改变了原始变量,但是缓存并不知道中断更改了原始变量,下次程序还看缓存的变量,就会造成数据更改不同步的问题,这时,可以在读取变量定义的前面加上volatile,告诉编译器这个变量是易变的,要直接从内存中找,不用缓存优化。

如果开启了编译器优化,在无意义加减变量,多线程更改变量,读写与硬件相关的存储器时,都需要加上volatile,防止编译器优化。

存储器操作

全擦除

STM32笔记 Flash,stm32,笔记,嵌入式硬件

第一步:读取LOCK位,看下芯片锁了没,如果LOCK=1,那就是锁住了,就执行解锁过程,解锁过程在KEYR寄存器中,先写KEY1,再写KEY2。没锁就不用解锁了,之后置控制寄存器CR的MER位为1,然后再置STRT位为1,STRT的1的触发条件,触发之后,芯片开始工作,芯片检测到MER位=1,芯片就全擦除。擦除过程开始后,程序要执行等待,判断状态寄存器SR中的BSY位,BSY芯片是否处于忙状态,等于1表示忙,等于0不忙,等于1的时候会跳回去继续判断,直到BSY等于0,跳出循环。

页擦除

STM32笔记 Flash,stm32,笔记,嵌入式硬件

这里第一步仍是解锁的流程,第二步,置控制寄存器的PER位为1,在AR地址寄存器中选择要擦除的页,最后置控制寄存器CR的STRT的位为1,最后等待BSY位,

闪存写入

STM32笔记 Flash,stm32,笔记,嵌入式硬件

STM32的闪存会在写入之前检查有没有擦除,如果没有擦除就写入,STM32则不执行写入操作,除非写入的数据全是0。 写入第一步:解锁, 第二步:需要置控制寄存器CR中的PG位为1,表示要写入数据,第三步:在指定的地址写入半字,写入操作只能以半字的形式写入, 第四步:等待忙

选项字节

STM32笔记 Flash,stm32,笔记,嵌入式硬件

这里n的意思 如:写入RDP数据时,要同时写入nRDP数据的反码,这样写入数据才是有效的,如果这两个寄存器不是互为反码,则写入的数据无效。这里硬件会自动写入。

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

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

•Data0/1:用户可自定义使用

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

选项字节编程

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

•解锁FLASH_CR的OPTWRE位

•设置FLASH_CR的OPTPG位为1

•写入要编程的半字到指定的地址

•等待BSY位变为0

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

选项字节擦除

•检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作(相当于事前等待)

•解锁FLASH_CR的OPTWRE位,相当于选项字节的解锁,也是先写入KEY1,再写入KEY2

•设置FLASH_CR的OPTER位为1

•设置FLASH_CR的STRT位为1

•等待BSY位变为0

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

器件电子签名

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

•闪存容量寄存器:

基地址:0x1FFF F7E0

大小:16位

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

基地址: 0x1FFF F7E8

大小:96位

代码

读写Flash

MyFlash.c

#include "stm32f10x.h"                  // Device header

//读一个32位的字
uint32_t MyFlash_ReadWord(uint32_t Addr)
{
	return *((__IO uint32_t*)(Addr));		//使用指针访问指定地址下的数据并返回
}

//读一个32位的字
uint16_t MyFlash_ReadHalfWord(uint32_t Addr)
{
	return *((__IO uint16_t*)(Addr));		//使用指针访问指定地址下的数据并返回
}

//读一个32位的字
uint8_t MyFlash_ReadByte(uint32_t Addr)
{
	return *((__IO uint8_t*)(Addr));		//使用指针访问指定地址下的数据并返回
}


//全擦除
void MyFlash_EraserAllPage(void)
{
	FLASH_Unlock();		//开锁
	FLASH_EraseAllPages();			//全擦除
	FLASH_Lock();		//关锁
}

void MyFlash_EraserPage(uint32_t Addr)
{
	FLASH_Unlock();		//开锁
	FLASH_ErasePage(Addr);		//写入页擦除
	FLASH_Lock();		//关锁
}

//写入字
void MyFlash_ProgramWord(uint32_t Addr, uint32_t data)
{
	FLASH_Unlock();		//开锁
	FLASH_ProgramWord(Addr,data);			//写入字
	FLASH_Lock();		//关锁
}

//写入半字
void MyFlash_ProgramHalfWord(uint32_t Addr, uint32_t data)
{
	FLASH_Unlock();		//开锁
	FLASH_ProgramHalfWord(Addr,data);			//写入半字
	FLASH_Lock();		//关锁
}

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_EraserPage(STORE_START_ADDRESS);			//擦除
		MyFlash_ProgramHalfWord(STORE_START_ADDRESS,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++)
	{
		Store_Data[i] = MyFlash_ReadHalfWord(STORE_START_ADDRESS+i*2);
	}
}

//保存数据   存到闪存里面
void Store_Save(void)
{
	MyFlash_EraserPage(STORE_START_ADDRESS);			//擦除
	for(uint16_t i=0;i<STORE_COUNT;i++)
	{
		MyFlash_ProgramHalfWord(STORE_START_ADDRESS+i*2,Store_Data[i]);		//除了标志位全部清0
	}
}

//将模块数据清零
void Store_Clear(void)
{
	for(uint16_t i=1;i<STORE_COUNT;i++)
	{
			Store_Data[i] = 0x0000;
	}
	Store_Save();
}

main.c文章来源地址https://www.toymoban.com/news/detail-800136.html

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

uint8_t Key;

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	Key_Init();
	Store_Init();

	OLED_ShowString(1,1,"Flag:");
	OLED_ShowString(2,1,"Data:");
	
	while (1)
	{
		Key = Key_GetNum();
		if(Key == 1)
		{
			Store_Data[1]++;
			Store_Data[2]++;
			Store_Data[3]++;
			Store_Data[4]++;
			Store_Save();
		}
		
		if(Key == 2)
		{
			Store_Clear();
		}
		OLED_ShowHexNum(1,6,Store_Data[0],4);
		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);
		
	}
}

读ID

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

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	
	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)
	{
		
	}
}

到了这里,关于STM32笔记 Flash的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 嵌入式系统开发笔记104:在STM32CubeIDE中导入工程

      本文讲述如何在STM32CubeIDE中导入现有工程。

    2024年02月16日
    浏览(52)
  • 嵌入式学习笔记——STM32的USART收发字符串及串口中断

    上一篇中,介绍了串口收发相关的寄存器,通过代码实现了一个字节的收发,本文接着上面的内容,通过功能函数实现字符串的收发,然后引入中断解决收发过程中while()死等的问题。 根据昨天的字符发送函数,只需要稍作修改即可实现发送函数了,一个字符串的结尾会有一

    2024年02月03日
    浏览(76)
  • 嵌入式学习笔记——STM32的USART相关寄存器介绍及其配置

    上一篇中,对串口做了个概述,主要是介绍了串口通信的特征,异步串行全双工通信,然后就是结合串口的框图梳理了一下STM32中USART的配置流程以及发送接收数据的流程,本文将接着上篇的内容,对串口的寄存器做个介绍,然后实现一个简单的收发实验。 根据之前GPIO的经验

    2024年02月05日
    浏览(54)
  • 蓝桥杯嵌入式CT117E-M4学习笔记02-STM32G431RBT6芯片学习

    首先学习了解一下蓝桥杯嵌入式CT117E-M4开发板的主控芯片STM32G431RBT6,本文仅为个人学习成果总结,如有错误,恳请指正。 上图为STM32CubeMX选型界面,如图可以看出STM32G431RBT6具有以下特点和硬件集成。 采用Cortex-M4 32位RISC核心架构,工作频率最高可达170Mhz。 128kBytes的FLASH,32

    2023年04月09日
    浏览(59)
  • stm32嵌入式实验考核

    STM32 实验考核题目 1. 利用 STM32 小板实现:控制外接 LED 灯每隔 3 秒钟亮暗变换,同 时在 PC 机上显示 MCU 的计时时间,MCU 的初始时间由 PC 机 方设置。 2. 利用 STM32 小板实现:利用导线外接 GPIO 口模拟 2 个按键输入, 根据输入组合的四种情况,分别控制三色灯四种流水灯效果

    2024年02月03日
    浏览(49)
  • STM32的时钟系统(嵌入式学习)

    时钟是指用于计量和同步时间的装置或系统。时钟是嵌入式系统的脉搏,处理器内核在时钟驱动下完成指令执行,状态变换等动作,外设部件在时钟的驱动下完成各种工作,例如:串口数据的发送、AD转换、定时器计数等。因此时钟对于计算机系统是至关重要的,通常时钟系

    2024年02月16日
    浏览(47)
  • 嵌入式 STM32 通讯协议--MODBUS

    目录 一、自定义通信协议 1、协议介绍 2、网络协议 3、自定义的通信协议  二、MODBUS通信协议 1、概述 2、MODBUS帧结构  协议描述 3、MODBUS数据模型   4、MODBUS事务处理的定义 5、MODBUS功能码  6、功能码定义   7、MODBUS数据链路层 8、MODBUS地址规则  9、MODBUS帧描述 10、MODBUS两种

    2024年02月11日
    浏览(62)
  • 嵌入式——新建STM32工程(标准库)

    目录 一、初识标准库 1.CMSIS标准及库层级关系 2.库文件介绍 (1)Libraries文件夹 ①CMSIS文件夹 ②STM32F10x_Std_Periph_Driver文件夹 ③ 在用库建立一个完整的工程时,还需要添加stm32f10x_it.c、 stm32f10x_conf.h 和 system_stm32f10x.c文件 (2)Project文件夹 (3)Utilities文件夹 3.库各文件之间的关

    2024年01月23日
    浏览(53)
  • STM32串口通信详解(嵌入式学习)

    时钟信号在电子领域中是指用于同步和定时电路操作的周期性信号。它在数字系统和通信系统中起着至关重要的作用,用于协调各个组件之间的数据传输和操作。 时钟信号有以下几个重要的方面: 频率:时钟信号的频率是指单位时间内信号周期的数量。它通常以赫兹(Hz)为

    2024年02月09日
    浏览(67)
  • 嵌入式C语言基础(STM32)

    前言:一条混迹嵌入式3年的老咸鱼,想到自己第一次接触到stm32的库函数时,c语言稀碎,痛不欲生的场景,该文章为萌新指条明路。 位操作在嵌入式中常用于直接对芯片的寄存器进行操作,当时作为初学者的我看着一脸懵逼,至于为什么这样修改,下面好好分析一下。  一

    2024年02月02日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包