STM32单片机入门学习——第49节: [15-2] 读写内部FLASH&读取芯片ID

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!
本文写于:2025.04.29

STM32开发板学习------第49节: [15-2] 读写内部FLASH&读取芯片ID

前言

本次笔记是用来记录我的学习过程,同时把我需要的困难和思考记下来,有助于我的学习,同时也作为一种习惯,可以督促我学习,是一个激励自己的过程,让我们开始32单片机的学习之路。

欢迎大家给我提意见,能给我的嵌入式之旅提供方向和路线,现在作为小白,我就先学习32单片机了,就跟着B站上的江协科技开始学习了.

在这里会记录下江协科技32单片机开发板的配套视频教程所作的实验和学习笔记内容,因为我之前有一个开发板,我大概率会用我的板子模仿着来做.让我们一起加油!

另外为了增强我的学习效果:每次笔记把我不知道或者问题在后面提出来,再下一篇开头作为解答!

开发板说明

本人采用的是慧净的开发板,因为这个板子是我N年前就买的板子,索性就拿来用了。另外我也购买了江科大的学习套间。

原理图如下

1、开发板原理图

2、STM32F103C6和51对比

3、STM32F103C6核心板

视频中的都用这个开发板来实现,如果有资源就利用起来。另外也计划实现江协科技的套件。

下图是实物图

引用

【STM32入门教程-2023版 细致讲解 中文字幕】

还参考了下图中的书籍:

STM32库开发实战指南:基于STM32F103(第2版)

数据手册

解答和科普

一、FLASH 硬件

工程结构:建立两个底层模块,最底层计划叫MyFLASH,在这里实现闪存最基本的3个功能,也就是读取、擦除和编程,之后,在此模块之上,在建一个模块,叫Store,主要实现参数数据的读写和存储管理,因为最终应用层想要实现的功能是,任意读写参数,并且这些参数是掉电不丢失的,至于你存在什么地方,怎么擦除,采用什么读写策略,这些不是应用层所关心的,所以Store会定义一个SRAM数组,需要掉电不丢失的参数,就写到SRAM数组里,之后调用保持的函数,这个SRAM数组就自动备份到闪存里,上电后,Store初始化, 会自动再把闪存里的数据读回SRAM数组里,这时闪存管理策略。

最后是main.c里面的应用层了,想要保持参数,就写到Store层的数组,再调用保持函数,备份到闪存,这样就能实现最终功能了。

辅助软件:stlink ultility,可以该数据,底层也是有擦除和写

选项字节,

Bank是加大容量

c 复制代码
void FLASH_Unlock(void);
void FLASH_Unlock(void)
{
  /* Authorize the FPEC of Bank1 Access */
  FLASH->KEYR = FLASH_KEY1;
  FLASH->KEYR = FLASH_KEY2;

#ifdef STM32F10X_XL
  /* Authorize the FPEC of Bank2 Access */
  FLASH->KEYR2 = FLASH_KEY1;
  FLASH->KEYR2 = FLASH_KEY2;
#endif /* STM32F10X_XL */
}
先写入Key1,再写入Key2.
void FLASH_Lock(void);
void FLASH_Lock(void)
{
  /* Set the Lock Bit to lock the FPEC and the CR of  Bank1 */
  FLASH->CR |= CR_LOCK_Set;

#ifdef STM32F10X_XL
  /* Set the Lock Bit to lock the FPEC and the CR of  Bank2 */
  FLASH->CR2 |= CR_LOCK_Set;
#endif /* STM32F10X_XL */
}
加锁对应设置FLASH_CR的LOCK位锁住FPEC和FLASH_CR;非常的直观。

对主闪存和选项字节进行擦除和编程的函数了。
FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
闪存擦除某一页;返回值高速状态;
FLASH_Status FLASH_EraseAllPages(void); 
全擦除
FLASH_Status FLASH_EraseOptionBytes(void);
擦除选项字节
status = FLASH_WaitForLastOperation(EraseTimeout);
  if(status == FLASH_COMPLETE)
  {
    /* Authorize the small information block programming */
    FLASH->OPTKEYR = FLASH_KEY1;
    FLASH->OPTKEYR = FLASH_KEY2;
    
    /* if the previous operation is completed, proceed to erase the option bytes */
    FLASH->CR |= CR_OPTER_Set;
    FLASH->CR |= CR_STRT_Set;
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(EraseTimeout);
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
写入字
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
写入半字
/* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(ProgramTimeout);
  
  if(status == FLASH_COMPLETE)
  {
    /* if the previous operation is completed, proceed to program the new data */
    FLASH->CR |= CR_PG_Set;
  
    *(__IO uint16_t*)Address = Data;
    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(ProgramTimeout);
    
    /* Disable the PG Bit */
    FLASH->CR &= CR_PG_Reset;
  } 
#endif  /* STM32F10X_XL */

第一步,事前等待,第二步,PG位置1,第三步,Address强转为指针,再指针取内容,赋值Data,也就是在指定地址Address下,写入指定数据Data;第四步,事后等待,最后PG位恢复为0;

FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);

FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages);

FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState);

FLASH_Status FLASH_UserOptionByteConfig(uint16_t OB_IWDG, uint16_t OB_STOP, uint16_t OB_STDBY);

FlagStatus FLASH_GetPrefetchBufferStatus(void);

获取预取缓存期状态

FLASH_Status FLASH_GetStatus(void);

FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);

1、MyFLSAH

c 复制代码
#include "stm32f10x.h"                  // Device header

uint32_t  MyFLASH_ReadWord(uint32_t Address)
{
	return  *((__IO uint32_t *)(Address)) ;
	
}

