当前位置: 首页 > news >正文

嵌入式开发--STM32G4系列片上FLASH的读写

这个玩意吧,说起来很简单,就是几行代码的事,但楞是折腾了我大半天时间才搞定。原因后面说,先看代码吧:

读操作

读操作很简单,以32位方式读取的时候是这样的:

data = *(__IO uint32_t *)(0x0800F000);

需要注意的是,当以32位方式读取时,地址需要是4的整数倍,即32位。
8位或16位方式类似操作即可
在这里插入图片描述

写操作

需要注意的是,写操作时,是以64位方式写入数据,即以双字的方式写入,以下代码是将一个u64的值0x12345678aabbccdd,写入0x0800F000这个地址

HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
HAL_FLASH_Program(0, 0x0800F000, 0x12345678aabbccdd);
HAL_FLASH_Lock();

扇区擦除

  EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;    //删除方式EraseInitStruct.Page        = start;                    //超始页号EraseInitStruct.NbPages     = len;                      //页的数量EraseInitStruct.Banks       = bank;                     //bank号HAL_FLASH_Unlock();         //解锁,以准备进行FLASH操作HAL_FLASHEx_Erase(&EraseInitStruct, &err);      //擦除HAL_FLASH_Lock();           //上锁,以结束FLASH操作

调试

写完烧录开始调试,发现问题了,有时能写入,有时不能写入。

先找了正点原子的例程来做参考,他的可以写入,但似乎也是有问题,没有仔细研究。而且原子的例程是操作寄存器进行读写的,不直观,移植性也不好,个人还是喜欢用HAL库的方式来做东西。

然后又找了ST的例程来看,刚好手上有一块G4的开发板,于是编译,报错,可能是我的开发环境比较新,与ST官方的编译环境不同,又是一通折腾,编译通过,但一加载调试,就卡死不动。

于是新建工程,再把ST的例程移植到我的工程中,编译通过,可以调试,还是有时能写有时不能写。又回到了起点。

不过在前面的折腾中总结了一个规律,第1次写入几乎都会失败,第2次有一半的机率成功,但后续成功率很高,几乎不会写入失败。于是改进一下,增加对写入的判断,如果发生错误,会重复写入不超过10次,如果超过10次仍然错误,则写入失败。这样就可以保证写入成功了。

测试代码如下

未包含写入失败的相关代码,需要的自行添加。

if(GET_KEY() == 0)
{data32[0] = *(__IO uint32_t *)(0x08010000+i*8);		//FLASH读数据,以字的方式读取data32[1] = *(__IO uint32_t *)(0x08010000+i*8+4);if((data32[0] == 0xffffffff) && (data32[1] == 0xffffffff))	//FLASH内容为空{LED1(1);HAL_FLASH_Unlock();__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);flash_count=0;while(flash_count < 10){if(HAL_FLASH_Program(0, 0x08010000+i*8, 0xaabbccdd12340000+i) == HAL_OK)break;elseflash_count++;}HAL_FLASH_Lock();}data32[0] = *(__IO uint32_t *)(0x08010000+i*8);data32[1] = *(__IO uint32_t *)(0x08010000+i*8+4);while(GET_KEY() == 0);LED1(0);i++;
}

在这里插入图片描述

本段代码能正确运行的前提,是待写入区域是空的,即全都是0xFF才行。

封装为函数

将代码封装一下,以便后续调用,并增加了一些条件判断

头文件如下:

//********************************************************************************************************
//*                                                                                   
//*  文件名:LL_flash.h                                                               
//*  文件说明:STM32G系列片上FLASH的相关操作                                                     
//*  作者:李佳                                                                       
//*  微信:LAOLIDESENLIN                                                              
//*  说明:本文档遵循GNU3.0开源许可证规范,即代码可开源并免费使用,引用、修改、衍生代码也需要开源、免费使用,
//*        但不允许修改后和衍生的代码做为闭源的商业软件发布和销售
//*
//********************************************************************************************************#ifndef __LL_FLASH_H__
#define __LL_FLASH_H__#include "LL_define.h"/**************************************************************************************/
/* G431芯片的128KFLASH容量的页地址分布如下,共有1个BANK,64页,每一页2kb大小 */
#define STM32_FLASH_BASE        0x08000000      /* STM32 FLASH 起始地址 */
#define STM32_FLASH_SIZE        0x20000         /* STM32 FLASH 总大小*/
#define STM32_FLASH_PAGE_SIZE   0x800           /* STM32 FLASH 页大小*/u8 LL_flash_erase_page(u16 start, u8 len, u8 bank);     //删除FLASH扇区
u8 LL_flash_read(u32 addr, u64* pdata64, u32 len_64);   //读片上FLASH
u8 LL_flash_write(u32 addr, u64* pdata64, u32 len_64);  //向片上FLASH写入数据#endif

