嵌入式系统中结构体数据类型的存储和读取

嵌入式系统中结构体数据类型的存储和读取

在嵌入式单片机系统开发中,系统配置参数通常需要永久存储在外部或者内部的flash / eeprom中。对于系统参数较多时,我们常常使用结构体来进行管理。

如下面的实例,比如这是一个基于单片机开发的某个产品的系统参数。这些参数被定义为系统配置结构体的成员变量。这些成员变量的类型都是不同的,所占的存储空间也不同。通过C语言中的sizeof(SysData)函数可以获得这个结构体类型的存储空间为N个字节。

注意:关于结构体整体占用存储空间的大小,并不是其中每个成员变量的长度简单的相加那么简单,牵扯到另外的知识。

typedef struct
{
  //关于系统时钟的变量
  uint8_t second;
  uint8_t minute;
  uint8_t hour;
  //阀门开关标志
  BOOL IsValveOpen;
  ValveStatus ValveNowSts;
  //设置温度记录
  uint16_t TempSet;
  BOOL IsTempMode;
  //当前室内温度记录
  uint16_t TempNow;
  //热量及使用时间记录
  uint32_t HeatUsed;
  uint32_t TimeUsed;
  //自动开关模式及时间设置记录
  uint8_t OpenMin;
  uint8_t CloseMin;
  uint8_t OpenHour;
  uint8_t CloseHour;
  BOOL IsAutoMode;
  //地址
  uint8_t MeterAddr[7];
  //信道
  uint8_t Channel;
}SysData;

这样我们在应用中就能够比较明了、明确的使用这个结构体,比如

SysData data;
data.second = 45;
data.HeatUsed = 13543;
...

那么如何实现对以上的结构体数据在flash或者eepron中进行存储和读取呢?即把完整的N字个节的结构体变量存储在flash或eeprom中连续的N个字节的存储空间中,读取时实现完整的变量读取,并且结构体中的成员变量也被正确赋值。

当然,你可以这么做:
依次取出结构体中每个成员,再依次存入flash或eeprom。读取方法相同。但这样做不免费时费力。

如果我们利用结构体指针,只要几行代码就能实现了。

#define SYS_DATA_ADDR	0	//flash中保存结构体数据的首地址
//单字节逐个保存的子函数,一般不用,但是可以用来说明和解释一些问题
void flashWriteSysData(SysData *data)
{
  uint16_t i = 0;
  uint8_t temp = 0;
  for(i=0;i<sizeof(*data);i++)
  {
    /*结构体中的成员类型不是一定的,因此,通过内存访问的形式,将结构体中数据按照
    字节依次取出,再写入flash。注意,使用结构体指针时,如果语句为:结构体首地址+1
    则地址会增加整个结构体的长度。理论上不能使用地址增加的形式访问结构体成员。
    如果需要特殊应用,则如下程序所示,先将结构体指针转换为uint8型指针,再通过
    地址增加的形式,就可以每次增加一个字节的地址了。*/
    memcpy(&temp,((uint8_t *)(data))+i,1);   //从结构体所在内存中拷贝一个byte
    FLASH_WiteByte((SYS_DATA_ADDR+i),temp); //写入这个byte
  }
}
//当然,实际应用中我们写了flash连续读写的子函数,以上函数则可以简化为
void flashWriteSysData(SysData *data)
{
	uint16_t len = sizeof(*data);	//获取结构体总长度字节数
	FLASH_WiteNByte(SYS_DATA_ADDR,(uint8_t *)data, len); //一次性写入全部,由于是按字节连续写入,则必须首先强制把data指针转换成uint8_t类型
}
//读取,一次性把所有数据存储在flash中的数据读入到data指针指向的结构体
void flashGetSysData(SysData *data)
{
  uint16_t len = sizeof(*data);	//获取结构体总长度字节数
  FLASH_ReadNByte(SYS_DATA_ADDR,(uint8_t *)data, len); //一次性全部读出,由于是按字节连续读出,则必须首先强制把data指针转换成uint8_t类型
}

以上的实现方法利用了结构体指针,写入和读出都是一次操作就完成了,读出数据到data指针指向的结构体后,我们就可以像线面一样方便的直接引用了。

uint8_t a = data.second;
uint32_t b = data.HeatUsed;
...

下面进行简单的分析:

入口参数“SysData *data”为结构体类型的指针,data指向该结构体的首地址。

“data+1”则会使该指针地址向后跳过42字节。因为data是结构体类型的指针,而该结构体在内存中占用连续的42字节空间,并被认为是一个变量。那么如果指针+1,其指向的地址将会向后跳过一个完整的结构体存储空间,即42字节。

如果需要使用这个指针来依次访问该结构体的每一个成员变量,即每次指针+1,地址跳过1byte,则需要将该结构体类型的指针转换为unsigned char类型的指针:“(uint8_t *)(data)”,这时候再+1,则地址仅会增加一个字节。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值