我正试图得到一个工会在一起Map出一些位字段在寄存器Map。我的代码如下:
typedef union __attribute__((packed)) {
struct {
uint8_t MODE:3;
uint8_t VSHCT:3;
uint8_t VBUSCT:3;
uint8_t AVG:3;
uint8_t RSVD:3;
uint8_t RST:1;
};
struct {
uint8_t lsbyte:8;
uint8_t msbyte:8;
};
uint16_t w;
} CON_MAP_t;
我正在使用以下命令初始化字段:
CON_MAP_t map = {
.RST = 0,
.RSVD = 4,
.AVG = 0,
.VBUSCT = 4,
.VSHCT = 4,
.MODE = 7
}
到目前为止一切正常,没有编译器问题或警告。
我期望二进制/十六进制表示为01000001_00100111 /0x 4127。
然而,在调试器中,我最终得到了'w'的值:00000100_00100111最低有效字节是正确的,但msb(yte)不是。
我不确定我是否错过了一些基本的东西,我只是盯着它太久了,但任何见解都将受到高度赞赏!
我正在使用:MPLABX v6.05最新XC 32编译器
设备是一个PIC 32 MX 130 F064 D调试用的PICKIT 4.
4条答案
按热度按时间vd2z7a6w1#
位字段是否可以跨越定义它们的数据类型的边界是由实现定义的。还有其他各种细节。请参阅reference的以下摘录:
位字段的以下属性是 * 实现定义的 *:
int
、signed int
、unsigned int
和_Bool
(自C99起)以外的类型在本例中,您有一个8位数据类型,您试图将3组3位数据打包到其中。第三组跨越了边界,您的编译器实现似乎不支持这一点。看起来该值已移动到下一个字节,这可能意味着
sizeof(CON_MAP_t)
大于2。因此,至少将第一个结构体中使用的类型更改为
uint16_t
,但要注意,对它的支持也是实现定义的(根据前面显示的摘录)。顺便说一句,不需要在第二个结构体中定义位字段(在这里,您为每个
uint8_t
指定了8位)。需要注意的是,使用位字段进行这种操作是严重依赖于平台和编译器的。使用它们进行类型双关语违反了严格的别名。
对于任何类型的可移植性,您都应该考虑其他解决方案。即使是针对一个特定的平台,至少为这个结构创建大量的单元测试以确保合理性也是明智的。
alen0pnh2#
当涉及到使用支持标准C编译器(如XC32 Compiler)的一些附加功能的编译器时,最好参考它的指南和手册,以了解像这样的特殊情况。
XC 32完全支持结构中的位字段,并保证第一个定义的位的顺序为最低有效位。以下是XC32 C Compiler User's Guide的8.6.2结构中的位字段部分的描述:
MPLAB XC 32 C/C++编译器完全支持结构中的位字段。位字段总是在8位存储单元中分配,即使在定义中通常使用类型
unsigned int
。存储单元在32位边界上对齐,但可以使用packed
属性进行更改。定义的第一位将是存储它的字的最低有效位。当声明位字段时,如果它适合,则在当前8位单元内分配它;否则,在结构内分配新的字节。位字段永远不能跨越8位分配单元之间的边界。例如,声明:
将产生占用1字节的结构。
因此,根据指南描述,您应该看到与您的位定义相同的顺序。
另外请注意,
packed
属性仅在您希望更改32位边界时使用。但这是没有必要的,因为你是只有16位。下面是一个演示,显示了代码后面的预期结果:
演示代码如图所示
fwzugrvs3#
正如在评论和其他帖子(如Why bit endianness is an issue in bitfields?)中所指出的那样,C标准对位字段的定义非常糟糕,甚至不好笑。在这个混乱的环境中再加上一个令人讨厌的声誉编译器(如MPLAB),您将面临灾难。
我的建议是忘记你曾经听说过位字段,并通过宏使用100%可移植的、标准化的整数常量来编写代码。在这个特定的例子中,没有明显的必要在字和字节之间输入双关语--既然位字段到处都是,为什么你还需要字节访问呢?
假设你的硬件寄存器名为
CON_MAP
,CPU是小字节序(字节序对你的位域代码很重要,但对我下面的解决方案不重要),那么:使用方法:
值
2u
、3u
等理想情况下应该用命名常量替换,以防您对它们有一些有意义的名称。喜欢:CON_MAP_MODE(FANCY_MODE | SPECIAL_MODE)
.以上是在嵌入式系统中实现硬件寄存器的几种常见工业标准方式之一。更多信息:How to access a hardware register from firmware?
ujv3wf0j4#
我想我做了我能想到的最后一件事才弄明白了这一点。我将宏类型定义改为标准的“int”,并在“w”字段中添加了位计数:
这似乎解决了硬件实现中的问题。
如果还有其他我可能遗漏的基本理解,请告诉我。
谢谢!