【发布时间】:2017-11-04 12:45:10
【问题描述】:
我需要在微控制器和另一个芯片之间进行 SPI 通信。该芯片接受 16 位字。但是抽象库要求数据以两个 8 位字节的形式发送。现在我想制作一个包装器,这样我就可以轻松地创建读写请求......但我还没有取得任何成功。应该是这样的:
下表显示 16 位。 MSB 可以是0 用于写入或1 用于读取。地址可以从0x0到0x7,数据为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
或者从寄存器 0x7 读取:
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