STM32标准库FLASH读写

1. STM32内部FLASH介绍

STM32系列一般集成有内部flash,这部分内存可以直接通过指针的形式进行读取。但是由于内部flash一般存储为重要数据或程序运行数据,在进行写入或擦除时需要使用库函数的解锁和上锁函数进行读写。在STM32中,内部flash大致可以分为下图的几部分:

图1.1.STM32内部flash示意图

1.1. 主存储器

这部分为正常存储区,负责存储片上程序,若片上存储区充足,则可将某些需要掉电保存的数据存储至空闲区段。

判断片上程序截止位置可以通过查看项目的map文件来查看,具体方法为

双击keil左侧的项目文件夹打开项目map文件:

图1.2.map文件寻找

在map文件中找到Memory Map of the image这个表:

图1.3.内存加载表

在这个表的最后一行找到这么一段:

图1.4.内存占用位置

其中的load base的值就是片上程序的截止位置,从这里到片上flash的寻址结束都是可以用作数据存储的空闲空间。

1.2. 系统存储区

这部分为固定数据,不可修改擦除。所存储数据为芯片厂家出厂时保存的数据,用于ISP烧录功能等,相当于linux系统中的MMU,进行内存寻址等功能。

1.2. 选项字节

该区域用于配置片上flash的读写保护、待机/停机复位、软件硬件看门狗等功能,可以通过修改FLASH的的选项控制寄存器修改。

2. FLASH存储常见问题

在常见的FLASH芯片以及芯片片上FLASH中通常会仅允许FLASH页擦除,word写入或halfword写入。

页,是FLASH芯片中存储数据的最小单位。在某些芯片的资料中,也可能扇区是最小单位,

扇区与页的大小要根据芯片厂家资料的具体划分来看。FLASH的擦除操作最小单位只能是页,而写入单位为word或halfword,读取方式为以指针形式读取。这就出现了第一个矛盾:擦除量远大于写入量,而且由于FLASH是分页存储,若每次写入都擦除的话会导致上次的写入丢失。因此我的建议是给页或程序需要保存的数据建立一个页缓冲区,每次写入前先将数据写入缓冲区,然后一次性将缓冲区内的数据写入FLASH。可以减少数据丢失的问题。

FLASH是分页存储的,这就引发了第二个矛盾,若有一个数据横跨两个页面怎么读取写入,FLASH官方禁止跨页读写,且数据严格要求对齐。即在32位计算机中,若最小写入单位为word,写入的地址就只能为4的倍数,若最小写入单位为halfword,写入的地址就只能为2的倍数。不符合倍数要求的数据无法进行正常写入

3. 具体函数编写思路

野火的代码洋洋洒洒一大篇,但是我感觉不适合初学者,所以我对其进行了简化。

首先需要进行头文件引入与预编译变量定义:

#include "stm32f10x_flash.h"
#include "math.h"
#define FLASH_PAGE_SIZE ((uint16_t)0x800)//2048
#define WRITER_START_ADDR ((uint32_t)0x08008000)
#define WRITER_END_ADDR ((uint32_t)0x0800c000)

其中FLASH_PAGE_SIZE为片上flash每一页的大小,我所使用的芯片为STM32F103VET6,属于大容量,每一页的大小为2048字节。这个变量在正常读写时不使用,但是大批量擦除内存时可以使用这个变量编写函数。

引用math.h是为了使用floor函数,用于向下取整。

3.1. 写入函数

void flash_write_word(uint32_t data,uint32_t addr)//data变量为32个二进制变量的数据,addr为地址偏移量,我认为在程序编写时对偏移量修改,好过记一大串的地址。
{
uint32_t addr_true = addr + WRITER_START_ADDR;//addr_true为起始地址+地址偏移量,为数据在flash的实际地址。
uint32_t erase_page = floorf((addr + WRITER_START_ADDR) / FLASH_PAGE_SIZE);//这里是为了得到要擦除的第几页,floorf函数用于向下取整。
FLASH_Unlock();//解锁flash。
FLASH_ErasePage(WRITER_START_ADDR+(erase_page*FLASH_PAGE_SIZE));//擦除实际地址所在页面的flash。
FLASH_ProgramWord(addr_true,data);//向实际地址写入数据,在实际应用中还会遇到halfword写入的情况,在这里不进行讨论。
FLASH_Lock();//锁定flash。
}

