以下代码都是以GD32F470ZIT6芯片代码举例,但是逻辑思路可以借鉴
1、将IO的操作函数定义成宏定义
typedef struct
{
uint32_t gpioX;
uint32_t validBit;
}IOControlInfo;
/*宏*/
#define SETIO(x) gpio_bit_set(x.gpioX,x.validBit)
#define RESETIO(x) gpio_bit_reset(x.gpioX,x.validBit)
#define READINIO(x) gpio_input_bit_get(x.gpioX,x.validBit)
#define READOUTIO(x) gpio_output_bit_get(x.gpioX,x.validBit)
void InitIO_New(IOControlInfo* io,
unsigned int input,
unsigned int validBit
)
{
io->gpioX = input;
io->validBit = validBit;
}
//使用
IOControlInfo Debug_LED;
InitIO_New(&Debug_LED, GPIOC, GPIO_PIN_9);
2、如何用结构体定义一个模块的所有寄存器
例如:我有一个PCS模组,其中这个模组有保持寄存器和输入寄存器的数据
其中输入寄存器中有以下数据,保持寄存器数据忽略
501 uwDSPVer_V DSP版本V u16 R
502 uwHardFault_1 硬件故障字1 u16 R"按位解析:
bit0 -- EPO故障标志;
bit1 -- IGBT硬件过流标志;
bit2 -- 母线硬件过压标志;
bit3 -- 预留
bit4 -- 功率模块逐波限流标志;
bit5 -- 平衡模块硬件过流标志;
bit6_15 -- 预留
1为故障,0为正常;其它位保留;"503 uwDcHisChgCapH 直流历史充电量 u32 R
504 uwDcHisChgCapL
505 wOutAB_Voltage 输出 AB 线电压 s16 R
#define PcsejRStr PcsEJData.ReadMon.Str
#define PcsejRUnio PcsEJData.ReadMon.uwData
#define PcsejR_WStr PcsEJData.R_WriteMon.Str
#define PcsejR_WUnio PcsEJData.R_WriteMon.uwData
typedef struct
{
union
{
uint16_t uwData[5];
#pragma pack(2) //2-byte alignment
struct
{
uint16_t uwDSPVer_V; //DSP版本V 0
union
{
uint16_t data;
struct
{
uint16_t bit0 :1; //bit0 -- EPO故障标志;
uint16_t bit1 :1; //bit1 -- IGBT硬件过流标志;
uint16_t bit2 :1; //bit2 -- 母线硬件过压标志;
uint16_t bit3 :1; //bit3 -- 预留
uint16_t bit4 :1; //bit4 -- 功率模块逐波限流标志;
uint16_t bit5_15:11;//bit5_15 -- 预留
}BITS;
}uwHardFault_1; //硬件故障字1 1
uint16_t uwDcHisChgCapH; //直流历史充电量 2 高16位 kWh
uint16_t uwDcHisChgCapL; // 3 低16位
int16_t wOutAB_Voltage; //输出 AB 线电压 4 显示值 = 寄存器值/10,保留一位小数 V
}Str;
#pragma pack() //Restore default alignment
}ReadMon;
union
{
uint16_t uwData[33];
struct
{
......
}Str;
}R_WriteMon;
}PCS_EJ;
3、#pragma pack(n)的意思
告诉编译器字节对齐方式为n字节对齐,n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。
#pragma pack ()是用来控制字节对齐的,一般头文件中没有的话是默认值,即以结构体中的最大元素所占字节对齐;
4、#pragma pack 的作用域
若存在多个#pragma pack (n),遵从向上对齐原则,即某个结构体定义上方最近的一个#pragma pack()。以下例子中 结构体a 和c上方无#pragma pack 则按照默认对齐方式,即sizeof(struct a)是 8 ,sizeof(struct c)是3字节。而结构体b上方 有按照一字节定义#pragma pack (1),则sizeof (struct b)是6字节
struct a{
char str;
int inter;
};
struct c{
char str;
char str1;
char str2;
};
#pragma pack(1)
struct b{
char str;
char str1;
int inter;
};
5、如何读取分区寄存器的值
举例:现在有很多个模块,每个模块互相没有关系。每一个模块占125个字节,然后每一个模块的125个字节不一定全部占满,可能存在部分字节预留,通信方式采用Modbus协议。
需求:我要读取各个模块的值,读取数据长度未知,代码怎么写
//modbus常见功能码 定义
typedef enum
{
R_CoilStatus = 0x01, //read coil status
R_HoldReg = 0x03, //read holding register
R_InputReg = 0x04, //read input register
W_SingleCoil = 0x05, //Write single coils
W_SingleReg = 0x06, //Write single register
W_ModeRest = 0x08, //Modul Reset
W_MultipleCoil = 0x0F, //Write multiple coils
W_MultipleReg = 0x10, //Write multiple register
W_UpdateFlag = 0x26, //Start burning firmware files
W_UpdataData = 0x17, //Burn firmware file data
W_UpdateRev = 0x23, //Query burning progress
W_MeterReg = 0xAA, //Zeroing the electricity meter
}ModbusF_Code;
//modbus协议接收数据格式:设备地址 03 寄存器高位 寄存器低位 数据长度高位 数据长度地位
uint16_t RegAddrStart = 0; //register Start address
uint16_t num = 0; //register or Coil number
RegAddrStart = (ReceiveBuffer[2] << 8) | ReceiveBufferr[3];//记录寄存器地址
switch(ReceiveBuffer[1])//判断接收数据的功能码是什么
{
case R_HoldReg: //read holding register
{
num = (ReceiveBuffer[4] << 8) | ReceiveBuffer[5]; //Number of registers
if((RegAddrStart <= 124) && (num <= 125) && ((RegAddrStart + num) <= 125)) //将每一个模块都进行分区读取,不能跨区读取数据
{
}
else if((RegAddrStart >= 125) && (RegAddrStart <= 249) && (num <= 125) && ((RegAddrStart + num) <= 250))
{
}
。。。。
}
}
6、enum枚举的作用
- 枚举是 C 语言中的一种基本数据类型,用于定义一组具有离散值的常量,它可以让数据更简洁,更易读。
- 枚举类型通常用于为程序中的一组相关的常量取名字,以便于程序的可读性和维护性。
- 定义一个枚举类型,需要使用 enum 关键字,后面跟着枚举类型的名称,以及用大括号 {} 括起来的一组枚举常量。每个枚举常量可以用一个标识符来表示,也可以为它们指定一个整数值,如果没有指定,那么默认从 0 开始递增。
-
enum 枚举名 {枚举元素1,枚举元素2,……};
7、如何注释有时使用 有时不使用 的代码
#if 0
........
#endif
8、如何更新一组寄存器的部分数据
typedef struct {
int data[10];
bool updateFlag[10];
}XX_W;
XX_W xx1 ;
#define XXData xx1.data
#define XXUpdateFlag xx1.updateFlag
如何data[index]需要更新,则将updataFlag[index]置为true,表示他要更新,我所欠缺的是 不喜欢把变量用结构体来表示,要多学习