【问题标题】:Assigning individual bits to bytes将各个位分配给字节
【发布时间】:2017-11-04 12:45:10
【问题描述】:

我需要在微控制器和另一个芯片之间进行 SPI 通信。该芯片接受 16 位字。但是抽象库要求数据以两个 8 位字节的形式发送。现在我想制作一个包装器,这样我就可以轻松地创建读写请求......但我还没有取得任何成功。应该是这样的:

下表显示 16 位。 MSB 可以是0 用于写入或1 用于读取。地址可以从0x00x7,数据为11位。

R/W  |      ADDRESS   |    DATA
 B15 |      B14-B11   |   B10-B0
 0   |        0000    | 00000000000
 W0  | A3, A2, A1, A0 | D10, D9, D8, D7, D6, D5, D4, D3, D2, D1, D0

例如,如果我想从寄存器0x1 中读取数据,我想我必须像这样设置位:

 W0  | A3, A2, A1, A0 | D10, D9, D8, D7, D6, D5, D4, D3, D2, D1, D0
  1  | 0    0   0   1 |  0    0   0   0   0   0   0   0   0   0   0

或者从寄存器 0x​​7 读取:

 W0  | A3, A2, A1, A0 | D10, D9, D8, D7, D6, D5, D4, D3, D2, D1, D0
  1  | 0    1   1   1 |  0    0   0   0   0   0   0   0   0   0   0

我已经尝试创建这个结构/联合,看看它是否可以工作:

typedef struct{
    uint8_t acc_mode:1;
    uint8_t reg_addr:4;
    uint8_t reg_data:8; //TODO fix me should be 11
} DRVStruct;

typedef union {
    DRVStruct content;
    uint16_t all;
} DRVUnion;

void DRV_PrepareReadMsg(uint8_t reg, uint8_t* msgBuffer) {
    DRVUnion temp;
    temp.content.acc_mode = 1;
    temp.content.reg_addr = reg;
    temp.content.reg_data = 0; //read mode does not need data!

    msgBuffer[1] = temp.all & 0xFF;
    msgBuffer[0] = temp.all >> 8;
}

我得到奇怪的结果...有时我会从 SPI 得到答案(我确信 SPI 通信正常,但我准备消息的代码是问题所在)。

所以问题是:

  • 我的做法是否正确?
  • 如何将reg_data 的位宽从 8 增加到 11 而不会出现编译错误?
  • 您对更好的方法有何建议?

【问题讨论】:

  • 使用 uint16_t,你不能把 reg_data 设置为 11 位吗?
  • 我建议使用位域。位字段表示是完全实现定义的,这意味着位的顺序以及它们在底层数据中映射的内容是您无法控制的。显然,您还必须在两个 uint8_t 值之间重叠位字段。不管怎样,uint8_t reg_data:11; 是行不通的。
  • 我不明白你为什么要把 protocl 结构与 uint16_t 联合起来。两个 uint8_t 会更有意义,消除掩蔽和移位。事实上,如果我要这样做,我会与 msgBuffer 联合,使用编译器提供的任何“pragma packed”,并使用 uint16_t 作为协议结构的基本类型,以便可以容纳 11 位字段.那个,并且要非常小心,围绕它进行大量调试以确保位/字节在它们应该在的位置......
  • uint8_t 实际上是 unsigned char 在您的实现中吗?这是一个依赖于平台的 hack,但如果 uint8_t 实际上是 unsigned char,那么您可以将 uint16_t * 转换为 uint8_t * 而不会违反严格的别名,因为任何指针总是可以转换为 char *。删除处理结构和联合所需的所有代码可能是值得的。
  • 使用 uint16_t 创建正确的类型,对齐限制为 16 位。 (但我更喜欢 not 使用位域。)您也可以保持位域结构无标记。

标签: c bit-manipulation bitwise-operators unions spi


【解决方案1】:

这似乎有效:


#include <stdio.h>
#include <stdint.h>

typedef union {
    struct{     // no struct tag, since it is not needed...
        uint16_t acc_mode:1;
        uint16_t reg_addr:4;
        uint16_t reg_data:11; //TODO fix me should be 11
        } bits;
    uint16_t all;
    uint8_t bytes[2]; //extra bonus when lit;-)
        } DRVUnion;

int main(void)
{
DRVUnion uni,uni13[13];

printf("Size=%zu, %zu\n", sizeof uni, sizeof uni13);
return 0;
}

【讨论】:

  • 谢谢这行得通...但是当我将单个字节分配给bytes 字段时,我必须这样做:instance.bytes[0] = data[1]; instance.bytes[1] = data[0] 如果我只是分配给allfield 它不会按预期工作。
  • 那是因为你的主机和目标的字节序不同。
猜你喜欢
  • 1970-01-01
  • 2010-11-21
  • 2021-07-25
  • 1970-01-01
  • 2016-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多