3.2. 读取函数

uint32_t flash_read_word(uint32_t addr)//addr为地址偏移量,返回的值为读取到的数据。
{
uint32_t addr_true = addr + WRITER_START_ADDR;//addr_true为起始地址+地址偏移量,为数据在flash的实际地址。
return (*(__IO uint32_t*)addr_true);//将addr_true变量指针化,得到flahs地址存储的数据。
}

(__IO uint32_t)addr_true 是C或C++代码中的一个类型转换和解引用操作。我们可以将其分解为几个部分来理解其含义:

__IO:这是一个宏定义,通常在嵌入式编程中使用,特别是在与硬件寄存器交互时。__IO通常表示“输入/输出”,意味着该变量或地址可以读写。在某些嵌入式系统中,为了优化性能,开发者可能会区分“只读”和“可读/写”的内存区域,并使用宏来标记它们。

uint32_t:这是一个无符号32位整数类型,定义在stdint.h或cstdint头文件中。它确保变量或指针指向的内存区域被解释为32位无符号整数。

(uint32_t)addr_true:这是一个类型转换(或称为类型转换)。它将addr_true(可能是一个void类型的指针或其他类型的指针)转换为指向uint32_t类型的指针。

:这是一个解引用操作。当应用于指针时,它会获取该指针指向的值。

因此,
(__IO uint32_t*)addr_true 的整体意思是:首先,将addr_true转换为一个指向uint32_t类型的指针(并确保该内存区域是可读/写的),然后获取该指针指向的32位无符号整数值。

这种操作在嵌入式编程中非常常见,特别是当需要直接访问或修改硬件寄存器的值时。

STM32标准库内部Flash读写的更多相关文章

  1. STM32 对内部FLASH读写接口函数(转)

    源:STM32 对内部FLASH读写接口函数 因为要用内部FLASH代替外部EEPROM,把参数放在STM32的0x08000000+320K处,其中20K是bootloader,300K是应用程序. ...

  2. 单片机stm32零基础入门之--初识STM32 标准库

    CMSIS 标准及库层次关系 因为基于Cortex 系列芯片采用的内核都是相同的,区别主要为核外的片上外设的差异,这些差异却导致软件在同内核,不同外设的芯片上移植困难.为了解决不同的芯片厂商生产的Co ...

  3. STM32 标准库

    CMSIS 标准及库层次关系 因为基于Cortex 系列芯片采用的内核都是相同的,区别主要为核外的片上外设的差异,这些差异却导致软件在同内核,不同外设的芯片上移植困难.为了解决不同的芯片厂商生产的Co ...

  4. STM32标准库GPIO操作

    STM32标准库GPIO操作 STM32任何外围设备的使用都分为两部分:初始化和使用.体现在代码上就是:(1)有一个初始化函数(2)main函数中的使用 1.初始化GPIO 初始化GPIO函数代码: ...

  5. 初识STM32标准库

    1.CMSIS 标准及库层次关系 CMSIS 标准中最主要的为 CMSIS 核心层,它包括了: STM32标准库可以从官网获得: 在使用库开发时,我们需要把 libraries 目录下的库函数文件添加 ...

  6. STM32 对内部FLASH读写接口函数

    因为要用内部FLASH代替外部EEPROM,把参数放在STM32的0x08000000+320K处,其中20K是bootloader,300K是应用程序. 原理:先要把整页FLASH的内容搬到RAM中 ...

  7. STM32 标准库V3.5启动文件startup_stm32f10xxx.s分析

    layout: post tags: [STM32] comments: true 文章目录 layout: post tags: [STM32] comments: true 前言 分析startu ...

  8. STM32 HAL库 UART 串口读写功能笔记

    https://www.cnblogs.com/Mysterious/p/4804188.html STM32L0 HAL库 UART 串口读写功能 串口发送功能: uint8_t TxData[10 ...

  9. [nRF51822] 11、基础实验代码解析大全 · 实验16 - 内部FLASH读写

     一.实验内容: 通过串口发送单个字符到NRF51822,NRF51822 接收到字符后将其写入到FLASH 的最后一页,之后将其读出并通过串口打印出数据. 二.nRF51822芯片内部flash知识 ...

  10. STM32 标准库3.5修改默认外部8M晶振为16M晶振

    ST官方标准库V3.5默认的外部晶振频率为8M,实际使用中外部晶振需要修改为16M: 经过实验,修改有效,具体的patch如下: 修改 HSE_VALUE 值 diff --git "a/L ...

