读写内部FLASH&读取芯片ID

STM32内部FLASH读写与ID读取

FLASH

  1. FLASH简介
    ①STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程.
    ②读写FLASH的用途:
    利用程序存储器的剩余空间来保存掉电不丢失的用户数据.
    通过在程序中编程(IAP),实现程序的自我更新.
    ③在线编程(In-Circuit Programming – ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序.
    ⑤在程序中编程(In-Application Programming – IAP)可以使用微控制器支持的任一种通信接口下载程序.
  2. FLASH结构图
  1. FLASH基本结构
    在这里插入图片描述
  2. FLASH解锁
    FPEC共有三个键值:
    RDPRT键 = 0x000000A5
    KEY1 = 0x45670123
    KEY2 = 0xCDEF89AB

RDPRT和KEY1 KEY2的对比如图:
在这里插入图片描述

对STM32的Flash进行编程或擦除等写操作前,除了可能需要处理读保护,通常还需要先解锁FPEC。这是通过向FLASH_KEYR寄存器依次写入KEY1和KEY2来完成的 。

解锁:
复位后,FPEC被保护,不能写入FLASH_CR
在FLASH_KEYR先写入KEY1,再写入KEY2,解锁
错误的操作序列会在下次复位前锁死FPEC和FLASH_CR
加锁:
设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR

  1. 使用指针访问FLASH
    在这里插入图片描述

看到如__IO后面是大写英文字母,前面有两个’_',它的作用是禁止编译器对变量进行优化,确保每次读写操作都直接访问其内存地址。
为什么这很关键?因为在嵌入式系统中,变量的值可能会在程序控制之外被改变:
硬件寄存器:例如一个状态寄存器的值,会随着外设的工作状态或外部事件而实时变化
中断服务程序中的变量:一个在主循环中被读取的变量,可能在其中断服务程序中被修改
如果不用 volatile修饰,编译器在优化代码时,可能会认为变量的值没有变化,从而用之前的缓存值代替,导致程序无法读取到最新状态,引发错误逻辑
在STM32的嵌入式开发中,__IO、__I、__O这些修饰符非常重要,它们主要用来安全、准确地操作硬件寄存器和一些特殊变量,如图:
在这里插入图片描述

  1. 程序存储器编程
  • 写入过程:在这里插入图片描述
  • 页擦除过程:
    在这里插入图片描述
  • 全擦除过程:
    在这里插入图片描述

读写内部FLASH&读取芯片ID

读写内部FLASH

  • 接线图如下:
    在这里插入图片描述
  • 由于本工程项目不涉及外部电路,所以在文件夹下System下创建MyFlash.c和MyFlash.h文件.

MyFlash.c代码:

#include "stm32f10x.h"                  // Device header

//读32位数据
uint32_t MyFlash_ReadWord(uint32_t Address)
{
	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_EraseAllPage(void)
{
	FLASH_Unlock();
	FLASH_EraseAllPages();
	FLASH_Lock();
}

//指定页擦除
void MyFlash_ErasePage(uint32_t PageAddress)
{
	FLASH_Unlock();
	FLASH_ErasePage(PageAddress);
	FLASH_Lock();
}

//写入全字(32bit)
void MyFlash_ProgramWord(uint32_t Address,uint32_t Data)
{
	FLASH_Unlock();
	FLASH_ProgramWord(Address,Data);
	FLASH_Lock();
}

//写入半字(16bit)
void MyFlash_ProgramHalfWord(uint32_t Address,uint16_t Data)
{
	FLASH_Unlock();
	FLASH_ProgramHalfWord(Address,Data);
	FLASH_Lock();
}
	

值得注意的是:
①STM32官方已经将解锁和加锁以及写入读出的函数封装好了,只需要调用即可.
②什么时候写入操作时用半字写入?什么时候用字写入?
答:何时使用 FLASH_ProgramHalfWord(写入半字):
1.处理16位数据时:这是最自然和高效的选择,例如对 uint16_t类型的变量或数组进行操作。
2.与半字外设寄存器交互时:某些外设的寄存器可能是16位宽度。
3.内存空间紧张时:如果只是存储少量16位数据,使用半字写入可以节省空间.
何时使用 FLASH_ProgramWord(写入字):
1.处理32位数据时:当需要存储 uint32_t、float(在STM32F10x中通常也是32位)等类型的数据时,直接使用字写入比拆分成两个半字更方便。
2.追求代码简洁性:即使底层是两次操作,用一个函数调用完成32位数据的写入,代码可读性更好。
3.注意:需要确保你的32位数据地址是4字节对齐的,否则可能引发硬件错误。
③什么操作前要对Flash解锁与上锁?
答:1.解锁与上锁:在对Flash进行写或擦除操作前,必须先调用 FLASH_Unlock()解锁FPEC;操作完成后,应立即调用 FLASH_Lock()上锁,防止误操作。
2.先擦后写:Flash存储单元的物理特性决定了在写入前,目标地址必须处于已擦除状态(即所有位为1,值等于0xFFFF)。尝试向未擦除的区域写入数据会失败或在状态寄存器中置位错误标志。
3.擦除的最小单位是页(Page).

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_EraseAllPage(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

  • 在System文件夹下建立Store.c文件和Store.h文件,用来更好的调用MyFlash.c文件里的函数对一个整页存储空间进行操作.

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);
		//将该页第一个位置为标志位0xA5A5
		MyFlash_ProgramHalfWord(STORE_START_ADDRESS,0xA5A5);
		//将该页的除去第一个标志位置为0
		for(uint32_t i = 1;i < STORE_COUNT;i ++)
		{
		MyFlash_ProgramHalfWord(STORE_START_ADDRESS + i * 2,0x0000);
		}
	}
	//将该页数据复制进数据,也就是SRAM
	for(uint32_t i = 0;i < STORE_COUNT;i ++)
	{
		Store_Data[i] = MyFlash_ReadHalfWord(STORE_START_ADDRESS + i * 2);
	}
}

void Store_Save(void)
{
	//先将该页的数据全部擦除
	MyFlash_ErasePage(STORE_START_ADDRESS);
	//把数据(SRAM)的所有内容备份到该页
	for(uint32_t i = 0;i < STORE_COUNT;i ++)
	{
		MyFlash_ProgramHalfWord(STORE_START_ADDRESS + i * 2,Store_Data[i]);
	}
}

//将数据清零
void Store_Clear(void)
{
	for(uint32_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

读取芯片ID

  • 接线图如下:
    在这里插入图片描述
  • 代码比较简单,直接在main.c文件里编程就行.

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


芯片的16位大小存在地址0x1FFFF7E0下,芯片的96位ID号存在地址0x1FFFF7E8和0x1FFFF7E8 + 0x02下.
具体如图片所示:
在这里插入图片描述

至此,STM32的基础知识已学习完毕.
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

必胜的思想钢印

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

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

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

打赏作者

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

抵扣说明:

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

余额充值