STM32 Flash学习(三)

硬件设计

开机的时候先显示一些提示信息,然后在主循环里面检测两个按键。
其中1个按键WK_UP用来执行写入FLASH的操作,另一个按照KEY0用来执行读出操作。

软件设计

添加了两个文件stmflash.c和stmflash.h。

#include "stmflash.h"
#include "delay.h"
#include "usart.h"extern void FLASH_PageErase(uint32_t PageAddress);//读取指定地址的半字(16位)
u16 STMFLASH_ReadHalfWord(u32 faddr)
{return *(vu16*)faddr;
}//不检查的写入
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{u16 i;for(i=0; i<NumToWrite; i++){HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,pBuffer[i]);WriteAddr+=2;//地址增加 2.}
}//从指定地址开始写入指定长度的数据
#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024 //字节
#else
#define STM_SECTOR_SIZE 2048 //字节u16 STMFLASH_BUF[STM_VECTOR_SIZE/2];void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{u32 secpos; //扇区地址u16 secoff;//扇区内偏移地址(16位字计算)u16 secremain; //扇内剩余地址(16位字计算)u16 i;u32 offaddr; //去掉0x08000000后的地址if(WriteAddr<STN32_FLASH_BASE||(WriteAdd>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE))) return;HAL_FLASH_Unlock(); //解锁offaddr = WriteAddr - STM32_FLASH_BASE;secpos = offaddr/SIM_SECTOR_SIZE;secoff = (offaddr%STM_SECTOR_SIZE)/2;secremain = SIM_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();		//上锁
}
#endif

STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
参数:

  • WriteAddr:起始地址(必须为2的倍数)
  • pBuffer:数据指针,指向要写入的数据缓冲区
  • NumToWrite:16位数据量(以半字为单位,即两个字节)

功能:
该函数用于将从指定地址开始的指定长度的数据写入闪存。
首先计算出目标地址所在的扇区地址和扇区内偏移地址。
然后,从Flash读出整个扇区的内容,并检查是否需要执行擦除操作。
如果需要擦除扇区,则执行擦除操作,并将数据复制到缓冲区。
最后,使用STMFLASH_Write_NoCheck函数将整个扇区的数据写入Flash。
如果不需要擦除操作,直接使用STMFLASH_Write_NoCheck函数将数据写入到扇区的剩余空间。

该函数对写入地址是有要求的,必须保证以下两点:

  1. 该地址必须是用户代码区以外的地址。
  2. 该地址必须是2的倍数。

条件1,如果把用户代码擦了,运行的程序就废了,可能出现死机的情况。条件2是STM32 FLASH的要求,每次必须写入16位,如果写入的地址不是2的倍数,那么写入的数据,可能就不正确。