uint16_t  MyFLASH_ReadHalfWord(uint32_t Address)
{
	return  *((__IO uint16_t *)(Address)) ;
	
}

uint8_t  MyFLASH_ReadByte(uint32_t Address)
{
	return  *((__IO uint8_t *)(Address)) ;
	
}

void MyFLASH_EraseAllPages(void)
{
	FLASH_Unlock();
	FLASH_EraseAllPages();
	FLASH_Lock();
}

void MyFLASH_ErasePages(uint32_t Address)
{
	FLASH_Unlock();
	FLASH_ErasePage(Address);
	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();
}
c 复制代码
#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 Address);

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

2、Store

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"


#define STORE_START_ADDRESS   0x0800FC00
#define STORE_COUNT				512

uint16_t  Store_Data[512];

void Store_Init(void)
{
	if ( MyFLASH_ReadHalfWord(0x0800FC00)!= 0xA5A5 )
	{
		MyFLASH_ErasePages(0x0800FC00);
		MyFLASH_ProgramHalfWord(0x0800FC00,0xA5A5);
		for(uint16_t i=1;i<512;i++ )
		{
		MyFLASH_ProgramHalfWord(0x0800FC00+ i*2 ,0x0000);
		}
	}
	
	for(uint16_t i=0;i<512;i++ )
		{
			Store_Data[i]=MyFLASH_ReadHalfWord(0x0800FC00+ i*2 );
		}
}

void Store_Save(void)
{
	MyFLASH_ErasePages(0x0800FC00);
	for(uint16_t i=0;i<512;i++ )
		{
			MyFLASH_ProgramHalfWord(0x0800FC00+ i*2 ,Store_Data[i]);
		}
	
}

void Store_Clear(void)
{
	for(uint16_t i=1;i<512;i++ )
		{
			Store_Data[i]=0x0000;
		}
	Store_Save();

}
c 复制代码
#ifndef    __STORE_H
#define    __STORR_H

extern uint16_t  Store_Data[];

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

#endif

所以这个是以小端模式读取的

c 复制代码
void MyFLASH_EraseAllPages(void)
{
	FLASH_Unlock();
	FLASH_EraseAllPages();
	FLASH_Lock();
} 

页擦除


首先,把闪存初始化一下,比如你第一次使用这个代码,那闪存默认全是FF,而参数和SRAM,一般都默认0,所以对于第一次使用,我们要给闪存的各个参数都置0,怎么判断是不是第一次使用,定义一个标志位,把闪存最后一页的第一个半字,当作标志位,判断。

c 复制代码
if ( MyFLASH_ReadHalfWord(0x0800FC00)!= 0xA5A5 )
	{
		MyFLASH_ErasePages(0x0800FC00);
		MyFLASH_ProgramHalfWord(0x0800FC00,0xA5A5);
		for(uint16_t i=1;i<512;i++ )
		{
		MyFLASH_ProgramHalfWord(0x0800FC00+ i*2 ,0x0000);
		}
	}

第二大步,上电的时候,把闪存的数据全都转存到SRAM数组里,转存到这个数组里的过程,就是上电的时候恢复数据,实现数据掉电不丢失。

上电的时候,把闪存备份的数据,恢复到SRAM数组里,之后,我们响存储掉电不丢失的参数的时候,就先任意更改这个数组,除了标志位的其他数据,更改后之后,把这个数组,整体备份到闪存的最后一页。

梳理:

这时闪存的最后一页,直接对它读写的话,那肯定不方便,效率低,还容易丢失数据,所以在SRAM中,定义一个数组,它就是闪存的"分身",我们再读写任何数据,就直接对SRAM操作,这样就非常方便了,但是SRAM掉电丢失,所以我们需要闪存来配合,SRAM每次更改的时候,都把数组整体备份到闪存里,而在上电的时候,再把闪存里的数据,初始化加载,回到SRAM里,这样SRAM数组,是不是就相当于掉电不丢失了。如果标志位是A5A5,就说明已经保持过了,如果不是A5A5,说明闪存是第一次使用,我们就先初始化一下,再加载数据。

对于出现很多次又可以修改的数据,最好使用宏定义替换一下,

程序过大影响最后一页了,可以设置不然程序影响用户存储




2、读取ID号

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.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)
	{
	}
}

问题

总结

读写内部FLASH&读取芯片ID

相关推荐
Neil今天也要学习17 分钟前
永磁同步电机控制算法--线性ADRC转速环控制器(一阶、二阶)
单片机·嵌入式硬件
蒙奇D索大41 分钟前
【11408学习记录】英语书信通知写作模板大全:5个高分句式+使用场景解析,速存每日一句拆解练习!
笔记·学习·考研·改行学it
搏博1 小时前
模式识别的基本概念与理论体系
人工智能·深度学习·学习·算法·机器学习·数据挖掘
唐僧洗头爱飘柔95271 小时前
(Go Gin)Gin学习笔记(二):路由配置、基本路由、表单参数、上传单个文件、上传多个文件、浅扒路由原理
学习·golang·gin·路由参数·路由配置·web开发框架·路由组
我的golang之路果然有问题1 小时前
快速了解Go+微服务(概念和一个例子)
开发语言·笔记·后端·学习·微服务·golang
朝九晚五ฺ2 小时前
【算法学习】哈希表篇:哈希表的使用场景和使用方法
数据结构·学习·散列表
xiaohanbao093 小时前
day11 python超参数调整
python·学习·机器学习·信息可视化·pandas
OG one.Z3 小时前
文件读取操作
c++·学习·文件读取
CodeWithMe3 小时前
【C/C++】线程池_学习笔记
笔记·学习
bylander3 小时前
【论文速读】《Scaling Scaling Laws with Board Games》
人工智能·学习