随机推荐

  1. git创建分支出现 fatal: Not a valid object name: 'master'.

    今天使用git 创建新分支的时候 出现了一个错误 fatal: Not a valid object name: 'master'. 问题描述:一个非法的master,原因:本地还没有创建master ...

  2. golang: 学会几个语法上的新写法

    看了VictoriaMetrics,学会了几个新写法,记录下来: 1. 数组拷贝 以前: arr := make([]byte, 0, len(oldArr) arr = append(arr, ol ...

  3. 从零开始配置 vim(16)——启动界面配置

    不知道各位小伙伴用过 spacevim 或者 LunarVim 又或者 doomvim 或者其他的什么 vim 配置框架,我们发现他们的启动界面都做的比较好看,而我们默认进入的启动界面则显得比较素了. ...

  4. 14.2 Socket 反向远程命令行

    在本节,我们将继续深入探讨套接字通信技术,并介绍一种常见的用法,实现反向远程命令执行功能.对于安全从业者而言,经常需要在远程主机上执行命令并获取执行结果.本节将介绍如何利用 _popen() 函数来启 ...

  5. CE修改器入门:运用代码注入

    从本关开始,各位会初步接触到CE的反汇编功能,这也是CE最强大的功能之一.在第6关的时候我们说到指针的找法,用基址定位动态地址.但这一关不用指针也可以进行修改,即使对方是动态地址,且功能更加强大. 代 ...

  6. 优化算法之梯度下降|Matlab实现梯度下降算法

    题目要求: 使用Matab实现梯度下降法 对于函数: min ⁡ f ( x ) = 2 x 1 2 + 4 x 2 2 − 6 x 1 − 2 x 1 x 2 \min f(x)=2 x_{1}^{ ...

  7. InnoDB存储引擎的行级锁

    InnoDB存储引擎的行级锁 InnoDB存储引擎和MyISAM的其中有两个很重要的区别:一个是事务,一个就是锁机制不同.事务之前有介绍,有问题的去补课;锁方面的不同是InnoDB引擎既有表锁又有行锁 ...

  8. Java锁到底是个什么东西

    一.java锁存在的必要性 要认识java锁,就必须对2个前置概念有一个深刻的理解:多线程和共享资源. 对于程序来说,数据就是资源. 在单个线程操作数据时,或快或慢不存在什么问题,一个人你爱干什么干什 ...

  9. 《ASP.NET Core 与 RESTful API 开发实战》-- (第8章)-- 读书笔记(上)

    第 8 章 认证和安全 8.1 认证 认证(Authentication)是指验证用户身份的过程,授权(Authorization)是验证一个已经通过认证的用户是否有权限做某些事的过程 常见的 HTT ...

  10. .NET开源的一个小而快并且功能强大的 Windows 动态桌面软件 - DreamScene2

    前言 很多同学都不愿给电脑设动态壁纸,其中有个重要原因就是嫌它占资源过多.今天大姚分享一个.NET开源.免费(MIT license)的一个小而快并且功能强大的 Windows 动态桌面软件,支持视频 ...