STM32入门教程(FLASH闪存篇)

重要的内容写在前面:

  1. 该系列是以up主江协科技的STM32视频教程为基础写下去的,大部分内容都参考了老师的课件,对于一些个人认为比较重要但是老师仅口述的部分,笔者都有用文字的方式记录并标出了重点。
  2. 文中的图片基本都来源于老师的课件以及开发板和芯片的手册,粘贴过来是为了方便阅读。
  3. 如果有条件的可以先学习一些相关课程再去看STM32的教程,学起来会更加轻松(不太建议零基础开始直接STM32,听起来可能会有点困难,可以先学51单片机),相关课程有数字电路(强烈推荐先学数电,不然可能会有很多地方理解起来很困难)、模拟电路、计算机组成原理(像寄存器、存储器、中断等在这门课里有很详细的介绍)、计算机网络等。
  4. 如有错漏欢迎指出。

视频链接:[15-1] FLASH闪存_哔哩哔哩_bilibili

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

(1)读写FLASH的用途:

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

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

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

(3)在程序中编程(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));  //读地址0x08000000下的2个字节(16位,半字)

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

*((__IO uint16_t *)(0x08000000)) = 0x1234;  //写2个字节(16位,半字)的数据0x1234到地址0x08000000

其中:

#define    __IO    volatile  //volatile在逻辑上没有什么作用,只是为了防止编译器优化

6、程序存储器编程:

(1)首先读取控制寄存器的LOCK位,如果LOCK=1,那就需要解锁(在KEYR寄存器先写入KEY1再写入KEY2)才能继续操作,如果LOCK=0,可以往下进行编程(写入)操作。

(2)置CR寄存器的PG位为1,开始编程操作,不过STM32的闪存会在写入数据之前会检查指定地址有没有被擦除,如果未被擦除,STM32不执行写入操作。(另外每次写入都必须一次性写半字,也就是16位

(3)编程过程需要一定时间,在这段时间里程序等待CR寄存器的BSY位被置为0,也就是等待编程操作完成。

(4)“读出并验证所有页的数据”这一步是测试程序的步骤,一般程序不执行这一步。

7、程序存储器页擦除:(被擦除部分全部置为1

(1)首先读取控制寄存器的LOCK位,如果LOCK=1,那就需要解锁(在KEYR寄存器先写入KEY1再写入KEY2)才能继续操作,如果LOCK=0,可以往下进行擦除操作。

(2)首先置CR寄存器的PER位为1,在AR寄存器中写入要擦除的页,再置STRT位为1,其中置STRT为1是触发条件,触发芯片开始工作,而PER=1则决定芯片需要进行页擦除操作。

(3)擦除过程需要一定时间,在这段时间里程序等待CR寄存器的BSY位被置为0,也就是等待擦除操作完成。

(4)“读出并验证所有页的数据”这一步是测试程序的步骤,一般程序不执行这一步。

8、程序存储器全擦除:

(1)首先读取控制寄存器的LOCK位,如果LOCK=1,那就需要解锁(在KEYR寄存器先写入KEY1再写入KEY2)才能继续操作,如果LOCK=0,可以往下进行擦除操作。

(2)首先置CR寄存器的MER位为1,置STRT位为1,其中置STRT为1是触发条件,触发芯片开始工作,而MER=1则决定芯片需要进行全擦除操作。

(3)擦除过程需要一定时间,在这段时间里程序等待CR寄存器的BSY位被置为0,也就是等待擦除操作完成。

(4)“读出并验证所有页的数据”这一步是测试程序的步骤,一般程序不执行这一步。

9、选项字节:(nXXX代表XXX的反码)

(1)信息块的组织结构:

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

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

③Data0/1:用户可自定义使用。

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

(2)选项字节编程:

①解锁闪存(解锁LOCK位),检查FLASH_SR的BSY位,以确认没有其他正在进行的编程操作。

②解锁FLASH_CR的OPTWRE位。

③设置FLASH_CR的OPTPG位为1。

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

⑤等待BSY位变为0。

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

(3)选项字节擦除:

①解锁闪存(解锁LOCK位),检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作。

②解锁FLASH_CR的OPTWRE位。

③设置FLASH_CR的OPTER位为1。

④设置FLASH_CR的STRT位为1。

⑤等待BSY位变为0。

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

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

(1)闪存容量寄存器:基地址为0x1FFF F7E0,大小为16位。

(2)产品唯一身份标识寄存器:基地址为0x1FFF F7E8,大小为96位。

11、读写内部FLASH:

(1)按照下图所示接好电路,并将OLED显示屏的项目文件夹复制一份作为模板使用。

(2)在stm32f10x_flash.h文件中有闪存模块相关的函数。

[1]FLASH_Unlock函数:解锁LOCK位。

[2]FLASH_Lock函数:加锁LOCK位。

[3]FLASH_ErasePage函数:闪存擦除某一页。

[4]FLASH_EraseAllPages函数:闪存擦除全部页。

[5]FLASH_EraseOptionBytes函数:擦除选项字节。

[6]FLASH_ProgramWord函数:往闪存指定地址写一个字(32位)。

[7]FLASH_ProgramHalfWord函数:往闪存指定地址写半个字(16位)。

[8]FLASH_ITConfig函数:开启中断。

[9]FLASH_GetFlagStatus函数:获取状态标志位。

[10]FLASH_ClearFlag函数:清除状态标志位。

[11]FLASH_GetStatus函数:获取当前状态。

[12]FLASH_WaitForLastOperation函数:等待上一次操作完成(等待BSY=0)。

(3)在项目的System组中添加MyFLASH.h文件和MyFLASH.c文件用于封装闪存模块的代码。

①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_ErasePages(uint32_t PageAddress);
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data);
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);