C文件如下

//********************************************************************************************************
//*                                                                                   
//*  文件名:LL_flash.c                                                               
//*  文件说明:STM32G系列片上FLASH的相关操作                                                     
//*  作者:李佳                                                                       
//*  微信:LAOLIDESENLIN                                                              
//*  说明:本文档遵循GNU3.0开源许可证规范,即代码可开源并免费使用,引用、修改、衍生代码也需要开源、免费使用,
//*        但不允许修改后和衍生的代码做为闭源的商业软件发布和销售
//*
//*
//*
//********************************************************************************************************#include "LL_flash.h"
#include "LL_IO.h"//按页删除片上FLASH数据
//start: 起始页号
//len:  待删除的页的数量
//bank: bank编号
//返回值:错误类型,0表示无错误
u8 LL_flash_erase_page(u16 start, u8 len, u8 bank)
{u32 err;u8 ret;FLASH_EraseInitTypeDef EraseInitStruct;u8 flash_count=0;EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;    //删除方式EraseInitStruct.Page        = start;                    //超始页号EraseInitStruct.NbPages     = len;                      //页的数量EraseInitStruct.Banks       = bank;                     //bank号HAL_FLASH_Unlock();         //解锁,以准备进行FLASH操作__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);for(u8 i=0; i<10; i++)      //最多重复10次,如果仍然失败,则返回{ret = HAL_FLASHEx_Erase(&EraseInitStruct, &err);      //擦除if (ret == HAL_OK)break;}if(ret != 0)led_err_flash(10, 100);   //擦除失败后闪灯提示, 供调试阶段使用, 正式使用时可删除HAL_FLASH_Lock();           //上锁,以结束FLASH操作return ret;
}//读片上FLASH
//addr: 32位的地址值,该值应当是8的整数倍,因为是按照64位的方式读取数据的
//pdata: 返回的数据首地址
//len: 待读取数据的长度, 长度是按u64的数量
//返回值: 错误类型,0表示无错误
u8 LL_flash_read(u32 addr, u64* pdata64, u32 len_64)
{u64 data;for(u32 i=0; i<len_64; i++){data = *(__IO uint64_t *)(addr+i*8);pdata64[i] = data;}return 0;
}//向片上FLASH写入数据,每次写入8字节,不够8字节的,以0xFF补足
//addr: 32位的地址值,该值应当是8的整数倍,因为是按照64位的方式读取数据的
//pdata: 待写入的数据首地址
//len: 待写入数据的长度,长度是按u64的数量
//返回值: 错误类型,0表示无错误
u8 LL_flash_write(u32 addr, u64* pdata64, u32 len_64)
{u8 ret;u64 read;HAL_FLASH_Unlock();           //上锁,以结束FLASH操作__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);for(u16 j=0; j<len_64; j++) //按u64计算的{for(u8 i=0; i<10; i++)    //不超过10次的重复操作,以保证写入成功{ret = HAL_FLASH_Program(0, addr+j*8, *(pdata64+j));if(ret == HAL_OK){read = *(__IO uint64_t *)(addr+j*8);  //在MCU认为写入正确以后,再次读取一下数据,并进行比对,如果比对不成功,也说明写入出错if(read != *(pdata64+j))ret = 255;return ret;}}}if(ret != 0)led_err_flash(10, 100);   //擦除失败后闪灯提示, 供调试阶段使用, 正式使用时可删除HAL_FLASH_Lock();           //上锁,以结束FLASH操作return ret;
}

封装后的测试函数

