STM32CubeMX学习笔记16--- STM32内部FLASH

1. 内部FLASH简介

        之前的文章中介绍过STM32F1利用SPI与外部FLASH(W25QXX芯片)通讯的例程,本例程将介绍STM32F1的内部FLASH,通过内部FLASH实现数据读写操作。 不同型号的STM32,其FLASH容量也有所不同,最小的只有16K字节,最大的则达到了1024K字节。此处我们使用的是STM32F103ZET6,其FLASH容量为512K字节,属于大容量产品,大容量产品的闪存模块组织图如下图示

STM32F1的闪存模块由:主存储器、信息块和闪存存储器接口寄存器3部分组成

  • 主存储器:用来存放代码和数据常量,起始地址是0x08000000,BOOT0和BOOT1都接GND时,就是从该起始地址运行代码的
  • 信息块:分为2个小部分,启动程序代码是用来存储ST自带的启动程序,用于串口下载代码,BOOT0接3.3V,BOOT1接GND时,运行的就是这部分代码;选择字节则一般用于配置写保护、读保护等功能
  • 闪存存储器接口寄存器:用于控制闪存读写等,是整个闪存模块的控制机构

对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理:编程与擦除的高电压由内部产生。在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行,即在进行写或擦除操作时,不能进行代码或数据的读取操作。

下面介绍闪存的读取、编程和擦除:

<1>. 闪存的读取 内置闪存模块可以在通用地址空间直接寻址,任何32位数据的读操作都能访问闪存模块的内容并得到相应的数据。

例如,要从地址addr,读取一个半字,可通过如下语句读取:  data = *(__IO uint16_t*)addr 将addr强制转换为vu16指针,然后取该指针所指向的地址的值,即得到了addr地址的值

<2>. 闪存的编程 STM32的闪存编程是由FPEC(闪存编程和擦除控制器)模块处理的,这个模块包含7个32位寄存器,它们分别是:

FPEC键寄存器(FLASH_KEYR)
选择字节键寄存器(FLASH_OPTKEYR)
闪存控制寄存器(FLASH_CR)
闪存状态寄存器(FLASH_SR)
闪存地址寄存器(FLASH_AR)
选择字节寄存器(FLASH_OBR)
写保护寄存器(FLASH_WRPR)

其中FPEC键寄存器共有3中键值: PDPRT=0x000000A5; KEY1=0x45670123; KEY2=0xCDEF89AB STM32复位后,FPEC模块是被保护的,不能写入FLASH_CR寄存器;通过写入特定的序列到FLASH_KEYR寄存器可以打开FPEC模块(即写入KEY1和KEY2),只有在写保护被解除后,才能操作相关寄存器。 闪存编程过程如下图所示:

<3>. 闪存的擦除 闪存编程的时候,要先判断其写入地址的FLASH是被擦除了的(也就是其值必须是0xFFFF),否则无法写入。闪存擦除分为页擦除和整片擦除。 闪存页擦除过程如下图示:

官方固件HAL库FLASH操作的几个常见函数:

//源文件: stm32f1xx_hal_flash.c和stm32f1xx_hal_flash_ex.c
HAL_FLASH_Unlock(void); //解锁函数
HAL_FLASH_Lock(void);   //锁定函数
HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);   //写操作函数
HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *SectorError);   //擦除函数
HAL_FLASH_WaitForLastOperation(uint32_t Timeout);   //等待操作完成函数

 

2、 硬件设计

led2指示灯用来提示系统运行状态,s1按键用来控制FLASH的数据写入,s2按键用来控制FLASH的数据读取,数据的写入与读取信息通过串口1打印出来

  • LED2指示灯
  • S2和S1按键
  • USART1
  • STM32F1内部FLASH

 3、STM32CubeMX设置

  • RCC设置外接HSE,时钟设置为72M
  • PE5设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位
  • PE3,PE4设置为GPIO输入模式、上拉模式
  • 输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码

4、程序编程

  • 创建按键驱动文件key.c 和相关头文件key.h

如果要对FLASH进行写入数据,需要执行以下四步:

  1. 解锁FLASH
  2. 擦除FLASH
  3. 写入FLASH
  4. 锁住FLASH
  • 创建FLASH驱动文件stmflash.c 和相关头文件stmflash.h
