在FLASH中读写结构体
在FLASH中读写结构体
注意事项
编程(写数据)地址要对齐
写数据时,我们要指定写入的地址,如果写入地址为非对齐,则会出现编程对齐错误。
比如遵循32位(4字节)地址对齐,你的地址只能是4的倍数。0x08001000正确,0x08001001错误。
不同型号对齐宽度可能不同,有的32位、有的128位等,可通过“取余”判断地址。比如我遇到在
EEPROM中写一个结构体时,下面这种会有问题,最后一个数据会写入失败。将uint8_t ID;改为uint32_t ID;则正常。typedef struct
{
// uint8_t ID;
uint32_t ID;
float zero;
float dutyCorr;
float fittingCorr;
float initialTemp;
} usrflash;
main.c
usrflash dtl645Config = {0};
...
// 读
FLASH_EEPROM_Read_struct(0x0801FC00, &dtl645Config);
// 写
FLASH_EEPROM_Write_struct(0x0801FC00, &dtl645Config);
flash.h
#ifndef __FLASH_H
#define __FLASH_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "main.h"
// 在 FLASH 写入的结构体变量的类型。
// 第一个成员变量的变量名(ID)不要随便改;
// 变量类型也千万别瞎改。
typedef struct
{
uint8_t ID; // ID
uint8_t addr[6]; // 终端地址
uint8_t baud; // 波特率
uint8_t residualCurrent; // 剩余电流值
uint8_t warningValue; // 剩余电流预警值(百分比)
uint8_t actionValue; // 剩余电流动作值
uint8_t lndt; // 极限不驱动时间
uint16_t trippingTimes; // 跳闸次数mi
uint8_t password[3]; // 密码
uint8_t sSwitch; // 动作状态(0为断开,1为闭合)(分闸/合闸)
uint8_t sStatus; // 运行状态(0为正常,1为警告,2为越限)
} usrflash;
// 在FLASH中写一个字(32bit)
void FLASH_EEPROM_Write(uint32_t a, uint32_t n);
// 从FLASH中读取一个字(32bit)
uint32_t FLASH_EEPROM_Read(uint32_t addr);
// 在FLASH中写一个结构体(usrflash 类型的结构体)
void FLASH_EEPROM_Write_struct(uint32_t addr, usrflash *userinfo);
// 从FLASH中读取一个结构体(usrflash 类型的结构体)
void FLASH_EEPROM_Read_struct(uint32_t addr, usrflash *userinfo);
#ifdef __cplusplus
}
#endif
#endif /* __FLASH_H */
flash.c
//***************************************************************************************************
// flash.c 串口相关功能实现
//
// Includes
// ******************************************************************************************
#include "flash.h"
//***************************************************************************************************
// 在FLASH中写一个字(32bit)
// 传入参数为写入的地址和要写入的数据, 写入数据以字为单位,每个字占32bit即4Byte.
// 擦除时将擦除此地址所在的一整页。
//
void FLASH_EEPROM_Write(uint32_t addr, uint32_t n)
{
// FLASH 擦除操作 --------------------------------------------------------------/
HAL_FLASH_Unlock(); //解锁
uint32_t PageError = 0; // 如果出现错误这个变量会被置为出错的FLASH地址
FLASH_EraseInitTypeDef EraseInitStruct; // 定义结构体
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; // Flash执行页面只做擦除操作
EraseInitStruct.PageAddress = addr; // 要擦除的地址
EraseInitStruct.NbPages = 1; // 要擦除的页数
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) == HAL_OK)
; // 擦除此地址所在的一整页
// FLASH 写入操作 --------------------------------------------------------------/
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, n); // 向FLASH中写入
HAL_FLASH_Lock(); // 锁住FLASH
}
//***************************************************************************************************
// 从FLASH中读取一个字(32bit)
// 入口参数为要读取的FLASH的地址,返回值为uint32_t变量
//
uint32_t FLASH_EEPROM_Read(uint32_t addr)
{
uint32_t pValue = *(__IO uint32_t*)(addr);
return pValue;
}
//***************************************************************************************************
// 在FLASH中写一个结构体(usrflash 类型的结构体,这个类型在 flash.h 中定义的).
// 传入参数为要写入的地址和 usrflash 型结构体变量的取地址。
// 擦除时将擦除此地址所在的一整页。
// STM32F103C8 的 FLASH 中每页的大小为 1k, 写入时需注意数据量不要太大。
//
void FLASH_EEPROM_Write_struct(uint32_t addr, usrflash* userinfo)
{
// FLASH 擦除操作 --------------------------------------------------------------/
HAL_FLASH_Unlock(); //解锁
uint32_t PageError = 0; // 如果出现错误这个变量会被置为出错的FLASH地址
FLASH_EraseInitTypeDef EraseInitStruct; // 定义结构体
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; // Flash执行页面只做擦除操作
EraseInitStruct.PageAddress = addr; // 要擦除的地址
EraseInitStruct.NbPages = 1; // 要擦除的页数
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) == HAL_OK); // 擦除此地址所在的一整页
// FLASH 写入操作 --------------------------------------------------------------/
for (uint8_t i = 0; i < sizeof(usrflash); i += 4) {
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i, *((__IO uint32_t*)(&userinfo->ID + i))) != HAL_OK) //这里的数据由于是结构体,需要从里面取值,一定要以第一个成员的地址开始偏移,不能使用结构体自身的地址来偏移,否则数据会出错
{
HAL_FLASH_Lock();
return;
}
}
HAL_FLASH_Lock();
}
//***************************************************************************************************
// 从FLASH中读取一个结构体(usrflash 类型的结构体,这个类型在 flash.h 中定义的).
// 入口参数为要读取的 FLASH 的地址和接收数据的 usrflash 型结构体变量的取地址。无返回值。
// 为了避免数据出错,读操作一定要和写操作对应,写是按双字写,读就要按双字读,否则就需要解决大小端的问题。
//
void FLASH_EEPROM_Read_struct(uint32_t addr, usrflash* userinfo)
{
for (uint8_t i = 0; i < sizeof(usrflash); i += 4) {
*((uint32_t*)(&userinfo->ID + i)) = *(__IO uint32_t*)(addr + i); //注意赋值的左边,必须要用结构体第一个成员的地址来偏移,双字偏移量是8
}
//这样获取的结构体内容,可以直接通过结构体变量或者结构体指针来访问了.
}
禁止转载到CSDN !
禁止转载到CSDN !
禁止转载到CSDN !
在FLASH中读写结构体的更多相关文章
- c++ 读写结构体到文件
可以使用fwrite()将一个结构体写入文件: fwrite(&some_struct,sizeof somestruct,1,fp);对应的fread函数可以再把它读出来,此处fwrite ...
- C++中的结构体
http://zhidao.baidu.com/link?url=8OYQSKV9mvSBc6Hkf9NsLQmipSge9VCZDJQGAZZs5PCBQ54UTmK98VRmAklEEAFYu7d ...
- C/C++中的结构体
结构体定义 结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构. 结构体作用 结构体和其他类型基础数据类型一样,例如int类型,char类型 只不过结构体可以做成 ...
- C语言中的结构体,结构体数组
C语言中的结构体是一个小难点,下面我们详细来讲一下:至于什么是结构体,结构体为什么会产生,我就不说了,原因很简单,但是要注意到是结构体也是连续存储的,但要注意的是结构体里面类型各异,所以必然会产生内存 ...
- 再识C中的结构体
在前面认识C中的结构体中我介绍了结构体的基础知识,下面通过这段代码来回顾一下: #include<stdio.h> #define LEN 20 struct Student{ //定义结 ...
- C语言中的结构体和C++中的结构体以及C++中类的区别
c++中结构体可以定义一个函数 C中的结构体和C++中结构体的不同之处:在C中的结构体只能自定义数据类型,结构体中不允许有函数,而C++中的结构体可以加入成员函数. C++中的结构体和类的异同: 一. ...
- 关于C语言中结构体中的结构体成员导致的字节对齐问题
关于结构体的字节对齐是什么,就不赘述,再此附上一篇文章,介绍字节对齐:http://www.linuxsong.org/2010/09/c-byte-alignment/ 这里的结构体字节对齐的数据类 ...
- C语言中处理结构体的原理
汇编中有几种寻址方式,分别是直接寻址:(ds:[idata]).寄存器间接寻址(ds:[bx]).寄存器相对寻址(ds:[bx + idata].ds:[bx + si])基址变址寻址(ds:[bx ...
- 结构体:探析C#文件方式读写结构体
最近直在研究Net Micro Framework字体文件(tinyfnt)由于tinyfnt文件头部有段描述数据所以很想 定义个结构体像VC样直接从文件中读出来省得用流个个解析很是麻烦 没有想到在中 ...
随机推荐
- Linux从头学07:中断那么重要,它的本质到底是什么?
作 者:道哥,10+年的嵌入式开发老兵. 公众号:[IOT物联网小镇],专注于:C/C++.Linux操作系统.应用程序设计.物联网.单片机和嵌入式开发等领域. 公众号回复[书籍],获取 Linux. ...
- Socket通信-服务端
WSADATA wsd; SOCKET sClient; SOCKET sServer; SOCKADDR_IN addrServ; char chRcvBuf[RECV_BUF_SIZE]; if ...
- Java HashSet和TreeSet【笔记】
Java HashSet和TreeSet[笔记] PS:HashSet.TreeSet 两个类是在 Map 的基础上组装起来的类 HashSet 类注释 1.底层实现基于 HashMap,所以迭代时不 ...
- 谷歌浏览器崩溃、电脑版微信,vscode打不开网页
家里的电脑使用64位win7,谷歌浏览器直接打开页面都会提示页面崩溃,电脑版微信打开页面链接为空白,vscode打开发行说明或插件说明显示空白,这一系列的问题好像都跟浏览器器有关,之前找了很多文章,都 ...
- WPF教程(四)RelativeSource属性
我们进行Bingding时,如果明确知道数据源的Name,就能用Source或者ElementName进行绑定,但是有时候我们需要绑定的数据源可能没有明确的Name,此时我们就需要利用Bingding ...
- 【springcloud】API Gateway 的路由和过滤(Zuul--1)
转自:https://blog.csdn.net/pengjunlee/article/details/87084646 Zuul是什么? API Gateway 是随着微服务(Microservic ...
- 10.SpringMVC之格式化、校验
数据格式化 数据格式化的注解: 数据校验JSR303 Hibernate Validator扩展注解 启动 springMVC数据校验 转换.格式化.校验出错处理:
- 【转】时冲的CSDN:Linux系统各个目录的作用
请各位移步原文链接:时冲的CSDN 以下仅用于个人梳理,排版方便阅读记忆(原文更优): from my typora: 文章目录 Linux文件系统 LINUX有四种基本文件系统类型: 1.普通文件: ...
- Linux从头学09:x86 处理器如何进行-层层的内存保护?
作 者:道哥,10+年的嵌入式开发老兵. 公众号:[IOT物联网小镇],专注于:C/C++.Linux操作系统.应用程序设计.物联网.单片机和嵌入式开发等领域. 公众号回复[书籍],获取 Linux. ...
- mybaits源码分析--事务管理(八)
一.事务管理 写到这也快进入收尾阶段了了,在介绍MyBatis中的事务管理时不可避免的要接触到DataSource的内容,所以接下来会分别来介绍DataSource和Transaction两块内容. ...