u64 data64[32]={0};
u8 g_text_buf[64] = {"LIJIA000LIJIA001LIJIA002LIJIA003LIJIA004LIJIA005LIJIA006LIJIA007"};if(GET_KEY() == 0){HAL_Delay(200);LL_flash_read(0x08010000, data64, 4);if(data64[0] == 0xffffffffffffffff)       //如果是空的,则写入{LED1(1);LED2(1);if(LL_flash_write(0x08010000, (u64*)g_text_buf, 4) != 0)continue;}else                                      //如果为非空,则擦除{LL_flash_erase_page(32, 1, 0);    //第32页,长度1页,BANK 0}LL_flash_read(0x08010000, data64, 4);while(GET_KEY() == 0);LED1(0);LED2(0);i++;}

测试结果如下图,如果写入成功,LED灯会在操作之后灭掉,如果写入失败,则LED灯会保持点亮状态,
在这里插入图片描述

总结

绝对不能只写入一次,就认为写入正确,恰恰相反,最开始的2次写入,极有可能写入失败。
所以必须重复写入几次,并且增加校验,即写入完成后,再读取数据,并进行比较,以确保正确。

相关文章:

嵌入式开发--STM32G4系列片上FLASH的读写

这个玩意吧&#xff0c;说起来很简单&#xff0c;就是几行代码的事&#xff0c;但楞是折腾了我大半天时间才搞定。原因后面说&#xff0c;先看代码吧&#xff1a; 读操作 读操作很简单&#xff0c;以32位方式读取的时候是这样的&#xff1a; data *(__IO uint32_t *)(0x080…...

嵌入式-Stm32-江科大基于标准库的GPIO的八种模式

文章目录 一&#xff1a;GPIO输入输出原理二&#xff1a;GPIO基本结构三&#xff1a;GPIO位结构四&#xff1a;GPIO的八种模式道友&#xff1a;相信别人&#xff0c;更要一百倍地相信自己。 &#xff08;推荐先看文章&#xff1a;《 嵌入式-32单片机-GPIO推挽输出和开漏输出》…...

2024年1月17日Arxiv热门NLP大模型论文:THE FAISS LIBRARY

Meta革新搜索技术&#xff01;提出Faiss库引领向量数据库性能飞跃 引言&#xff1a;向量数据库的兴起与发展 随着人工智能应用的迅速增长&#xff0c;需要存储和索引的嵌入向量&#xff08;embeddings&#xff09;数量也在急剧增加。嵌入向量是由神经网络生成的向量表示&…...

深度解析JVM类加载器与双亲委派模型

概述 Java虚拟机&#xff08;JVM&#xff09;是Java程序运行的核心&#xff0c;其中类加载器和双亲委派模型是JVM的重要组成部分。本文将深入讨论这两个概念&#xff0c;并解释它们在实际开发中的应用。 1. 什么是类加载器&#xff1f; 类加载器是JVM的一部分&#xff0c;负…...

前端下载文件流,设置返回值类型responseType:‘blob‘无效的问题

前言&#xff1a; 本是一个非常简单的请求&#xff0c;即是下载文件。通常的做法如下&#xff1a; 1.前端通过Vue Axios向后端请求&#xff0c;同时在请求中设置响应体为Blob格式。 2.后端相应前端的请求&#xff0c;同时返回Blob格式的文件给到前端&#xff08;如果没有步骤…...

C++核心编程——类和对象(一)

本专栏记录C学习过程包括C基础以及数据结构和算法&#xff0c;其中第一部分计划时间一个月&#xff0c;主要跟着黑马视频教程&#xff0c;学习路线如下&#xff0c;不定时更新&#xff0c;欢迎关注。 当前章节处于&#xff1a; ---------第1阶段-C基础入门 ---------第2阶段实战…...

脱模斜度是什么意思,为什么要有脱模斜度,没有斜度不行吗?

问题描述&#xff1a;脱模斜度是什么意思&#xff0c;为什么要有脱模斜度&#xff0c;没有斜度不行吗&#xff1f; 问题解答&#xff1a; 脱模斜度是指在模具中的零件在脱模&#xff08;从模具中取出&#xff09;过程中相对于模具开合方向的倾斜程度。在模具设计和制造中&…...

【现代密码学】笔记9-10.3-- 公钥(非对称加密)、混合加密理论《introduction to modern cryphtography》

【现代密码学】笔记9-10.3-- 公钥&#xff08;非对称加密&#xff09;、混合加密理论《introduction to modern cryphtography》 写在最前面8.1 公钥加密理论随机预言机模型&#xff08;Random Oracle Model&#xff0c;ROM&#xff09; 写在最前面 主要在 哈工大密码学课程 张…...

牛客-寻找第K大、LeetCode215. 数组中的第K个最大元素【中等】

文章目录 前言牛客-寻找第K大、LeetCode215. 数组中的第K个最大元素【中等】题目及类型思路思路1&#xff1a;大顶堆思路2&#xff1a;快排二分随机基准点 前言 博主所有博客文件目录索引&#xff1a;博客目录索引(持续更新) 牛客-寻找第K大、LeetCode215. 数组中的第K个最大元…...

MySQL的各种日志

目录 一、错误日志 二、二进制日志 1、介绍 2、作用 3、相关信息 4、日志格式 5、查看二进制文件 6、二进制日志文件删除 三、查询日志 四、慢日志 一、错误日志 记录MySQL在启动和停止时&#xff0c;以及服务器运行过程中发生的严重错误的相关信息&#xff0c;当数据库…...

rust跟我学六:虚拟机检测

图为RUST吉祥物 大家好,我是get_local_info作者带剑书生,这里用一篇文章讲解get_local_info是怎么检测是否在虚拟机里运行的。 首先,先要了解get_local_info是什么? get_local_info是一个获取linux系统信息的rust三方库,并提供一些常用功能,目前版本0.2.4。详细介绍地址:…...

测试bug分析

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 例如&#xff1a;项目场景&#xff1a;示例:通过蓝牙芯片(HC-05)与手机 APP 通信&#xff0c;每隔 5s 传输一批传感器数据(不是很大) 问题描述 提示&#xff1a;这里描述项目中遇到的问题&#xff1…...

css-盒子等样式学习

盒子居中&#xff0c;继承外层盒子的宽高 兼容性&#xff08;border-box&#xff09;将边框收到盒子内部 初始化div 不用管box-setting content-box 还原 创建为一个类 &#xff0c;让所有需要还原的类 进行继承 padding 用法表示margin上下左右边距 body 外边距&…...

数据库系统概论 第1章绪论 1.1数据库的四个基本概念

1.1.1 数据库的4个基本概念 - 数据(Data) - 数据库(Database, DB) - 数据库管理系统(DataBase Management System, DBMS) - 数据库系统(DataBase System, DMS) 1. 数据 - 数据(Data)是数据库中存储…...

使用Linux搭建svn

1.安装 Apache 和 Subversion 软件包 sudo yum install httpd subversion mod_dav_svn2.启动 Apache 服务 sudo systemctl start httpd3.设置 Apache 服务开机自启动 sudo systemctl enable httpd4.创建/svn 目录 sudo mkdir /svn5.设置 /svn 目录的权限&#xff1a; sudo…...

Kafka的安装、管理和配置

Kafka的安装、管理和配置 1.Kafka安装 官网: https://kafka.apache.org/downloads 下载安装包,我这里下载的是https://archive.apache.org/dist/kafka/3.3.1/kafka_2.13-3.3.1.tgz Kafka是Java生态圈下的一员&#xff0c;用Scala编写&#xff0c;运行在Java虚拟机上&#xf…...

某银行主机安全运营体系建设实践

随着商业银行业务的发展&#xff0c;主机规模持续增长&#xff0c;给安全团队运营工作带来极大挑战&#xff0c;传统的运营手段已经无法适应业务规模的快速发展&#xff0c;主要体现在主机资产数量多、类型复杂&#xff0c;安全团队难以对全量资产进行及时有效的梳理、管理&…...

虚拟化技术、Docker、K8s笔记总结

一、虚拟化技术 是一种将物理资源&#xff08;如服务器、存储设备、网络设备等&#xff09;抽象、转换和分割成多个逻辑资源的技术。通过虚拟化技术&#xff0c;用户可以在单个物理设备上运行多个相互独立的虚拟环境&#xff0c;从而提高资源的利用率、降低运维成本和提高系统…...

基于springboot+vue的在线拍卖系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…...

【征服redis3】一文征服redis的jedis客户端

使用数据库的时候&#xff0c;我们可以用JDBC来实现mysql数据库与java程序之间的通信&#xff0c;为了提高通信效率&#xff0c;我们有了数据库连接池比如druid等等。而我们想通过Java程序控制redis&#xff0c;同样可以借助一些工具来实现&#xff0c;这就是redis客户端&#…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...