TLC6C5724驱动开发:位域结构体数组无填充打包为SPI缓冲区方法
解决TLC6C5724驱动中结构体数组无填充打包/解析问题
我正在用C语言开发TLC6C5724的驱动,需实现通用移位寄存器功能,涉及两组带位域的结构体数组:
typedef struct { uint8_t outb : 7; uint8_t outg : 7; uint8_t outr : 7; } dcdata_field_t; typedef struct { uint16_t outb : 12; uint16_t outg : 12; uint16_t outr : 12; } gs_field_t;
对应的数组定义为:
dcdata_field_t dcdata[8]; gs_field_t gsdata[8];
需要生成无填充的输出缓冲区(例如uint8_t dcdata_output[21];,8个dcdata元素共168位=21字节),要求字段间、结构体间完全连续无间隙,最终通过SPI传输至设备。以下是两种可行的实现方案:
方法一:手动位操作(跨编译器兼容,最可靠)
位域的内存布局(位序、填充规则)依赖编译器实现,直接强制类型转换结构体数组存在兼容性风险,手动位操作是最稳妥的方式。
1. 打包dcdata数组到输出缓冲区
每个dcdata_field_t含3个7位字段,共21位,8个元素总长度21字节。通过逐位写入填充缓冲区:
#include <string.h> void pack_dcdata(const dcdata_field_t* dcdata, uint8_t* output) { memset(output, 0, 21); uint32_t bit_pos = 0; for (int i = 0; i < 8; i++) { // 写入outb字段(7位,高位先发,可根据SPI实际传输顺序调整) for (int bit = 6; bit >= 0; bit--) { uint8_t byte_idx = bit_pos / 8; uint8_t bit_in_byte = 7 - (bit_pos % 8); output[byte_idx] |= ((dcdata[i].outb >> bit) & 0x01) << bit_in_byte; bit_pos++; } // 写入outg字段(7位) for (int bit = 6; bit >= 0; bit--) { uint8_t byte_idx = bit_pos / 8; uint8_t bit_in_byte = 7 - (bit_pos % 8); output[byte_idx] |= ((dcdata[i].outg >> bit) & 0x01) << bit_in_byte; bit_pos++; } // 写入outr字段(7位) for (int bit = 6; bit >= 0; bit--) { uint8_t byte_idx = bit_pos / 8; uint8_t bit_in_byte = 7 - (bit_pos % 8); output[byte_idx] |= ((dcdata[i].outr >> bit) & 0x01) << bit_in_byte; bit_pos++; } } }
2. 打包gsdata数组到输出缓冲区
每个gs_field_t含3个12位字段,共36位,8个元素总长度36字节:
void pack_gsdata(const gs_field_t* gsdata, uint8_t* output) { memset(output, 0, 36); uint32_t bit_pos = 0; for (int i = 0; i < 8; i++) { // 写入outb字段(12位) for (int bit = 11; bit >= 0; bit--) { uint8_t byte_idx = bit_pos / 8; uint8_t bit_in_byte = 7 - (bit_pos % 8); output[byte_idx] |= ((gsdata[i].outb >> bit) & 0x01) << bit_in_byte; bit_pos++; } // 写入outg字段(12位) for (int bit = 11; bit >= 0; bit--) { uint8_t byte_idx = bit_pos / 8; uint8_t bit_in_byte = 7 - (bit_pos % 8); output[byte_idx] |= ((gsdata[i].outg >> bit) & 0x01) << bit_in_byte; bit_pos++; } // 写入outr字段(12位) for (int bit = 11; bit >= 0; bit--) { uint8_t byte_idx = bit_pos / 8; uint8_t bit_in_byte = 7 - (bit_pos % 8); output[byte_idx] |= ((gsdata[i].outr >> bit) & 0x01) << bit_in_byte; bit_pos++; } } }
3. 解析缓冲区回结构体数组
反向执行位读取操作即可,以dcdata为例:
void unpack_dcdata(const uint8_t* input, dcdata_field_t* dcdata) { uint32_t bit_pos = 0; for (int i = 0; i < 8; i++) { dcdata[i].outb = 0; // 读取outb字段 for (int bit = 6; bit >= 0; bit--) { uint8_t byte_idx = bit_pos / 8; uint8_t bit_in_byte = 7 - (bit_pos % 8); uint8_t bit_val = (input[byte_idx] >> bit_in_byte) & 0x01; dcdata[i].outb |= bit_val << bit; bit_pos++; } // 读取outg字段 dcdata[i].outg = 0; for (int bit = 6; bit >= 0; bit--) { uint8_t byte_idx = bit_pos / 8; uint8_t bit_in_byte = 7 - (bit_pos % 8); uint8_t bit_val = (input[byte_idx] >> bit_in_byte) & 0x01; dcdata[i].outg |= bit_val << bit; bit_pos++; } // 读取outr字段 dcdata[i].outr = 0; for (int bit = 6; bit >= 0; bit--) { uint8_t byte_idx = bit_pos / 8; uint8_t bit_in_byte = 7 - (bit_pos % 8); uint8_t bit_val = (input[byte_idx] >> bit_in_byte) & 0x01; dcdata[i].outr |= bit_val << bit; bit_pos++; } } }
方法二:编译器属性强制无填充(需注意兼容性)
若使用GCC/Clang等支持扩展属性的编译器,可给结构体添加__attribute__((packed))强制取消字节填充,但位域的位序仍由编译器决定,需确认与设备要求匹配:
typedef struct __attribute__((packed)) { uint8_t outb : 7; uint8_t outg : 7; uint8_t outr : 7; } dcdata_field_t; typedef struct __attribute__((packed)) { uint16_t outb : 12; uint16_t outg : 12; uint16_t outr : 12; } gs_field_t;
此时sizeof(dcdata_field_t)为3字节(21位向上对齐到24位),dcdata[8]总长度为24字节,比需求的21字节多3位填充,仍需手动截断或调整位处理逻辑,因此手动位操作仍是更可靠的选择。
内容的提问来源于stack exchange,提问作者matiko122
相关产品推荐
相关产品推荐