#endif

②MyFLASH.c文件:

#include "stm32f10x.h"                  // Device header

uint32_t MyFLASH_ReadWord(uint32_t Address)  //读取Address地址下的一个字
{
	return *((__IO uint32_t *)(Address));
}

uint16_t MyFLASH_ReadHalfWord(uint32_t Address)  //读取Address地址下的半个字
{
	return *((__IO uint16_t *)(Address));
}

uint8_t MyFLASH_ReadByte(uint32_t Address)  //读取Address地址下的一个字节
{
	return *((__IO uint8_t *)(Address));
}

void MyFLASH_EraseAllPages(void)  //擦除全部页
{
	FLASH_Unlock();  //解锁
	FLASH_EraseAllPages(); //擦除全部页
	FLASH_Lock();    //加锁
}

void MyFLASH_ErasePages(uint32_t PageAddress)  //擦除页起始地址PageAddress下的页
{
	FLASH_Unlock();  //解锁
	FLASH_ErasePage(PageAddress); //擦除指定页
	FLASH_Lock();    //加锁
}

void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)  //往Address地址写一个字Data
{
	FLASH_Unlock();  //解锁
	FLASH_ProgramWord(Address, Data); //往指定地址写一个字
	FLASH_Lock();    //加锁
}

void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)  //往Address地址写半个字Data
{
	FLASH_Unlock();  //解锁
	FLASH_ProgramHalfWord(Address, Data); //往指定地址写半个字
	FLASH_Lock();    //加锁
}

(4)在项目的System组中添加Store.h文件和Store.c文件用于封装业务层的代码。

①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

②Store.c文件:

#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"

#define STORE_START_ADDRESS  0x800FC00  //使用闪存0x800FC00的一页进行调试(只要程序占用空间不大,该页一般空闲)
#define STORE_COUNT          512        //一页有512个半字

uint16_t Store_Data[STORE_COUNT];       //存储在SRAM中的数组,用于暂存FLASH中的数据

void Store_Init(void)
{
	if(MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5)  //闪存的0x800FC00页只初始化一次即可
	{
		MyFLASH_ErasePages(STORE_START_ADDRESS);  //擦除0x800FC00页
		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);
		//0x800FC00页的第一个半字用来做标志,如果初始化过一次,数据掉电不丢失,重新上电后该半字仍为0xA5A5
		for(uint16_t i = 1; i < STORE_COUNT; i++)  //该页剩下的空间全部初始化为0
		{
			MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000);
		}
	}
	for(uint16_t i = 0; i < STORE_COUNT; i++)  //将0x800FC00页的数据拷贝到SARM的数组中,便于程序修改
	{
		Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2);
	}
}

void Store_Save(void)  //将SARM中的数据保存到闪存的0x800FC00页
{
	MyFLASH_ErasePages(STORE_START_ADDRESS);  //先擦除原来保存的数据
	for(uint16_t i = 0; i < STORE_COUNT; i++) //把SRAM中数组的数据逐半字写入FLASH
	{
		 MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]);
	}
}

void Store_Clear(void)  //清空0x800FC00页保存的数据(不是擦除)
{
	for(uint16_t i = 1; i < STORE_COUNT; i++)  //先把SARM中暂存的数据请空
	{
		Store_Data[i] = 0;
	}
	Store_Save();  //将SARM中的数据保存到闪存的0x800FC00页
}