//从指定地址开始读出指定长度的数据
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
{u16 i;for(i=0; i<NumToRead; i++){pBuffer[i] = STMFLASH_ReadHalfWord(ReadAddr); //读取2个字节ReadAddr += 2;}
}//测试用
void Test_Write(u32 WriteAddr,u16 WriteData)
{STMFLASH_Write(WriteAddr, &WriteData, 1);//写入1个字
}

main.c文件

#include "stmflash.h"//要写入到STM32_FLASH的字符串数组
const u8 TEXT_Buffer[] = {"STM32 FLASH TEST"};
#define SIZE sizeof(TEXT_Buffer)
#define FLASH_SAVE_ADDR 0x08020000 //设置FLASH保存地址必须为偶数int main(void)
{u8 key = 0;u8 datatemp[SIZE];HAL_Init();                    	 	//初始化HAL库    Stm32_Clock_Init(RCC_PLL_MUL9);   	//设置时钟,72Mdelay_init(72);               		//初始化延时函数uart_init(115200);					//初始化串口LED_Init();							//初始化LED	KEY_Init();							//初始化按键while(1){key = KEY_SCAN(0);if(key = KEY1_PRES){STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);}if(key==KEY0_PRES)	//KEY0按下,读取字符串并显示{STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE);}}
}

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

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

相关文章

freeswitch的mod_xml_curl模块

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 随着fs服务的增多&#xff0c;每一台fs都需要在后台单独配置&#xff0c;耗时耗力&#xff0c;心力憔悴。 如果有一个集中管理配置的配置中心&#xff0c;统一管理所有fs的配置&#xff0c;并可以实现动态的修改配置就…

自动驾驶地面车辆的雷达里程计:方法与数据集综述

在不同复杂环境中&#xff0c;各种传感器的性能会有所不同。它们各自具有优势和劣势&#xff0c;因此融合多模态数据提供了一种解决方案&#xff0c;可以克服各个传感器单独使用时的限制[90]–[92]。此外&#xff0c;许多讨论的传感器已经广泛应用于自动驾驶领域&#xff0c;因…

PHP 支付宝支付、订阅支付(周期扣款)整理汇总

最近项目中需要使用支付宝的周期扣款&#xff0c;整理一下各种封装方法 APP支付&#xff08;服务端&#xff09; /******************************************************* 调用方法******************************************************/function test_pay(){$isSubscri…

mybatis日志工厂

前言&#xff1a; 如果一个数据库操作&#xff0c;出现异常&#xff0c;我们需要排错&#xff0c;日志就是最好的助手 官方给我们提供了logImpl&#xff1a;指定 MyBatis 所用日志的具体实现&#xff0c;未指定时将自动查找。 默认工厂&#xff1a; 在配置文件里添加&#xf…

深度剖析APP开发中的UI/UX设计

作为一个 UI/UX设计师&#xff0c;除了要关注 UI/UX设计之外&#xff0c;还要掌握移动开发知识&#xff0c;同时在日常工作中也需要对用户体验有一定的认知&#xff0c;在本次分享中&#xff0c;笔者就针对自己在工作中积累的一些经验来进行一个总结&#xff0c;希望能够帮助到…

cartographer发布畸变矫正后的scan数据

实现方式&#xff1a; 模仿源代码&#xff0c;在cartographer_ros写一个函数&#xff0c;以函数指针的方式传入cartographer后端&#xff0c;然后接收矫正后的scan数据&#xff0c;然后按照话题laserScan发布出来。 需要同时发布点云强度信息的&#xff0c;还要自己添加含有强度…

如何连接远程服务器?快解析内内网穿透可以吗?

如今我们迎来了数字化转型的时代&#xff0c;众多企业来为了更好地推动业务的发展&#xff0c;常常需要在公司内部搭建一个远程服务器。然而&#xff0c;对于企业员工来说&#xff0c;在工作过程中经常需要与这个服务器进行互动&#xff0c;而服务器位于公司的局域网中&#xf…

Go重写Redis中间件 - Go实现Redis协议解析器

Go实现Redis协议解析器 Redis网络协议详解 在解决完通信后,下一步就是搞清楚 Redis 的协议-RESP协议,其实就是一套类似JSON、Protocol Buffers的序列化协议,也就是我们的客户端和服务端通信的协议 RESP定义了5种格式 简单字符串(Simple String) : 服务器用来返回简单的结…

简述IO(BIO NIO IO多路复用)

在unix网络变成中的五种IO模型: Blocking IO(阻塞IO) NoneBlocking IO (非阻塞IO) IO mulitplexing(IO多路复用) signal driven IO (信号驱动IO) asynchronous IO (异步IO) BIO BIO&#xff08;Blocking IO&#xff09;是一种阻塞IO模型&#xff0c;也是传统的IO操作模型之一…

Windows上安装和使用git到gitoschina和github上_亲测

Windows上安装和使用git到gitoschina和github上_亲测 git介绍与在windows上安装创建SSHkey在gitoschina使用 【git介绍与在windows上安装】 Git是一款免费、开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。 相关介绍可以参考 <百度百科>…

【Vue3 + Element Plus】纯前端实现本地数据分页

先附上效果图 Vue3 Element Plus 实现本地分页 首页弹窗代码 <el-table :data"tableData" style"width: 100%" border stripe><el-table-column v-for"{ id, prop, label } in tableColumn" :prop"prop" :key"id"…

RocketMQ概论

目录 前言&#xff1a; 1.概述 2.下载安装、集群搭建 3.消息模型 4.如何保证吞吐量 4.1.消息存储 4.1.1顺序读写 4.1.2.异步刷盘 4.1.3.零拷贝 4.2.网络传输 前言&#xff1a; RocketMQ的代码示例在安装目录下有全套详细demo&#xff0c;所以本文不侧重于讲API这种死…

【Rust 基础篇】Rust默认泛型参数:简化泛型使用

导言 Rust是一种以安全性和高效性著称的系统级编程语言&#xff0c;其设计哲学是在不损失性能的前提下&#xff0c;保障代码的内存安全和线程安全。在Rust中&#xff0c;泛型是一种非常重要的特性&#xff0c;它允许我们编写一种可以在多种数据类型上进行抽象的代码。然而&…

tcp keepalive

tcp keepalive用于检查两者之间的链路是否正常&#xff0c;或防止链路断开。 一旦建立了TCP连接&#xff0c;该连接被定义为有效&#xff0c;直到一方关闭它。一旦连接进入连接状态&#xff0c;它将无限期地保持连接状态。但实际上&#xff0c;这种联系不会无限期地持续下去。如…

数据结构:快速的Redis有哪些慢操作?

redis 为什么要这莫快&#xff1f;一个就是他是基于内存的&#xff0c;另外一个就是他是他的数据结构 说到这儿&#xff0c;你肯定会说&#xff1a;“这个我知道&#xff0c;不就是 String&#xff08;字符串&#xff09;、List&#xff08;列表&#xff09;、 Hash&#xff08…

1.Ansible

文章目录 Ansible概念作用特性总结 部署AnsibleAnsible模块commandshellcronusergroupcopyfilehostnamepingyumserice/systemdscriptmountarchiveunarchivereplacesetup inventory主机清单主机变量组变量组嵌套 Ansible 概念 Ansible是一个基于Python开发的配置管理和应用部署…

【Redis】面试题

1. 为什么要用缓存 1. 提高系统的读写性能。 2. 减轻数据库的压力&#xff0c;防止大量的请求到达数据库&#xff0c;让数据库压力剧增&#xff0c;拖垮数据库。redis数据存储在内存中&#xff0c;高效的数据结构&#xff0c;读写数据比数据库快。 将热点数据存储在redis当中&…

#P1004. [NOIP1998普及组] 三连击

题目描述 将 1, 2, \ldots , 91,2,…,9 共 99 个数分成 33 组&#xff0c;分别组成 33 个三位数&#xff0c;且使这 33 个三位数构成 1 : 2 : 31:2:3 的比例&#xff0c;试求出所有满足条件的 33 个三位数。 输入格式 无 输出格式 若干行&#xff0c;每行 33 个数字。按照…

数据结构:分块查找

分块查找&#xff0c;也叫索引顺序查找&#xff0c;算法实现除了需要查找表本身之外&#xff0c;还需要根据查找表建立一个索引表。例如图 1&#xff0c;给定一个查找表&#xff0c;其对应的索引表如图所示&#xff1a; 图 1 查找表及其对应的索引表 图 1 中&#xff0c;查找表…

小程序 账号的体验版正式版的账号信息及相关配置

siteinfo.js // 正式环境 const releaseConfig {appID: "",apiUrl: "",imgUrl: "" }; // 测试环境&#xff08;包含开发环境和体验环境&#xff09; const developConfig {appID: "",apiUrl: "",imgUrl: "" }…