#ifndef __STMFLASH_H__
#define __STMFLASH_H__#include "main.h"  //FLASH起始地址
#define STM32_FLASH_BASE 0x08000000 		//STM32 FLASH的起始地址#define FLASH_WAITETIME 50000extern void FLASH_PageErase(uint32_t PageAddress);
uint16_t STMFLASH_ReadHalfWord(uint32_t faddr);		  //读出半字  
void STMFLASH_Write_NoCheck(uint32_t WriteAddr,uint16_t *pBuffer,uint16_t NumToWrite);
void STMFLASH_Write(uint32_t WriteAddr,uint16_t *pBuffer,uint16_t NumToWrite);		//从指定地址开始写入指定长度的数据
void STMFLASH_Read(uint32_t ReadAddr,uint16_t *pBuffer,uint16_t NumToRead);   		//从指定地址开始读出指定长度的数据#endif
#include "stmflash.h"uint16_t STMFLASH_ReadHalfWord(uint32_t faddr)
{return *(__IO uint16_t*)faddr; 
}void STMFLASH_Write_NoCheck(uint32_t WriteAddr,uint16_t *pBuffer,uint16_t NumToWrite)   
{ 			 		 uint16_t i;for(i=0;i<NumToWrite;i++){HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,pBuffer[i]);WriteAddr+=2;		//地址增加2.}  
} #define STM_SECTOR_SIZE	2048	//大容量STM32的扇区大小为2Kuint16_t STMFLASH_BUF[STM_SECTOR_SIZE/2];void STMFLASH_Write(uint32_t WriteAddr,uint16_t *pBuffer,uint16_t NumToWrite)	
{uint32_t secpos;	   //扇区地址uint16_t secoff;	   //扇区内偏移地址(16位字计算)uint16_t secremain; //扇区内剩余地址(16位字计算)	   uint16_t i;    uint32_t offaddr;   //去掉0X08000000后的地址if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*512)))return;//非法地址HAL_FLASH_Unlock();					//解锁offaddr=WriteAddr-STM32_FLASH_BASE;		//实际偏移地址.secpos=offaddr/STM_SECTOR_SIZE;			//扇区地址  0~127 for STM32F103RBT6secoff=(offaddr%STM_SECTOR_SIZE)/2;		//在扇区内的偏移(2个字节为基本单位.)secremain=STM_SECTOR_SIZE/2-secoff;		//扇区剩余空间大小   if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围while(1) {	STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容for(i=0;i<secremain;i++)	//校验数据{if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除  	  }if(i<secremain)				//需要擦除{FLASH_PageErase(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);	//擦除这个扇区FLASH_WaitForLastOperation(FLASH_WAITETIME);            	//等待上次操作完成CLEAR_BIT(FLASH->CR, FLASH_CR_PER);							//清除CR寄存器的PER位,此操作应该在FLASH_PageErase()中完成!//但是HAL库里面并没有做,应该是HAL库bug!for(i=0;i<secremain;i++)//复制{STMFLASH_BUF[i+secoff]=pBuffer[i];	  }STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  }else {FLASH_WaitForLastOperation(FLASH_WAITETIME);       	//等待上次操作完成STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. }if(NumToWrite==secremain)break;//写入结束了else//写入未结束{secpos++;				//扇区地址增1secoff=0;				//偏移位置为0 	 pBuffer+=secremain;  	//指针偏移WriteAddr+=secremain*2;	//写地址偏移(16位数据地址,需要*2)	   NumToWrite-=secremain;	//字节(16位)数递减if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完else secremain=NumToWrite;//下一个扇区可以写完了}	 };	HAL_FLASH_Lock();		//上锁
}void STMFLASH_Read(uint32_t ReadAddr,uint16_t *pBuffer,uint16_t NumToRead)   	
{uint16_t i;for(i=0;i<NumToRead;i++){pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.ReadAddr+=2;//偏移2个字节.	}
}
  • 在main.c文件下编写STM32 flash测试代码
/* USER CODE BEGIN 0 */
const uint8_t Text_Buf[] = {"STM32F103ZET6 FLASH TEST"};
#define TEXTSIZE sizeof(Text_Buf)
#define FLASH_SAVE_ADDR 0x08070000/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{///***省略***////* USER CODE BEGIN WHILE */uint8_t key;uint8_t Read_Buf[TEXTSIZE];printf1("STM32 Flash Test...\r\n");while (1){key = KEY_Scan(0);if(key == 1){STMFLASH_Write(FLASH_SAVE_ADDR,(uint16_t *)Text_Buf,TEXTSIZE);printf1("FLASH Write : %s\r\n",Text_Buf);}if(key == 2){STMFLASH_Read(FLASH_SAVE_ADDR,(uint16_t *)Read_Buf,TEXTSIZE);printf1("FLASH Read : %s\r\n",Read_Buf);}HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);HAL_Delay(200);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

5、下载验证

编译无误下载到开发板后,可以看到LED2指示灯不断闪烁,当按下S1按键后数据写入到FLASH内,当按下S2按键后将写入的数据读取出来,同时串口打印出相应信息

6、参考文献 

STM32CubeMX系列 | STM32内部FLASH - 知乎 (zhihu.com)

 STM32CUBEMX-读写内部Flash_stm32cubemx flash-CSDN博客

STM32CubeMX学习笔记(51)——读写内部Flash_cubemx flash-CSDN博客

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

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

相关文章

nexus 简单运维

Nexus介绍 Nexus 是 Sonatype 公司发布的一款仓库&#xff08;Repository&#xff09;管理软件&#xff0c;常用来搭建 Maven 私服&#xff0c;所以也有人将 Nexus 称为“Maven仓库管理器”。 Nexus常用命令 进入安装目录 再进入bin目录 ​ cd /opt/nexus-3.13/nexus-3.13.…

【java】后序遍历二叉树

采用递归方式实现 节点类 public class Node {private int value;//父节点private Node fNode;//左节点private Node left;//右节点private Node right;//是否已经打印过private boolean sign false;public Node() {}public boolean isSign() {return sign;}public void setS…

未来AI发展趋势

引言 如何实现一个乌托邦式的社会呢&#xff1f;如果直接将人类社会分为两个阵营&#xff0c;一个是以旧人类为首的阵营&#xff0c;一个是以AI抚养的新人类阵营&#xff0c;其中新人类阵营的所有资源皆由旧人类阵营提供&#xff0c;且旧人类阵营的人类都经过了化学阉割无法生…

14:00面试,15:00就出来了,问的问题过于变态了。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到2月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

静态时序分析:典型与非典型时序路径的约束详解(一)

相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 时序路径是静态时序分析中的一个重要概念&#xff0c;了解时序路径能帮助设计者更好地编写SDC脚本&#xff0c;本文旨在详细介绍时序路径相关内容。 首先给出时序…

Unity角色动画变成半跪\半蹲\下沉 的问题

导入的人物动画发生如图形态 解决方法&#xff1a;找到动画模型&#xff0c;Rig - AnimationType 改为Humanoid &#xff0c;然后Apply一下

高效加载大文件(pandas+dask)

一、仅用pd加载大文件(iterator、chunksize) 要使用Pandas进行高效加载超大文件&#xff0c;我们通常会利用其内置的分块&#xff08;chunk&#xff09;处理功能。不过&#xff0c;请注意&#xff0c;Pandas本身并不支持多线程读取文件&#xff1b;它更倾向于单线程中进行块处理…

千云GPS平台 -在k8s上部署Mysql

构建xtrabackup docker build -t registry.cn-zhangjiakou.aliyuncs.com/qy566/xtrabackup:8.0.34 --rm .部署进度 你可以通过运行以下命令查看启动进度: kubectl get pods -l app=mysql -n mysql-db --watch 测试mysql 发送客户端请求写入数据kubectl run mysql-client --…

解决cs不能生成Linux木马的问题

要解决的问题&#xff1a;众所周知&#xff0c;msf上面的shell或者是其他的shell想反弹给cs默认情况下是只支持windows的&#xff0c;因为cs的监听模块默认没有linux的&#xff0c;但是有些主机就是用linux搭建的&#xff0c;这可怎么办呢。就要用到一个插件CrossC2。 下载插件…

实操keepalived(高可用)+Nginx(四层代理+七层代理),实现高可用、负载均衡以及动静分离

一 vrrp技术 VRRP 相关术语 VRRP能够在不改变组网的情况下&#xff0c;将多台路由器虚拟成一个虚拟路由器&#xff0c;i通过配置虚拟路由器的IP地址为默认网关&#xff0c;实现网关的备份。 协议版本: VRRPv2 (常用) 和VRRPv3:0 VRRPv2仅适用于IPv4网络&#xff0c;VRRPv3适用…

[项目设计] 从零实现的高并发内存池(五)

&#x1f308; 博客个人主页&#xff1a;Chris在Coding &#x1f3a5; 本文所属专栏&#xff1a;[高并发内存池] ❤️ 前置学习专栏&#xff1a;[Linux学习] ⏰ 我们仍在旅途 ​ 目录 8 使用定长内存池脱离new 9. 释放对象时不传大小 10.性能优化 10.1…

[LeetCode][239]【学习日记】滑动窗口最大值——O(n)单调队列

题目 239. 滑动窗口最大值 难度&#xff1a;困难相关标签相关企业提示 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回滑动窗口中的最大值。 示例 1…

雷赛控制卡的扩展IO连接

雷赛控制卡的扩展IO点无法控制问题处理 现象 因设备的上IO点较多&#xff0c;所以使用了多个雷赛32点位的IO扩展卡。上位机程序在控制输出IO时发现主模块IO和第一个扩展IO的输出可以控制。但第二个IO扩展卡和第三个IO扩展卡的输出控制不了。经排查出发现轴卡在初始化时只连接了…

校园小情书微信小程序,社区小程序前后端开源,校园表白墙交友小程序

功能 表白墙卖舍友步数旅行步数排行榜情侣脸漫画脸个人主页私信站内消息今日话题评论点赞收藏 效果图

2024年腾讯云学生服务器优惠活动「云+校园」政策解读

2024年腾讯云学生服务器优惠活动「云校园」&#xff0c;学生服务器优惠价格&#xff1a;轻量应用服务器2核2G学生价30元3个月、58元6个月、112元一年&#xff0c;轻量应用服务器4核8G配置191.1元3个月、352.8元6个月、646.8元一年&#xff0c;CVM云服务器2核4G配置842.4元一年&…

几何工具的使用

Geometry - Creation 创建几何 CogCreateCircleTool&#xff1a;创建圆CogCreateEllipseTool:创建椭圆CogCreateLineBisectPointsTool&#xff1a;带有两个点的平行线CogCreateLineParallelTool:在某一点创建某条线的平行线CogCreateLinePerpendicularTool:在某一点创建某条线…

LT6813/ADBMS1818底层驱动---均衡控制

1、LT6813采用内部均衡的原理 2、平衡控制结构体 根据数据库中读取的控制值设置平衡。要为单元设置平衡&#xff0c;必须将相应的位写入配置寄存器中。LTC 驱动程序仅执行数据库中 BMS 写入的数据。 参数 ltc_stateLTC 状态机的状态pSpi接口指向 SPI 配置的指针pTxBuff &…

数据结构(八)——初识单链表

&#x1f600;前言 单链表是数据结构中最基本的一种链表结构&#xff0c;它由一系列节点组成&#xff0c;每个节点包含数据和指向下一个节点的指针。单链表具有灵活性和动态性&#xff0c;可以根据需要插入、删除和查找元素&#xff0c;适用于各种场景和问题的解决。 在本篇文章…

day7-网络编程

1>基于UDP的网络聊天室 Ser.c #include <myhead.h> #define SER_IP "10.211.55.9" // 服务器IP #define SER_PORT 9999struct user {char usrName[20];struct sockaddr_in cin; }; int main(int argc, char const *argv[]) {// 1.创建用于监听的套接字int…

MyBatis-Flex学习总结

写在前面的话 MyBatis-Flex 是一个优雅的 MyBatis 增强框架&#xff0c;它非常轻量、同时拥有极高的性能与灵活性。我们可以轻松的使用 Mybaits-Flex 链接任何数据库&#xff0c;其内置的 QueryWrapper 帮助我们极大的减少了 SQL 编写的工作的同时&#xff0c;减少出错的可能性…