所有教程由网友发布,仅供参考,请谨慎采纳。科创不对教程的科学性、准确性、可靠性负责。
STM32L4内部Flash双字节对齐读写短字符串
全桥整流2025/01/08原创 秋名山最速传说 IP:广东
中文摘要
本文介绍了在STM32L4系列MCU中使用内部Flash存储数据的方法及排查问题的过程。主要内容包括:Flash页面大小与写入规则的差异说明,选择Flash最后一页存储少量数据以降低对代码存放的干扰,分析了因内存未对齐导致Hard Fault的问题并定位到STRD指令无法支持非对齐访问,通过对齐操作解决问题。同时,展示了Flash擦除、写入和读取的核心代码,并通过示例验证了方法的可行性。
Abstract
This article introduces the method of using internal Flash for data storage in STM32L4 series MCUs and the process of troubleshooting issues. The main content includes an explanation of differences in Flash page size and writing rules, selecting the last page of Flash to store small amounts of data to minimize interference with code storage, analyzing a Hard Fault caused by unaligned memory access and pinpointing the issue to the STRD instruction's inability to support unaligned access, and resolving it through alignment operations. Additionally, the core code for Flash erasure, writing, and reading is demonstrated, and the feasibility of the method is verified through examples.

前言:

为什么要用内部Flash呢?当然是无需增加外围器件啦,存点信息挺方便的。我这里存储少量信息,不涉及换页操作(单次写入小于页大小),并且写入频次不高。本来以为这个文章挺快能完成的,短字符串的读操作中一个handles Hard fault interrupt给我整懵了,增加字符串长度这个问题就没有了,花了点时间处理这个问题(排查出来是内存没对齐,M4内核支持非对齐访问,但会造成代码效率下降,并且要手动改寄存器SCB->CCR,同时并不是所有指令都支持非对齐访问,比如我下文的STRD(存储双字)指令就不支持非对齐访问,像LDR(加载),STR(存储)就可以非对齐访问)。


正文:

1、如下图1,STM32L4(我使用MCU的Flash是256K的)的Flash的页大小是2K的,不是F1的1K这个要注意,并且不支持单字写入得双字写入。同时可以注意到Bank1和Bank2的Name不是连续的,但这个不连续的Name不影响我们的程序,我认为这里有点误导的,实际使用是连续的page name。如下图2是FLASH Erase structure definition,如下图3是实际上传入的page的范围。

image.png

image.png

image.png


2、计划使用Flash最后的一页存储信息,这个区域平时读写比较少,也不干涉到程序代码存放的问题。STM32L系列的Flash擦除寿命大约是10K次擦除,不是频繁写是够用了,一般来说擦除才消耗寿命,读几乎不消耗寿命,注意不能把擦除操作放在没有delay的while里面反复执行。


3、分析了一波,就开始代码编写了,代码后面再展示,这里我重点说如何排错的。

如前言所说,短字符串(我测试发现1,2,4,6个字符的字符串会卡住,长字符串不会卡住,推测长短字符串使用AC5编译的时候对齐规则不一样)的读操作中转跳进handles Hard fault interrupt。

首先我们得知道是从哪里跳进来的。先看看寄存器(如下图1),LR的值为0xFFFFFFF9,代表从异常返回到线程模式(具体看M4权威指南,下图2是个简化的表格),并使用主堆栈指针MSP。

然后读取MSP寄存器的值,依据:R0~R3、R12、LR、PC、XPRS 顺序,找到LR(和前文的LR不一样)的值,即第6个寄存器值。这个值代表发生异常后调用的下一条指令的地址。然后再追查回上一条函数是Flash_Read函数,定位问题出现在Flash_Read函数,同时调出Fault Reports注意到UNALIGNED被置位,代表内存非对齐访问。

我们再转跳PC地址(即第7个寄存器值),对应的汇编语句就是发生错误的语句(如下图3),可以看到发生错误的是STRD指令,涉及内存读写,结合UNALIGNED被置位,基本可以确信是该汇编语句对应的.c语句出现问题。下图4的方法据其他技术文章说可以,但我实际上发现不行,下图4的方法更快,可以优先尝试一下(当然,也可以通过大量断点来定位问题,只不过上面提到的方法更快一点)。

image.png

image.png

image.png

image.png


4、既然定位到问题是内存没对齐导致的,那我们在初始化的时候就把他对齐,使用__align(num)(我感觉下面代码有些对齐不是必要的)。  

__align(8) uint8_t TEXT_Buffer[]={"KCer"};//要写入到STM32 FLASH的字符串数组
#define SIZE sizeof(TEXT_Buffer)	 	//数组长度
__align(8) uint8_t datatemp[SIZE];  //Flash读取缓存数组,对齐
__align(8) uint8_t *p=datatemp;	    //数组指针,对齐


5、效果展示

image.png


6、主干代码展示

Use_flash.h

#ifndef _USE_FLASH_H_
#define _USE_FLASH_H_
#include "main.h"
#include "stm32l4xx_hal_flash_ex.h"
 
void Flash_Erase(void); 
void Flash_Write(uint64_t *pBuffer,uint16_t NumToWrite);
void Flash_Read(uint64_t *pBuffer,uint16_t NumToRead);
 
