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

2024-01-21

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

读操作

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式开发--STM32G4系列片上FLASH的读写 的相关文章

  • 小白刷题之图形输出

    拓展 string string int num char ch num表示打印字符个数 ch表示打印内容 include
  • Python自动化操作:简单、有趣、高效!解放你的工作流程!

    今天跟大家分享一套自动化操作流程解决方案 基于 Python语言 涉及 pyautogui pyperclip pythoncom win32com 依赖包 安装命令为 pip install pyautogui pip install p
  • 怎么注册微商城?开启微商城之旅

    在这个数字化时代 微商城的出现为商家提供了一个全新的机会 商家企业可以通过微商城来展示和销售自己的产品 而对于一些商家而言 不知道怎么注册微商城 下面给大家做一个简单的分享 第一步 选择合适的微商城搭建工具 在注册微商城之前 首先需要选择一

随机推荐

  • 2024年网络安全十10大发展趋势发布

    2023年网络安全十10大发展趋势发布 近日 中国计算机学会 CCF 计算机安全专委会中 来自国家网络安全主管部门 高校 科研院所 大型央企 民营企业的委员投票评选出2023年网络安全十大发展趋势 福利 趋势一 数据安全治理成为数字经济的基
  • Windows7系统iprop.dll文件丢失问题

    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题 如果是新手第一时间会认为是软件或游戏出错了 其实并不是这样 其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库 这时你可以下载这个iprop d
  • 白帽子如何快速挖到人生的第一个漏洞 | 购物站点挖掘商城漏洞

    本文针对人群 很多朋友们接触安全都是通过书籍 网上流传的PDF 亦或是通过论坛里的文章 但可能经过了这样一段时间的学习 了解了一些常见漏洞的原理之后 对于漏洞挖掘还不是很清楚 甚至不明白如何下手 可能你通过 sql labs 初步掌握了sq
  • 图解python | 字符串及操作

    1 Python元组 Python的元组与列表类似 不同之处在于元组的元素不能修改 元组使用小括号 列表使用方括号 元组创建很简单 只需要在括号中添加元素 并使用逗号隔开即可 tup1 ByteDance ShowMeAI 1997 202
  • 每天10个前端小知识 <Day 5>

    前端面试基础知识题 1 typeof 与 instanceof 有什么区别 typeof与instanceof都是判断数据类型的方法 区别如下 typeof会返回一个变量的基本类型 instanceof返回的是一个布尔值 instanceo
  • 2024年华数杯国际赛B题:光伏发电功率 思路模型代码解析

    2024年华数杯国际赛B题 光伏发电功率 Photovoltaic Power 一 问题描述 中国的电力构成包括传统能源发电 如煤 油和天然气 可再生能源发电 如水电 风能 太阳能和核能 以及其他形式的电力 这些发电模式在满足中国对电力的巨
  • iprtrmgr.dll文件丢失导致程序无法运行问题

    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题 如果是新手第一时间会认为是软件或游戏出错了 其实并不是这样 其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库 这时你可以下载这个iprtrmg
  • 前端基础:回顾es6相关知识

    Author note 题记 ECMAscript is international standard of javascript ECMA 是 js的国际标准版语言 let and const 为什么之前用var现在需要用let cons
  • iPhone16或全系升级8GB内存,支持Wi-Fi 6E!

    随着新的一年到来 苹果下一代机型iPhone 16系列的爆料也越来越充实 越来越详细 从多个爆料中 我们甚至已经都够想象出下一代iPhone的基本雏形 海通国际技术分析师Jeff Pu 为我们带来了新的内容 这位分析师称 iPhone 16
  • Jenkins 插件下载速度慢、安装失败了!我教你怎么解决!

    Jenkins部署完毕 如果不安装插件的话 那它就是一个光杆司令 啥事也做不了 所以首先要登陆管理员账号然后点击系统管理再点击右边的插件管理安装CI CD必要插件 但是问题来了 jenkins下载插件速度非常慢 而且经常提示下载插件失败 真
  • ir50_32.dll文件丢失导致程序无法运行问题

    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题 如果是新手第一时间会认为是软件或游戏出错了 其实并不是这样 其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库 这时你可以下载这个ir50 32
  • 【固定翼飞机】基于最优控制的固定翼飞机着陆控制器设计研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码及文章
  • 深度好文:最全的大模型 RAG 技术概览

    本文是对检索增强生成 Retrieval Augmented Generation RAG 技术和算法的全面研究 对各种方法进行了系统性的梳理 涉及了 RAG 流程中的数据拆分 向量化 查询重写 查询路由等等 在做 RAG 的小伙伴一定知道
  • messages,CentOS 7不收集日志或不存在 /var/log/messages

    var log message var log secure等都不记录了 并且都是空文件 重启机器 reboot 无效 重启日志 systemctl start rsyslog 无效 怀疑空间不足 删除 var log messages 重
  • 图解python | 基础数据类型

    1 Python变量类型 Python基本数据类型一般分为6种 数值 Numbers 字符串 String 列表 List 元组 Tuple 字典 Dictionary 集合 Set 本文详细讲解Python中变量赋值 数据类型以及数据类型
  • 2024年金三银四网络安全考试试题

    2023年金三银四网络安全考试试题 1 关于数据使用说法错误的是 A 在知识分享 案例中如涉及客户网络数据 应取敏感化 不得直接使用 B 在公开场合 公共媒体等谈论 传播或发布客户网络中的数据 需获得客户书面授权或取敏感化 公开渠道获得的除
  • 外包干了3个月,技术退步明显。。。。。

    先说一下自己的情况 本科生 19年通过校招进入广州某软件公司 干了接近4年的功能测试 今年年初 感觉自己不能够在这样下去了 长时间呆在一个舒适的环境会让一个人堕落 而我已经在一个企业干了四年的功能测试 已经让我变得不思进取 谈了2年的女朋友
  • [C++]:11.模拟实现vector

    模拟实现vector 二 模拟实现vector 0 看一看源码SGI 1 vector h 2 stl vector h 1 构造 2 析构函数
  • 利用 LangChain 和 Neo4j 向量索引,构建一个RAG应用程序

    Neo4j 在5 11版本中将向量搜索功能完全集成到 Neo4j AuraDB 和 Neo4j 图数据库中 随后对 Neo4j 向量检索的全面支持也被集成到了 LangChain 库中 Neo4j 向量检索已成为检索增强生成 RAG 应用程
  • 嵌入式开发--STM32G4系列片上FLASH的读写

    这个玩意吧 说起来很简单 就是几行代码的事 但楞是折腾了我大半天时间才搞定 原因后面说 先看代码吧 读操作 读操作很简单 以32位方式读取的时候是这样的 data IO uint32 t 0x0800F000 需要注意的是 当以32位方式读