(5)在main.c文件中粘贴以下代码,然后编译,将程序下载到开发板中进行调试。

#include "stm32f10x.h"                  // Device headerCmd
#include "OLED.h"
#include "Key.h"
#include "Store.h"

uint8_t KeyNum;

int main()
{
	OLED_Init();
	Key_Init();
	Store_Init();
	
	OLED_ShowString(1,1,"Flag:");
	OLED_ShowString(2,1,"Data:");
	
	while(1)
	{
		KeyNum = Key_GetNum();
		
		if(KeyNum == 1)  //按下按键1,修改闪存数据
		{
			Store_Data[1] += 1;
			Store_Data[2] += 2;
			Store_Data[3] += 3;
			Store_Data[4] += 4;
			Store_Save();       //保存数据
		}
		if(KeyNum == 2)  //按下按键2,清空指定页STORE_START_ADDRESS的数据
		{
			Store_Clear();      //清空数据
		}
		
		OLED_ShowHexNum(1,6,Store_Data[0],4);  //0xA5A5(标志)
		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);
	}
}

(6)使用STM32 ST-LINK Utility可以查看闪存中的数据存储情况,在程序下载到开发板中后,点击红框所在的按钮,然后把Address修改为0x0800FC00,就可以查看0x0800FC00这一页的存储内容。(如果想下载新程序,需要点击蓝框所在按钮,否则ST-LINK会被占用)

(7)程序最大占用空间可以设置,当程序所占空间超过设定值时程序将无法编译。

12、读取芯片ID:

(1)按照下图所示接好电路,并将OLED显示屏的项目文件夹复制一份作为模板使用。

(2)产品唯一身份标识寄存器的地址:

(3)在main.c文件中粘贴以下代码,然后编译,将程序下载到开发板中进行调试。

#include "stm32f10x.h"                  // Device header
#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)
	{
		
	}
}
  • 27
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: "微盘 STM32入门 基础系列教程" 是一套专门介绍STM32微控制器的入门教程。 该教程适用于想要学习STM32微控制器的初学者。教程从基础开始,逐步介绍了STM32的相关知识和技巧。学习者将会学习到如何设置STM32开发环境、如何编写STM32的程序、如何使用STM32的各种外设等。 教程重点讲解了STM32微控制器的基本概念和架构。学习者将了解到STM32微控制器的工作原理、寄存器的使用方法以及常见的外设功能和原理。教程使用简单易懂的语言和图表,帮助学习者快速上手、深入理解。 除了理论讲解,教程还提供了大量的实例和实践项目。学习者可以跟随教程一步步完成各种小项目,如LED灯的闪烁、按键的检测、温度传感器的读取等。这些实践项目有助于巩固学习成果,提高实际应用能力。 此外,教程还引导学习者使用STM32的开发工具和开发板进行实践。学习者可以利用教程提供的资源,搭建自己的学习环境。教程推荐了一些常用的STM32开发工具、开发板和配件,帮助学习者选择适合自己的设备。 总而言之,"微盘 STM32入门 基础系列教程" 是一套适合STM32初学者的入门教程。通过学习该教程,学习者可以快速了解STM32微控制器的基础知识和应用技巧,为后续更深入的学习和开发打下坚实的基础。 ### 回答2: 微盘 STM32入门基础系列教程是一套针对新手学习STM32单片机的教程资源。这套教程以微盘作为文件分享平台,提供给学习者们使用。 该教程系列通过系统化的方式,详细介绍了STM32单片机的基础知识和开发技术。从硬件介绍、开发环境搭建、程序编写,到常用外设的控制和实践案例,都有详细的讲解和实例演示。 首先,教程提供了STM32硬件平台的概述和介绍,包括STM32系列的特点、主要硬件资源和引脚布局等,帮助学习者对STM32有一个整体认识。 其次,教程详细介绍了如何搭建STM32开发环境,包括下载安装Keil MDK集成开发环境、建立工程和编译程序等操作步骤,使学习者能够顺利开始进行STM32的开发。 然后,教程通过逐步讲解LED灯控制、按键输入、数码管显示等基本外设的使用方法,以及ADC、PWM等常用模块的配置和应用案例,帮助学习者掌握STM32的基础开发技巧。 最后,教程还提供了一些实际项目实践,如温度监测系统、蓝牙控制LED灯等,让学习者将所学的知识应用到实际中,进一步巩固和提高自己的能力。 总之,微盘 STM32入门基础系列教程通过提供完整的学习资料、清晰的讲解和实践案例,为初学者提供了一个系统学习STM32的平台,帮助他们快速入门并掌握STM32单片机的开发技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevalin爱灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值