[ARM入门] 联合体在嵌入式编程中的应用

[复制链接]
 楼主| 药无尘 发表于 2022-6-22 15:56 | 显示全部楼层 |阅读模式
联合体
之前的文章《枚举和结构体的结合》文中提到,结构体就像是打包封装,把一些有共同特征的变量封装在内部。结构体是一种构造类型或复杂类型,它可以包含多个类型不同的成员。在C语言中,还有另外一种和结构体非常类似的语法,叫做联合体(Union)(有些地方也叫做共用体)。
联合体举例如下
  1. union data{
  2.     char n;
  3.     char ch;
  4.     char f;
  5. };
  6. union data a, b, c;
结构体和联合体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而联合体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
结构体占用的内存大于等于所有成员占用的内存的总和(存在字节对齐问题,这里不深入讨论)。
联合体占用的内存等于最长的成员占用的内存。联合体中如果对新的成员赋值,就会把原来成员的值覆盖掉。
  1. //上文的联合体定义
  2. char data;
  3. a.n = 0x0A;//虽然只修改了成员变量n,但是ch和f变量都会被改变
  4. data = a.f;//那么data的值就会被修改为0x0A


 楼主| 药无尘 发表于 2022-6-22 15:59 | 显示全部楼层
联合体的应用
联合体在一般中的编程应用中还是很少的,在单片机编程中较多,在之前的文章《枚举和结构体的结合》将枚举和结构体的结合,下面的例子简单说明下联合体在结构体中的应用(我的日常开发中联合体一般都是和结构体一起使用的)。
在做显示屏应用时牵扯到一个概念,显示屏(全彩)上一个像素点是由于Red,Green,Blue三种颜色组成,在888模式下,每个像素点都是由8个bit组成的,这个时候为了便是一个像素点就需要用到结构体,这也对应前面的文章,结构体就像是打包封装,把一些有共同特征的变量封装在内部。
  1. typedef struct{
  2.     uint8_t Red;
  3.     uint8_t Green;
  4.     uint8_t Blue;
  5.     uint32_t Pix_Value;
  6. }LCD_Pixvalue_S;
上面这样的写法是十分清晰的,访问很方便,可以单独访整个像素,也可以访问像素的某个颜色,有个问题那就是我操作红色像素,需要重新给Pix_Value成员赋值,如下

  1. LCD_Pixvalue_S LCD_Pixvalue;
  2. LCD_Pixvalue.Red = 0x12;
  3. LCD_Pixvalue.Pix_Value = LCD_Pixvalue.Red<<16 | LCD_Pixvalue.Red<<8 |LCD_Pixvalue.Blue;
需要多一句代码,并且内存占用也大。当然,直接用下面写法,不会多占用内存,但是访问不方便。
  1. typedef struct{
  2.     uint32_t Pix_Value;
  3. }LCD_Pixvalue_S;
那么这个时候,使用联合体和结构体组合起来,就可以既不会多占内存,访问也很方便。

  1. typedef union{
  2.     struct{
  3.         uint8_t Red;
  4.         uint8_t Green;
  5.         uint8_t Blue;      
  6.     }Pix;
  7.     uint32_t Pix_Value;
  8. }LCD_Pixvalue_S;
那么就可以如下操作
  1. LCD_Pixvalue_S LCD_Pixvalue;
  2. uint32_t data;
  3. LCD_Pixvalue.Pix_Value = 0x0012FF00;
  4. LCD_Pixvalue.Pix.Red = 0x25;//单独修改红色
  5. data = LCD_Pixvalue.Pix_Value;//data的值就为0x0025FF00
当然,结构体的定义你也可以写到外面,其他地方可以使用,如下
  1. typedef struct{
  2.     uint8_t Red;
  3.     uint8_t Green;
  4.     uint8_t Blue;      
  5. }Pix_s;
  6. typedef union{
  7.     Pix_s Pix;
  8.     uint32_t Pix_Value;
  9. }LCD_Pixvalue_S;
那么关于内存占用的问题,上面的定义方法,定义一个LCD_Pixvalue_S类型的变量占用4个字节。示例图如下

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 药无尘 发表于 2022-6-22 16:00 | 显示全部楼层
联合体在串口开发中的应用
上面举例LCD应用中联合体的应用,这个是由于LCD一个像素由红绿蓝3色组成,所以用联合体很方便。在单片机项目开发中,串口协议解析也可以利用到联合体,十分方便。
在私有自定义协议中,合理定义协议,利用联合体代码十分方便。下面的例子不讲帧头,帧尾部分,举例联合体在协议解析中的应用,灵活应用。
串口协议举例如下
那么代码可以如下编写

  1. typedef union{
  2.     struct{
  3.         uint32_t cmdlen;
  4.         uint8_t cmd;
  5.         uint8_t cmdbuf[7];
  6.         uint16_t crc16;
  7.     }unit;
  8.     uint8_t buffer[14];
  9. }uart_buffer_s;
  10. uart_buffer_s uart_buffer;
  11. int main(void)
  12. {
  13.   uint8_t len;
  14.   len = 0;
  15.   uart_buffer.buffer[len++] = 0x12;
  16.   uart_buffer.buffer[len++] = 0x34;
  17.   uart_buffer.buffer[len++] = 0x56;
  18.   uart_buffer.buffer[len++] = 0x78;
  19.   uart_buffer.buffer[len++] = 0xAA;
  20.   for(int i=0;i<7;i++)
  21.   {
  22.     uart_buffer.buffer[len++] = i;
  23.   }
  24.   uart_buffer.buffer[len++] = 0x11;
  25.   uart_buffer.buffer[len++] = 0x22;
  26.   while (1);
  27. }
运行的结果如下
可以看到,我们只是往uart_buffer.buffer填充数据,模拟串口接收数据,接收完毕后,就自动解析出来我们自定义的cmdlen,cmd,cmdbuf和crc16。这里需要注意的是16位和32位的数据类型是小端模式,关于小端模式请看之前的文章《C语言在STM32中的内存分配》,这个还是很方便的。
!!!但是!!!需要注意字节对齐的问题,比如把上述cmdbuf修改为8个字节,就会出问题,如下
crc16就会出问题,这个就是字节对齐的问题,不懂的同学自行百度一下,这里不再重点讲解。

除了上述自定义协议解析时会用到联合体,还可以解决浮点型float的读取问题,float是占用4个字节的,如果将从串口接收的4个字节转换成float呢?联合体就可以可以解决这个问题
下面示例代码,我们知道浮点数231.5的16进制表示为0x43678000。

  1. typedef union{
  2.     float data;
  3.     uint8_t buffer[4];
  4. }uart_buffer_s;
  5. uart_buffer_s uart_buffer;
  6. int main(void)
  7. {
  8.   uint8_t len;
  9.   len = 0;
  10.   uart_buffer.buffer[len++] = 0x00;
  11.   uart_buffer.buffer[len++] = 0x80;
  12.   uart_buffer.buffer[len++] = 0x67;
  13.   uart_buffer.buffer[len++] = 0x43;
  14.   while (1);
  15. }
结果如下
可以看到我们模拟从串口收到4个字节,使用联合体,不用我们额外写代码,就可以自动转化为float型。当然这个转换也是小端模式的,小端模式详解请看文章《C语言在STM32中的内存分配》。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
天命风流 发表于 2022-6-29 15:43 | 显示全部楼层
受教了               
您需要登录后才可以回帖 登录 | 注册

本版积分规则

79

主题

623

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部