具有非连续布局的C位字段元素
我正在寻找最优雅的接口上的输入,以放置在内存映射寄存器接口周围,其中目标对象在寄存器中拆分:具有非连续布局的C位字段元素,c,struct,embedded,unions,bit-fields,C,Struct,Embedded,Unions,Bit Fields,我正在寻找最优雅的接口上的输入,以放置在内存映射寄存器接口周围,其中目标对象在寄存器中拆分: union __attribute__ ((__packed__)) epsr_t { uint32_t storage; struct { unsigned reserved0 : 10; unsigned ICI_IT_2to7 : 6; // TOP HALF unsigned reserved1 : 8;
union __attribute__ ((__packed__)) epsr_t {
uint32_t storage;
struct {
unsigned reserved0 : 10;
unsigned ICI_IT_2to7 : 6; // TOP HALF
unsigned reserved1 : 8;
unsigned T : 1;
unsigned ICI_IT_0to1 : 2; // BOTTOM HALF
unsigned reserved2 : 5;
} bits;
};
在这种情况下,访问单个位T
或任何保留
字段都可以正常工作,但要读取或写入ICI\u IT
需要类似以下代码:
union epsr_t epsr;
// Reading:
uint8_t ici_it = (epsr.bits.ICI_IT_2to7 << 2) | epsr.bits.ICI_IT_0to1;
// Writing:
epsr.bits.ICI_IT_2to7 = ici_it >> 2;
epsr.bits.ICI_IT_0to1 = ici_it & 0x3;
union epsr\u t epsr;
//阅读:
uint8_t ici_it=(epsr.bits.ici_it 2to7>2;
epsr.bits.ICI_IT_0to1=ICI_IT&0x3;
此时,我已经失去了位域抽象试图提供的简单性/便利性。我考虑了宏解决方案:
#define GET_ICI_IT(_e) ((_e.bits.ICI_IT_2to7 << 2) | _e.bits.ICI_IT_0to1)
#define SET_ICI_IT(_e, _i) do {\
_e.bits.ICI_IT_2to7 = _i >> 2;\
_e.bits.ICI_IT_0to1 = _i & 0x3;\
while (0);
#定义GET_ICI_IT(_e.bits.ICI_IT_2to7>2\
_e、 bits.ICI_IT_0to1=_i&0x3\
而(0);
但一般来说,我不太喜欢这样的宏,我讨厌在阅读别人的代码时追查它们,我决不会给别人造成这样的痛苦。我希望有一个创造性的技巧,包括结构/联合/你有什么可以更优雅地隐藏这个对象的分裂性质(理想情况下作为对象的简单成员).如果你那么不喜欢宏,只需使用内联函数,但你的宏解决方案很好。我认为没有“好”的方法,事实上我不会依赖于位域……有时候最好是有一堆详尽的宏来完成你想做的一切,好好记录它们,然后完全依赖它们你的问题
#define ICI_IT_HI_SHIFT 14
#define ICI_IT_HI_MASK 0xfc
#define ICI_IT_LO_SHIFT 5
#define ICI_IT_LO_MASK 0x02
// Bits containing the ICI_IT value split in the 32-bit EPSR
#define ICI_IT_PACKED_MASK ((ICI_IT_HI_MASK << ICI_IT_HI_SHIFT) | \
(ICI_IT_LO_MASK << ICI_IT_LO_SHIFT))
// Packs a single 8-bit ICI_IT value x into a 32-bit EPSR e
#define PACK_ICI_IT(e,x) ((e & ~ICI_IT_PACKED_MASK) | \
((x & ICI_IT_HI_MASK) << ICI_IT_HI_SHIFT) | \
((x & ICI_IT_LO_MASK) << ICI_IT_LO_SHIFT)))
// Unpacks a split 8-bit ICI_IT value from a 32-bit EPSR e
#define UNPACK_ICI_IT(e) (((e >> ICI_IT_HI_SHIFT) & ICI_IT_HI_MASK) | \
((e >> ICI_IT_LO_SHIFT) & ICI_IT_LO_MASK)))
#定义ICI_IT_HI_SHIFT 14
#定义ICI_IT_HI_掩码0xfc
#定义ICI\u IT\u LO\u班次5
#定义ICI\u IT\u LO\u掩码0x02
//包含在32位EPSR中拆分的ICI_IT值的位
#定义ICI_IT_压缩_掩码((ICI_IT_HI_掩码ICI_IT_LO_SHIFT)和ICI_IT_LO_掩码)))
请注意,为了可读性,我没有加入类型转换和普通宏内容。是的,我在提到可读性时得到了讽刺…您的编译器支持匿名联合吗 我发现它是一个优雅的解决方案,它摆脱了你的
.bits
部分。它不符合C99,但大多数编译器都支持它。它成为了C11的标准
另请参见以下问题:.坚持使用宏,真的。如果你想让你的代码更有趣,假设你有读/写epsr函数,让它们接受struct两个字段(T和ICC_IT),并在函数中将其从/转换为espr_T。位字段非常不可靠,esp就是这样使用的(指向另一个编译域或硬件中的某物)。硬件不是动态的,不会改变(不需要定义一次并使用多次)。不管怎样,位字段将导致掩码和移位,只需直接或在宏中对掩码和移位进行编码。当它们实际出现问题时,您是否有任何指针或引用?这是嵌入式空间中一个非常常见的习惯用法,当然不是我发明的。。Dan Saks在www.embedded.com上详细介绍了这个问题这些年。这是他的一篇文章的一部分,但还有其他几篇可能会对您有所帮助。只需一句话。您不需要命名结构中未使用的字段。删除reserved0、reserved1和reserved2名称效果绝对不错。这对于不需要设置这些字段的初始值设定器来说有好处。好奇您为什么要这样做避免位字段?对于32位体系结构(当前上下文)上的单个字大小的结构,情况定义得非常好据我所知,位域不受C标准的限制。编译器可以或多或少地以它选择的任何方式实现它们。如果你在单个平台上使用单个编译器,也许可以。但我不喜欢依赖它们。据我所知,它至少有义务保持域的有序。在这一点上,early断言(sizeof(union epsr_t)==4)这足以让偏执狂验证事情是否如预期的那样。在这一点上,我发现可读性大大提高。另外,当避免使用宏时,调试器和其他工具可以获得很好的功能。这很好。我不是专家,也许我的方式也有点老套。但是当我为一个定义非常好的协议安排大量位时,我只是不太信任位域,就像我信任冷硬位操作一样。使用宏来完成所有操作会使代码更难维护,但一旦我出于某种目的开发了一组宏,我会非常仔细地测试它们,然后再也不碰它们。这是合理的,它只是不可伸缩。从100个寄存器开始,其中大部分是ch有5~20个组件,你真的开始欣赏比特域方法的可扩展性和可读性。不管怎样,我认为我已经解决了这个问题,只是想把它扔到世界各地。你的解决方案会是什么样子,去掉了.bit?回顾我的答案(这么多年之后),它没有多大意义:它没有提供一个解决方案。仍然需要变戏法。