KEIL代码编程技巧(1)

以下代码都是以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枚举的作用

  1. 枚举是 C 语言中的一种基本数据类型,用于定义一组具有离散值的常量,它可以让数据更简洁,更易读。
  2. 枚举类型通常用于为程序中的一组相关的常量取名字,以便于程序的可读性和维护性。
  3. 定义一个枚举类型,需要使用 enum 关键字,后面跟着枚举类型的名称,以及用大括号 {} 括起来的一组枚举常量。每个枚举常量可以用一个标识符来表示,也可以为它们指定一个整数值,如果没有指定,那么默认从 0 开始递增。
  4. 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,表示他要更新,我所欠缺的是 不喜欢把变量用结构体来表示,要多学习

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值