看一下结构体、联合体结合使用在C语言与嵌入式中的一些实用技巧

正文
结构体、联合体是c语言中的构造类型,结构体我们平时应该都用得很多。但是,对于联合体,一些初学的朋友可能用得并不多,甚至感到陌生。我们先简单看一下联合体:
在c语言中定义联合体的关键字是union。
定义一个联合类型的一般形式为:
union 联合名{成员表};  
成员表中含有若干成员,成员的一般形式为:类型说明符 成员名。其占用的字节数与成员中最大数据类型占用的字节数。
下面我们一起看一下结构体、联合体结合使用在c语言、嵌入式中的一些实用技巧。
1、应用于管理不同的数据
示例代码:
enum data_pkg_type{    data_pkg1 = 1,    data_pkg2,    data_pkg3    };struct data_pkg1{    // ...};struct data_pkg2{    // ...};struct data_pkg3{    // ...};struct data_pkg{    enum data_pkg_type data_pkg_type;    union     {       struct data_pkg1 data_pkg1_info;   struct data_pkg2 data_pkg2_info;   struct data_pkg3 data_pkg3_info;    }data_pkg_info;};  
这里把struct data_pkg1、struct data_pkg2、struct data_pkg3三个结构体放到了struct data_pkg这个结构体里进行管理,把data_pkg_type与union里的三个结构体建立一一对应关系,我们需要用哪一结构体数据就通过data_pkg_type来进行选中。
在进行数据组包的时候,先给data_pkg_type进行赋值,确定数据包的类型,再给对应的union里的结构体进行赋值;在进行数据解析的时候,通过data_pkg_type来选择解析哪一组数据。
思考一下,如果在union里面再嵌套一层union会怎么样?会变得更复杂?以前的话,我会觉得越嵌套会越复杂,我也很抵制这种不断嵌套的做法。但后来看了我同事鱼鹰的设计之后,我惊呆了!这可太秀了,他就是这么嵌套使用把原本复杂的系统数据管理得明明白白的。我们看他怎么设计的(看个大概的图):
可以看到最左边和最右边这就建立起了一一对应关系,我们的模块很多,数据很多,但是在这样的设计中显得很清晰、很容易维护。
2、寄存器、状态变量封装
我们看一看ti的寄存器封装是怎么做的:
所有的寄存器被封装成联合体类型的,联合体里边的成员是一个32bit的整数及一个结构体,该结构体以位域的形式体现。这样就可以达到直接操控寄存器的某些位了。比如,我们要设置pa0引脚的gpaqsel1寄存器的[1:0]两位都为1,则我们只操控两个bit就可以很方便的这么设置:
gpioctrlregs.gpaqsel1.bit.gpio0 = 3  
或者直接操控整个寄存器:
gpioctrlregs.gpaqsel1.all |=0x03   
如果不是工作于芯片原厂,寄存器的封装应该离我们很远。但我们可以学习使用这种方法,然后用于我们的实际应用开发中。
下面就看一种实际应用:管理一些状态变量。
示例代码:
union sys_status{   uint32 all_status;   struct    {      bool status1:  1; // false / true      bool status2:  1; //       bool status3:  1; //       bool status4:  1; //       bool status5:  1; //       bool status6:  1; //       bool status7:  1; //       bool status8:  1; //       bool status9:  1; //       bool status10: 1; //    // ...  }bit;};  
之前记得群里有一位小伙伴问系统有几十个状态变量需要管理,怎么做比较好。如上例子就是比较好的一种管理方法。
3、数据组合/拆分、大小端
(1)验证大小端
#include typedef unsigned int  uint32_t;typedef unsigned char uint8_t;union bit32_data{    uint32_t data;    struct     {        uint8_t byte0;        uint8_t byte1;        uint8_t byte2;        uint8_t byte3;    }byte;};int main(void){    union bit32_data num;        num.data = 0x12345678;   if (0x78 == num.byte.byte0)  {   printf(little endian);  }  else if (0x78 == num.byte.byte3)  {   printf(big endian);  }else{}    return 0;}  
运行结果:
(2)数据组合、拆分
在数据组合与拆分之前首先需要确实当前平台的大小端。比如小编使用的平台是小端模式。
① 把0x12345678拆分成0x78、0x56、0x34、0x12:
#include typedef unsigned int  uint32_t;typedef unsigned char uint8_t;union bit32_data{    uint32_t data;    struct     {        uint8_t byte0;        uint8_t byte1;        uint8_t byte2;        uint8_t byte3;    }byte;};int main(void){    union bit32_data num;        num.data = 0x12345678;    printf(byte0 = 0x%x, num.byte.byte0);    printf(byte1 = 0x%x, num.byte.byte1);    printf(byte2 = 0x%x, num.byte.byte2);    printf(byte3 = 0x%x, num.byte.byte3);    return 0;}  
运行结果:
② 把0x78、0x56、0x34、0x12组合成0x12345678:
#include typedef unsigned int  uint32_t;typedef unsigned char uint8_t;union bit32_data{    uint32_t data;    struct     {        uint8_t byte0;        uint8_t byte1;        uint8_t byte2;        uint8_t byte3;    }byte;};int main(void){    union bit32_data num;        num.byte.byte0 = 0x78; num.byte.byte1 = 0x56; num.byte.byte2 = 0x34; num.byte.byte3 = 0x12;    printf(num.data = 0x%x, num.data);    return 0;}  
运行结果:
但是数据组合与拆分有更好的方法:移位操作。篇幅有限不再贴出代码,
4、结构体 & 缓冲区
#define buf_size 16union protocol_data{ uint8_t data_buffer[buf_size]; struct  {  uint8_t data1;  uint8_t data2;  uint8_t data3;  uint8_t data4;  // ... }data_info;};  
这种应用得很广泛,用于自定义通信协议。struct里面的内容可以设计得很简单,比如全是有用的数据,或是设计得很复杂,包含一些协议头尾、包长、有效数据、校验等内容。
但无论如何,我们组包发送的过程是填充结构体->发送data_buffer;反之接收数据解析的过程就是接收数据存于data_buffer->使用结构体数据。
5、传输浮点数据
union f_data { float f; struct {  unsigned char byte[4]; };}  
类似的,使用这样子的方法可以用于传输浮点数,更具体地不再展开,网络上有很多这一块的资料。感兴趣的朋友可以自己操作验证验证。


MAX4607-MAX4609双路、SPST、CMOS模拟开关
华为长春研究所成立:聚焦5G、光学、工业互联网、车联网及智慧汽车
2021亚太国际智能装备博览会圆满闭幕,感恩有你同行
嵌入式工控机的优点是怎样的
锂电池为什么会爆炸?锂电池爆炸的几率有多大
看一下结构体、联合体结合使用在C语言与嵌入式中的一些实用技巧
2020“隐私计算元年”,成为大数据时代最重要的护航手
程序员写代码的目的是什么
表面缺陷检测仪已成为高质量生产中不可或缺的一部分
一家来自南非的企业也打算加入动力电池领域的大军
AMD推出EPYC 3000系列处理器,8核16线程最低25W TDP
XIP模式的littlefs_shell代码实现
新思科技携手Juniper投资新公司,开拓快速发展的硅光子市场
配电箱的安装要求与规范
台湾前五大面板厂商去年净亏损合计34.5亿美元
鼠标的发展
Windows 10动态磁贴正逐步淘汰 新图标或将代替
realme成全球成长最快智能手机品牌之一 骁龙865旗舰新品将值得期待
浅谈通信的调制技术
谷歌云游戏服务Stadia将以网页行驶登录苹果设备