嵌入式开发--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;
      else
        flash_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次写入,极有可能写入失败。
所以必须重复写入几次,并且增加校验,即写入完成后,再读取数据,并进行比较,以确保正确。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Grafana(二)Grafana 两种数据源图表展示(json-api与数据库)

一. 背景介绍 在先前的博客文章中&#xff0c;我们搭建了Grafana &#xff0c;它是一个开源的度量分析和可视化工具&#xff0c;可以通过将采集的数据分析、查询&#xff0c;然后进行可视化的展示&#xff0c;接下来我们重点介绍如何使用它来进行数据渲染图表展示 Docker安装G…

CSS Day10

10.1 2D位移 属性名&#xff1a;transform 属性值&#xff1a;translateX 水平方向的位移 相对于自身位置移动 translateY 垂直方向的位移 相对于自身位置移动 transform&#xff1a;translate(x,y); 位移和定位搭配使用&#xff1a; position:absolute; top:50%; left:50%; tr…

HCIP-BGP实验2

一.实验要求 全网可达&#xff01; 二.实验步骤 1.配置好所有环回和路由的ip R1 [r1]dis ip int brief Interface IP Address/Mask Physical Protocol GigabitEthernet0/0/0 12.1.1.1/24 up up …

取消lodash.throttle中的默认执行完最后一次函数

我的场景: 我有一个列表,我考虑用户连续点击删除的情况&#xff0c;如果用户连续点击&#xff0c;可能会导致数据库中的数据被删除了&#xff0c;但是我还需要刷新数据列表才能反应到页面上&#xff0c;可是这时候用户又点击了同一条数据的删除按钮多次&#xff0c;导致发起了…

QT quick基础:组件gridview

组件gridview与android中gridview布局效果相同。 一、下面记录qt quick该组件的使用方法。 方法一&#xff1a; // ContactModel.qml import QtQuick 2.0ListModel {ListElement {name: "1"portrait: "icons/ic_find.png"}ListElement {name: "2&quo…

《Kali渗透基础》16. 密码攻击

kali渗透 1&#xff1a;身份认证2&#xff1a;密码破解2.1&#xff1a;应用场景2.2&#xff1a;方法简介 3&#xff1a;字典生成工具3.1&#xff1a;Crunch3.2&#xff1a;CUPP3.3&#xff1a;Cewl3.4&#xff1a;JTR 4&#xff1a;密码破解工具4.1&#xff1a;在线密码破解4.1…

AIOps探索 | 基于大模型构建高效的运维知识及智能问答平台(2)

前面分享了平台对运维效率提升的重要性和挑战以及基于大模型的平台建设解决方案&#xff0c;新来的朋友点这里&#xff0c;一键回看精彩原文。 基于大模型构建高效的运维知识及智能问答平台&#xff08;1&#xff09;https://mp.csdn.net/mp_blog/creation/editor/135223109 …

【vscode】6、调试 shell

文章目录 经常在 IDE 下使用 高级语言后&#xff0c;往往并不习惯 shell 编程&#xff0c;因为没有酷炫的界面。但现在 vscode 可以很方便的调试 shell 脚本。 配置方法如下&#xff1a; vscode 下载 Bash Debug 插件 mac 升级 bash 版本&#xff08;因为此 vscode 插件需要 b…

腾讯云的域名使用阿里云服务器配置

因为近期云服务器到期了&#xff0c;之前的域名已经完成了备案不想轻易回收。于是就换了个厂商&#xff0c;从腾讯云换到了阿里云。但是因为两个厂商不互通。我又不想把域名转入到阿里云。所以就开启了配置之路&#xff0c;一路磕磕绊绊。给大家整理一份顺序&#xff0c;一步到…

深入解析:如何使用Java、SpringBoot、Vue.js和MySQL构建课表管理系统

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

Jira 宣布Data Center版涨价5%-15%,6年内第8次提价

近日&#xff0c;Atlassian官方面向合作伙伴发布2024年涨价通知&#xff1a; 自2024年2月15日起&#xff0c;旗下核心产品Jira Software、Confluence、Jira Service Management的DC版本&#xff08;Data Center版本&#xff09;价格提高5%-15%&#xff08;涨幅与坐席数阶梯相关…

Pod控制器:

Pod控制器&#xff1a; Pv pvc 动态PV Pod控制器&#xff1a;工作负载。WordLoad&#xff0c;用于管理pod的中间层 &#xff0c;确保pod资源符合预期的状态 预期状态&#xff1a; 副本数容器的重启策略镜像的拉取策略 Pod出现故障时的重启等等 Pod控制器的类型&#xff1a…

[足式机器人]Part2 Dr. CAN学习笔记-Advanced控制理论 Ch04-17 串讲

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-Advanced控制理论 Ch04-17 串讲

uniapp + node.js 开发问卷调查小程序

前后端效果图 后端&#xff1a;nodejs 12.8 ; mongoDB 4.0 前端&#xff1a;uniapp 开发工具&#xff1a;HBuilderX 3.99 前端首页代码 index.vue <!-- 源码下载地址 https://pan.baidu.com/s/1AVB71AjEX06wpc4wbcV_tQ?pwdl9zp --><template><view class&q…

ubuntu系统(10):使用samba共享linux主机中文件

目录 一、samba安装步骤 1、Linux主机端操作 &#xff08;1&#xff09;安装sabma &#xff08;2&#xff09;修改samba配置文件 &#xff08;3&#xff09;为user_name用户设置samba访问的密码 &#xff08;4&#xff09;重启samba服务 2、Windows端 二、使用 1、代码…

CMake TcpServer项目 生成静态库.a / 动态库.so

CMake 实战构建TcpServer项目 静态库/动态库-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/135608829?spm1001.2014.3001.5501 在这篇博客的基础上&#xff0c;我们把头文件放在include里边&#xff0c;把源文件放在src里边&#xff0c;重新构建 hehedali…

网页设计(一)开发环境配置与HTML基础

一、使用HBuilderX创建项目和文件 加粗样式HX创建HTML文档的模板位置 template.html模板内容 1.创建HTML项目 创建项目引导界面 新建项目窗口 Web-2009030199项目所在位置图 2.在项目下新建prj_1_2.html文件 创建文件引导界面 新建html文件界面 3.新建prj_1_2.ht…

什么是DNS(域名系统)

域名系统&#xff08;DNS&#xff09;像是互联网的电话簿&#xff0c;将人们容易记住的网址转换成计算机能理解的IP地址。 没有DNS&#xff0c;我们就需要记住复杂的数字序列来访问网站 DNS的基本概念 定义&#xff1a;DNS是一个分布式数据库&#xff0c;它将域名&#xff08…

智能小程序小部件(Widget)导航、地图、画布等组件,以及开放能力、原生组件说明

智能小程序小部件(Widget)导航、地图、画布等组件&#xff0c;以及开放能力、原生组件说明。 导航组件 navigator 页面链接&#xff0c;控制小程序的跳转。navigator 子节点的背景色应为透明色。 属性说明 属性名类型默认值必填说明urlstring是跳转地址deltanumber1否当 …

【开发实践】前端jQuery+gif图片实现载入界面

一、需求分析 载入界面&#xff08;Loading screen&#xff09;是指在计算机程序或电子游戏中&#xff0c;当用户启动应用程序或切换到新的场景时&#xff0c;显示在屏幕上的过渡界面。它的主要作用是向用户传达程序正在加载或准备就绪的信息&#xff0c;以及提供一种视觉上的反…