通用参数读写函数
以下是一个基于 STM32 的通用参数读写函数的示例代码,实现了对不同数据类型参数在指定地址的读写操作:
#include "stm32f10x.h"
// 解锁 Flash
void FLASH_Unlock(void);
// 锁定 Flash
void FLASH_Lock(void);
// 擦除页
FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
// 写参数(支持不同数据类型)
void writeParameter(void *data, uint32_t address, uint8_t dataType)
{
FLASH_Unlock();
FLASH_ErasePage(address);
FLASH_Status status;
switch (dataType)
{
case 1:
{
uint8_t *value8 = (uint8_t *)data;
*(__IO uint8_t *)address = *value8;
break;
}
case 2:
{
uint16_t *value16 = (uint16_t *)data;
*(__IO uint16_t *)address = *value16;
break;
}
case 4:
{
uint32_t *value32 = (uint32_t *)data;
*(__IO uint32_t *)address = *value32;
break;
}
default:
break;
}
FLASH_Lock();
}
// 读参数(支持不同数据类型)
void readParameter(void *data, uint32_t address, uint8_t dataType)
{
switch (dataType)
{
case 1:
{
uint8_t *value8 = (uint8_t *)data;
*value8 = *(__IO uint8_t *)address;
break;
}
case 2:
{
uint16_t *value16 = (uint16_t *)data;
*value16 = *(__IO uint16_t *)address;
break;
}
case 4:
{
uint32_t *value32 = (uint32_t *)data;
*value32 = *(__IO uint32_t *)address;
break;
}
default:
break;
}
}
以下是对代码的解释:
-
函数功能
writeParameter
函数:用于将不同数据类型的参数写入到 STM32 的内部 Flash 中。它接受三个参数,data
是要写入数据的指针,address
是写入数据的目标 Flash 地址,dataType
表示数据类型(1 表示 8 位,2 表示 16 位,4 表示 32 位)。readParameter
函数:用于从 STM32 的内部 Flash 中读取不同数据类型的参数。它也接受三个参数,data
是用于存储读取数据的指针,address
是读取数据的源 Flash 地址,dataType
的含义与写入函数相同。
-
Flash 操作流程
- 在
writeParameter
函数中,首先解锁 Flash,然后执行页擦除操作,这是因为 Flash 的编程特性决定了写入操作前需要先擦除相应的存储区域。接着根据dataType
的值,将数据按照相应的长度写入 Flash 中。最后锁定 Flash 以确保数据的安全性。 readParameter
函数相对简单,它根据dataType
的值,从指定的 Flash 地址读取相应长度的数据,并将数据存储到由data
指向的变量中。
- 在
以下是一个简单的使用示例:
int main()
{
uint8_t byteData = 0x55;
uint16_t wordData = 0x1234;
uint32_t dwordData = 0x12345678;
// 写入不同类型的数据
writeParameter(&byteData, 0x08008000, 1);
writeParameter(&wordData, 0x08008002, 2);
writeParameter(&dwordData, 0x08008004, 4);
// 读取不同类型的数据
uint8_t readByte;
uint16_t readWord;
uint32_t readDword;
readParameter(&readByte, 0x08008000, 1);
readParameter(&readWord, 0x08008002, 2);
readParameter(&readDword, 0x08008004, 4);
return 0;
}
在这个示例中,首先定义了不同数据类型的变量(8 位、16 位和 32 位),然后使用writeParameter
函数将这些变量的值分别写入 Flash 的不同地址。接着,定义了新的变量用于存储读取的数据,并使用readParameter
函数从 Flash 中读取相应的数据。
将不同数据类型的读写操作改为结构体形式的代码:
#include "stm32f10x.h"
// 解锁 Flash
void FLASH_Unlock(void);
// 锁定 Flash
void FLASH_Lock(void);
// 擦除页
FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
// 定义一个结构体用于存储不同类型的数据
typedef struct {
uint8_t byteData;
uint16_t wordData;
uint32_t dwordData;
} Parameters;
// 写参数(使用结构体)
void writeParameters(Parameters *params, uint32_t address)
{
FLASH_Unlock();
FLASH_ErasePage(address);
*(__IO uint8_t *)(address) = params->byteData;
*(__IO uint16_t *)(address + 1) = params->wordData;
*(__IO uint32_t *)(address + 3) = params->dwordData;
FLASH_Lock();
}
// 读参数(使用结构体)
void readParameters(Parameters *params, uint32_t address)
{
params->byteData = *(__IO uint8_t *)(address);
params->wordData = *(__IO uint16_t *)(address + 1);
params->dwordData = *(__IO uint32_t *)(address + 3);
}
以下是对代码的解释:
-
结构体定义
- 定义了一个名为
Parameters
的结构体,其中包含了uint8_t
类型的byteData
、uint16_t
类型的wordData
和uint32_t
类型的dwordData
三个成员,用于存储不同长度的数据。
- 定义了一个名为
-
写参数函数
writeParameters
函数接受一个指向Parameters
结构体的指针params
和一个uint32_t
类型的address
作为参数。- 在函数内部,首先解锁 Flash,然后擦除指定地址所在的页。
- 接着按照顺序将结构体中的数据写入 Flash 中,注意写入的地址需要根据数据的长度进行偏移(
uint8_t
占 1 个字节,uint16_t
占 2 个字节,从address + 1
开始写,uint32_t
从address + 3
开始写)。 - 最后锁定 Flash。
-
读参数函数
readParameters
函数同样接受一个指向Parameters
结构体的指针params
和一个uint32_t
类型的address
作为参数。- 函数内部直接从指定地址按照数据长度依次读取数据到结构体的相应成员中。
以下是一个使用示例:
int main()
{
Parameters params;
params.byteData = 0x55;
params.wordData = 0x1234;
params.dwordData = 0x12345678;
// 写入结构体数据
writeParameters(¶ms, 0x08008000);
Parameters readParams;
// 读取结构体数据
readParameters(&readParams, 0x08008000);
return 0;
}
在这个示例中,首先创建了一个Parameters
结构体变量params
,并对其成员进行赋值。然后使用writeParameters
函数将结构体中的数据写入 Flash 中。接着创建了另一个Parameters
结构体变量readParams
,并使用readParameters
函数从 Flash 中读取数据到这个结构体中。
以下是在结构体中添加新成员变量时修改参数读写函数的步骤:
一、添加新成员变量到结构体
假设原来的结构体为:
typedef struct {
uint8_t byteData;
uint16_t wordData;
uint32_t dwordData;
} Parameters;
- 考虑数据对齐:在定义结构体时,注意数据的对齐方式。一些处理器对数据的对齐有要求,例如在某些架构下,
int
类型的数据可能需要 4 字节对齐。可以使用编译器的#pragma pack
指令来控制结构体的对齐方式,确保结构体在内存中的布局符合预期,从而保证数据完整性。
#pragma pack(1)
typedef struct {
uint8_t byteData;
uint16_t wordData;
uint32_t dwordData;
} Parameters;
#pragma pack()
- 在上述示例中,
#pragma pack(1)
强制结构体Parameters
的成员按照 1 字节对齐,这样结构体的大小就是成员变量大小之和(1 + 2+ 4 = 7 字节)。#pragma pack()
用于恢复默认的对齐方式。
现在添加一个新的uint32_t
类型成员变量newData
,结构体变为:
typedef struct {
uint8_t byteData;
uint16_t wordData;
uint32_t dwordData;
uint32_t newData;
} Parameters;
二、修改写参数函数
-
调整写入顺序和地址偏移
- 在
writeParameters
函数中,需要考虑新成员变量在结构体中的位置以及数据长度,来调整写入的顺序和地址偏移。 - 由于前面三个成员变量(
byteData
占 1 字节,wordData
占 2 字节,dwordData
占 4 字节)总共占用了 7 字节,所以新的成员变量newData
应该从address + 7
开始写入。
- 在
-
更新写入操作代码
- 原有的写入操作是:
void writeParameters(Parameters *params, uint32_t address)
{
FLASH_Unlock();
FLASH_ErasePage(address);
*(__IO uint8_t *)(address) = params->byteData;
*(__IO uint16_t *)(address + 1) = params->wordData;
*(__IO uint32_t *)(address + 3) = params->dwordData;
FLASH_Lock();
}
- 修改后的写入操作如下:
void writeParameters(Parameters *params, uint32_t address)
{
FLASH_Unlock();
FLASH_ErasePage(address);
*(__IO uint8_t *)(address) = params->byteData;
*(__IO uint16_t *)(address + 1) = params->wordData;
*(__IO uint32_t *)(address + 3) = params->dwordData;
*(__IO uint32_t *)(address + 7) = params->newData;
FLASH_Lock();
}
三、修改读参数函数
-
调整读取顺序和地址偏移
- 在
readParameters
函数中,同样要根据新成员变量的位置来调整读取的顺序。 - 因为新成员变量在结构体中是在前面三个成员之后,所以读取时要从相应的偏移地址开始读取新成员变量的值。
- 在
-
更新读取操作代码
- 原有的读取操作是:
void readParameters(Parameters *params, uint32_t address)
{
params->byteData = *(__IO uint8_t *)(address);
params->wordData = *(__IO uint16_t *)(address + 1);
params->dwordData = *(__IO uint32_t *)(address + 3);
}
- 修改后的读取操作如下:
void readParameters(Parameters *params, uint32_t address)
{
params->byteData = *(__IO uint8_t *)(address);
params->wordData = *(__IO uint16_t *)(address + 1);
params->dwordData = *(__IO uint32_t *)(address + 3);
params->newData = *(__IO uint32_t *)(address + 7);
}
写结构体的函数时,如何保证数据的完整性?
结构体成员变量名的命名规则是什么?
如何优化参数读写函数的性能?