STM32基于Flash对结构体读写暨再认识结构体内存分配

article/2025/10/15 4:40:19

目录

 前言

认识结构体内存分布

模拟结构体搬运

实现FLASH的结构体读写


 前言

        记录本篇主要是记录我进行stm32学习中利用stm32的flash(闪存读写结构体的总结。

认识结构体内存分布


        结构体,主要要来存放我们的自定义类型的数据,结构体可以再次嵌套结构体不断套娃,从C++的角度上说,结构体内部还能嵌套别的类对象,以及结构体本身就是一个类,类方法和变量默认是public修饰。

        对于结构体的大小,也就是考虑其内部变量的对齐,对齐是指与有效对齐量对齐,有效对齐量是指变量或是结构体本身(结构体本身作为一个整体也要对齐)需要与自身对齐量与指定对齐量中较小的那个进行对齐。 

所谓对齐就是就内存所在地址%其数据大小=0

可写成以下公式

有效对齐量=MIN(自身对齐量,指定对齐量)

  • 结构体的自身对齐量是其数据成员最大的自身对齐量!
  • 若不进行设定,默认的指定对齐量是4个字节(在stm32中)。
  • 若是结构体内部有数组,那么数组的自身对齐量就是其数据类型的自身对齐量。
  • 若结构体内部嵌套了一个结构体或是一个类对象,那么被嵌套的结构体或者是类对象是自身对齐量为其本身结构体的所占空间大小。
  • 数据类型在内存的存放次序是小端,其中最低位所在字节存放在较低的起始地址,最高位所在字节存放在较高的结束地址。

typedef struct {  //需要写入flash的信息u8 lightlimit;u8 CID[8];u8 UID[8];u16 poisition;
} FlashInfo;int main() {u16 arr[5] = { 0 };M test = { 1,2,3 };FlashInfo q = { 0,"\0","\0",1 };printf("%d\r\n", sizeof(FlashInfo));}

VS调试输出20 。 

         通过debug看内存:结构体的首地址为0x00d3fd5c,于是结构体自上而下第一个数据大小为一个字节,自身对齐量为1,VS默认指定对齐量为8,有效对齐值=MIN(1,8)=1,q.CID是一个u8类型的数组,其自身对齐值为数组成员的自身对齐值也就是1,所以有效对齐值为1,所以其内存分布在0x00d3fd5d-0x00d3fd64(共3+5=8个字节),同理q.UID分布在0x00d3fd65-0x00d3fd6c(8个字节),q.position的数据类型为u16占2个字节,其有效对齐量=MIN(2,8)=2个字节,而0x00d3fd6d%2!=0,所以偏移到0x00d3fd5c与其有效对齐量对齐。其内存分布为0x00d3fd6e-0x00d3fd6f(占2个字节),最终考虑结构体与其自身对齐,其有效对齐量=MIN(2(数据成员的最大自身对齐量,指定对齐量)=2, 内存分布为0x00d3fd5c到0x00d3fd6f,共20个字节,20%2=0,到此结构体不需要再往后偏移无用字节对齐。

再打开VS的内存

四种图案分别代表  lightlimit ,u8 CID[8] u8 UID[8] u16 poisition.箭头所指的CC为偏移的一个字节,CC是微软windows初始化时默认值可做守卫判断是否越界等等。

在此更进一步,指定对齐值是可以自己设定的,

#pragma pack(1)   // 1 bytes对齐

若是这样设置,那么结构体的大小就是成员数据的大小之和。

#pragma pack()  括号内空,默认指定对齐为8(vs中)

模拟结构体搬运

        好了,到这认识到位了,接下来就可以试着操作flash,如何操作,试着把结构体当做一个数组,把结构体的地址映射成数组的地址(一个字节,或者是2个字节都可以),为此,我在真正操作Flash前用vs写个测试代码。

        定义一个结构体,把结构体的地址映射成一个u8 类型的数组的地址,把结构体的数据取出放入另一个准备好的u8 类型的数组,再把数组地址映射成结构体地址再输出.

如下(设置数据和结构体的指定对齐量为1个字节,结构体大小就为子成员总和

#include "stdio.h"typedef unsigned short u16;
typedef unsigned char u8;
#pragma pack(1)
typedef struct {char a;int c;short b;} M;int main() {M test = { 1,2,3 };u8 arr[sizeof(M)] = { 0 };u8* p = (u8 *) & test;printf("结构体大小 %d", sizeof(M));for (int i = 0; i < sizeof(test); i++) {arr[i] = p[i];}M* test_arr = (M*)arr;//转换成结构体地址printf("[END}:%d %d %d\r\n", test_arr->a, test_arr->b, test_arr->c);return 0;
}

输出结果 

在此,我使用的开发板正点原子stm32F103ZET6精英版的开发文档中有写到:

于是把8位的u8,转换成u16,也就是一个字节到2个字节。

我这里又用在vs测试一下,由于我指定对齐量为1会导致结构体大小可能会是奇数。平时使用不需要设置指定对齐量,默认的指定对齐量是偶数,刚好是u16,2个字节的倍数。


#include "stdio.h"typedef unsigned short u16;
typedef unsigned char u8;
#pragma pack(1)
typedef struct {char a;int c;short b;} M;int main() {M test = { 1,2,3 };u16 arr[sizeof(M)%2==0?sizeof(M)/2: sizeof(M) / 2+1] = {0};//因为指定对齐量是1个字节,结构体\为奇数/2会模1,使得数组装不下。u16* p = (u16 *) & test;printf("结构体大小 %d", sizeof(M));for (int i = 0; i < sizeof(test)/2; i++) {arr[i] = p[i];}M* test_arr = (M*)arr;printf("[END}:%d %d %d\r\n", test_arr->a, test_arr->b, test_arr->c);return 0;
}

到这验证想法成功,试着操作STM32的FLASH。

stm32中指定对齐量是4个字节。

实现FLASH的结构体读写

定义结构体

 typedef struct {  //需要写入flash的信息
      u8 lightlimit; //温度阈值
      u8 CID[8];  //设备编号
      u8 UID[8];   //所绑定的RF卡号
      u16 poisition;   //位置编号
          
} FlashInfo;

FlashInfo info={9,"123456","987654",2};  //这是需要写入结构体内部的数据

 读结构体。(需要注意每读一次,地址向上增长2个字节)

 void ReadMyStruct(void){
     
     u16 *p=(u16*)&info;
     
     
     STMFLASH_Read(FLASH_COMMON_ADDR,p,sizeof(info)/2);
     printf("Test:%d--%s--%s--%d\r\n",info.lightlimit,(char * )info.CID,(char *)info.UID,info.poisition);
     
     
 }

void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)       
{
    u16 i;
    for(i=0;i<NumToRead;i++)
    {
        pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
        ReadAddr+=2;//偏移2个字节.    
    }
}

写结构体

先擦除所在页再写数据

 static  void WriteMyStruct(void){
         u16 *p=(u16*)&info;
       
            HAL_FLASH_Unlock(); //FLASH解锁
            FLASH_PageErase(FLASH_COMMON_ADDR); //擦除页
            FLASH_WaitForLastOperation(FLASH_WAITETIME);                //等待上次操作完成
            CLEAR_BIT(FLASH->CR, FLASH_CR_PER);        //清除标记
            u32 addr=FLASH_COMMON_ADDR;
            for(int i=0;i<sizeof(FlashInfo)/2;i++){ //以16位为一个字            
            HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,addr,p[i]);//写一个字(32位)数据,若是一个半字则写半字16位     
            addr+=2;
            }
                        HAL_FLASH_Lock();//上锁
            
 
  
     
}


http://chatgpt.dhexx.cn/article/LxXAOXMN.shtml

相关文章

单片机的结构体

手把手教你写单片机的结构体 FROM8号线攻城狮 公众号 电子信息工程硕士在读&#xff0c;分享单片机、嵌入式linux、物联网等知识&#xff0c;致力于打造最硬核的嵌入式技术公众号。 半碗鸡汤&#xff0c;半碗杂粮。 摘要&#xff1a;听说还有好多学单片机的小伙伴不会用结构…

windows下mysql初始密码设置

转载自&#xff1a;http://blog.csdn.net/ofreelander/article/details/50802780 1.my-default.ini 改名my.ini 在解压的目录下面复制my-default.ini一份改名字为 my.ini。 2.打开 Windows 环境变量设置, 新建变量名 MYSQL_HOME , 变量值为 MySQL 安装目录路径, 在 环境变量 …

MYSQL修改初始化密码的方法

解决方法之一&#xff1a; “ ALTER USER rootlocalhost identified by 123456 ; ” 最近安装了mysql&#xff0c;在使用“ set password for rootlocalhostpassword(123456);”时会遇到 “ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that…

Centos下安装mysql查看初始密码并修改密码

题记&#xff1a;当我们在centos成功安装了mysql后&#xff0c;需要查看初始密码&#xff0c;大概率也需要自己修改密码。 一&#xff0c;查看mysql历时密码 cat var/log/mysqld.log二&#xff0c;进入到mysql mysql -u root -p按回车&#xff0c;然后再password:输入刚才所…

MySQL设置初始密码—注意版本mysql-8.0.30

MySQL设置初始密码—注意版本mysql-8.0.30 第一步&#xff1a;使用管理员模式下的命令行&#xff0c;进入mysql的所在文件下的bin目录; 第二步&#xff1a;输入命令mysql -u root -p; 第三步&#xff1a;这里第一次不用输入密码; 第四步&#xff1a; ALTER USER rootlocalhost…

安装mysql的初始密码在哪里

mysql初始密码是多少 介绍怎么找到mysql安装的root的初始密码 mysql初始密码是多少 介绍怎么找到mysql安装的root的初始密码 怎么给mysql设置用户名和密码 登录数据库 “命令提示字符”窗口录入&#xff0c; 录入cd C:\mysql\bin 并按下回车键&#xff0c;将目录切换为 cd C:\m…

关于MySQL初始化密码忘记的问题

本文章可以解决MySQL密码忘记的问题&#xff01;&#xff01;&#xff01; 1、到mysql安装的bin目录下运行PowerShell 2、在命令行输入 mysql -u root -p 登录 mysql&#xff0c;可以随意输入一个密码&#xff0c;返回”ERROR 1045 (28000): Access denied for user rootlocal…

mysql安装,以及初始化密码

1.首先下载 mysql安装包&#xff0c; 我这里下载的mysql 5.7(免安装版) https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.24-winx64.zip 然后解压到你自己的目录中&#xff0c;我这里为 D 盘下&#xff1a; 此处&#xff0c;下下来的压缩包里面&#xff0c; data 目…

mysql初始没有密码怎么设置

由于本人是win7的忠实用户&#xff0c;最近被win10所吸引索性换了系统&#xff0c;还是改不了恋旧的习惯。所以用的mysql还是5.6。进入正题 在装完mysql并且配置完环境变量之后发现密码为空 1.首先进入到你安装的目录下&#xff0c;这里我的是E盘 2.之后输入mysql -u root -p …

1、数据库-MySql

什么是数据库 数据库(DataBase,简称DB) 概念&#xff1a;长期存放在计算机内&#xff0c;有组织&#xff0c;可共享的大量数据的集合&#xff0c;是一个数据"仓库" 作用&#xff1a;保存&#xff0c;并能安全管理数据(如:增删改查等)&#xff0c;减少冗余… 数据…

C++中字符串大小写字母转换

最近在学习 STL&#xff0c;string 也是 STL 中的一种容器&#xff0c;遇到一个字符串中字母大小写转换的例子&#xff0c;这里就顺便总结一下在C中常用的字符串大小写转换方法&#xff0c;有需要的可以参考。代码如下&#xff1a; 1、char[]类型&#xff0c;调用库函数 // …

大小写字母转换(java大小写字母转换)

苹果4SQQ密码大小写字母怎么转换不&#xff1f;苹果4SQQ密码大小写字母 每次要输密码,当时在电脑上设置的密码,可以改变大小写,但是到了手机上,我不知道怎么换大小写,那位朋友告诉下 苹果手机输入法怎么改字母的大小写 (很高兴为您解答,有帮助请给好评,谢谢! ) 大小写转换-键盘…

英文字母大小写转换

从键盘输入一个大写英文字母&#xff0c;将其转换味小写字母后&#xff0c;再显示到屏幕上&#xff08;显示字符后要输出一个换行&#xff09; 输入格式要求&#xff1a;提示信息&#xff1a;"Press a key and then press Enter:" 程序运行实例如下&#xff1a; P…

Java 字符串中的大小写字母转换

Java 实现字符串中字母大小写转换函数 实现思路&#xff1a;将字符串中的每个字符取出&#xff0c;判断该字符是不是大写字母&#xff0c;如果是的话则该字符32&#xff08;ASCII码&#xff09;并进行强制转换&#xff0c;转换为char&#xff0c;然后在通过字符串的合并操作进…

大写字母转换成小写字母

大写字母转换成小写字母 大写字母转换成小写字母1.示例&#xff1a;2. 解题思路3. 代码展示及分析3.1 ASCII码操作字符串转换大小写3.2 位运算 4. 运行结果 大写字母转换成小写字母 题目&#xff1a; 实现函数 ToLowerCase()&#xff0c;该函数接收一个字符串参数 str&#xf…

C++ | 大小写字母转换

1.题目描述 实现字母的大小写转换。多组输入输出。 输入描述: 多组输入&#xff0c;每一行输入大写字母。 输出描述: 针对每组输入输出对应的小写字母。 答案如下&#xff1a; #include<iostream> using namespace std;int main(){ //----------------------------…

Java-大小写字母转换

题目&#xff1a;输入一段字符&#xff0c;包含大写字母或者小写字母&#xff0c;输出对应的大写或者小写转换&#xff0c;数字不管 代码实现&#xff1a; import java.util.Scanner;public class ZimuChange {public static void main(String[] args) {System.out.println(&qu…

线程中CreateEvent、SetEvent、WaitForSingleObject的用法

原文地址&#xff1a;https://www.cnblogs.com/MrYuan/p/5238749.html 首先介绍CreateEvent是创建windows事件的意思&#xff0c;作用主要用在判断线程退出&#xff0c;线程锁定方面. CreateEvent 函功能描述&#xff1a;创建或打开一个命名的或无名的事件对象. EVENT有两种…

CreateEvent SetEvent WaitForSingleObjec

在自动重置事件对象中&#xff0c;当WaitSingleObject/WaitForMultipleObjects接收到SetEvent发送过来的信号后则返回WAIT_OBJECT_0&#xff0c;此时操作系统&#xff08;待定&#xff09;自动重置等待的事件对象&#xff08;即自动将其设置为无信号状态。无论何时通过SetEvent…

线程中CreateEvent和SetEvent及WaitForSingleObject的用法

首先介绍CreateEvent是创建windows事件的意思&#xff0c;作用主要用在判断线程退出&#xff0c;线程锁定方面. CreateEvent 函功能描述&#xff1a;创建或打开一个命名的或无名的事件对象. EVENT有两种状态&#xff1a;发信号&#xff0c;不发信号。 SetEvent/ResetEvent…