#endif

Use_flash.c

#include "Use_flash.h"
#include "stdio.h"
#include "string.h"
 
#define STM32_FLASH_SIZE 	256 	 	//所选STM32的FLASH容量大小(单位为K),256K以下的产品是1K一个page,256及以上是2K一个page
    #if     STM32_FLASH_SIZE < 256      //设置扇区大小
    #define STM_SECTOR_SIZE     1024    //1K字节
    #else 
    #define STM_SECTOR_SIZE	    2048    //2K字节
#endif

#define STM32_FLASH_BASE            0x08000000 		//STM32 FLASH的起始地址
#define FLASH_USER_START_ADDR   ( STM32_FLASH_BASE + STM_SECTOR_SIZE * 126 ) //写Flash的地址,这里从第126页开始
#define FLASH_USER_END_ADDR     ( STM32_FLASH_BASE + STM_SECTOR_SIZE * 127 ) //写Flash的地址,这里以第127页结束
uint32_t PAGEError = 0;
static FLASH_EraseInitTypeDef EraseInitStruct;
 /**********************************************************************************
  * 函数功能: 页擦除
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无 
  */
void Flash_Erase(void)
{  	
	EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
	EraseInitStruct.Page   =      126;//从126页开始
	EraseInitStruct.NbPages     = 1;//只擦除1页
	EraseInitStruct.Banks = FLASH_BANK_1;
	if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)  
 {
	 HAL_FLASH_Lock();  
	 printf(" Erase error\r\n");
	 Error_Handler( );
 }
}
 /**********************************************************************************
  * 函数功能: 数据写入
  * 输入参数: 写入数据缓存数组指针、写入数据数
  * 返 回 值: 无
  * 说    明:无 
  */    
void Flash_Write(uint64_t *pBuffer,uint16_t  NumToWrite)
{
        uint16_t  i=0;
	uint32_t Address = FLASH_USER_START_ADDR;
	HAL_FLASH_Unlock();	    //解锁
	Flash_Erase( );         //先擦除 再写入
	printf(" 擦除完成,准备写入......\r\n");
	while ( (Address < FLASH_USER_END_ADDR) && (i<NumToWrite)  )    
	{
		if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, Address, pBuffer[i]) == HAL_OK)
		{
			Address = Address + 8;  //地址后移8个字节
			i++;	
		}
		else
		{  
			printf("Write error\r\n"); 
			Error_Handler( );            
		}
	}
	HAL_FLASH_Lock();   //上锁
}

 /**********************************************************************************
  * 函数功能: 数据读取
  * 输入参数: 读取数据缓存数组指针、读出数据数
  * 返 回 值: 无
  * 说    明:无
  */
void Flash_Read(uint64_t  *pBuffer,uint16_t  NumToRead)
{
   uint16_t  i=0;
   uint64_t temp;
   uint32_t Address = FLASH_USER_START_ADDR;
   while ( (Address < FLASH_USER_END_ADDR) && (i<NumToRead)  )
  {
    temp = *(__IO uint64_t *)Address; // 从 FLASH 读取数据
    memcpy(&pBuffer[i++], &temp, sizeof(uint64_t)); // 使用 memcpy 
    Address = Address + 8;   //地址后移8个字节
  }
}           

main.c主干代码

#include "stdio.h"
#include "string.h"
#include "Use_flash.h"
.
.
.
.
.
.
Flash_Write((uint64_t*)TEXT_Buffer,(SIZE+7)/8);  //强制转换后写入数组长度向上取整,双字节
printf(" 写入成功,准备读取......\r\n");
Flash_Read((uint64_t*)datatemp,(SIZE+7)/8);
printf(" 读取成功......写入内容为:\r\n");
printf("%s\r\n、",p);


结束语:

写文章还是有意思的,要整理输出文字不想误导读者,因此对程序进行鲁棒性测试,修复暴露出来的问题。回忆大学时光,如果按照个人成果产出的重要性排序,于我而言个人博客(技术文章)无疑是排第一的,远超论文和比赛获奖。


参考:

STM32L431内部flash读写参考代码_stm32l431 flash读写-CSDN博客

STM32L4系列内部FLASH双字编程示例 - STM32团队 ST意法半导体中文论坛

STM32系列(HAL库)——内部FLASH读写实验_简约版_stm32 hal flash读写-CSDN博客

解决STM32因字节对齐问题导致读写Flash失败进入HardFault的问题_flash进行搬移操作,固定位置错误-CSDN博客

Cortex-M 处理器 hardfault 定位方法和步骤(基于Keil mdk) - 薛定谔我的猫 - 博客园

stm32中字节对齐问题(__align(n),__packed用法) - King先生 - 博客园


ps:欢迎各位提建议和意见,回复均有KCB


[修改于 16时47分前 - 2025/01/08 11:29:12]

来自:电子信息 / 电子技术严肃内容:教程/课程
0
 
1
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
全桥整流
进士 学者 机友 笔友
文章
28
回复
283
学术分
1
2020/01/26注册,7时18分前活动

秋名山最速传说!

主体类型:个人
所属领域:无
认证方式:身份证号
IP归属地:广东
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}