我正在寻找最优雅的接口上的输入来放置内存映射的寄存器接口,其中目标对象在寄存器中被分割:
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
或任何reserved
字段都可以正常工作,但要读取或写入ICI_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;
此时我已经失去了bitfield抽象试图提供的简单/便利的一大块。我考虑过宏观解决方案:
#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);
但是,作为一般规则,我并不是像这样的巨大粉丝,我讨厌在我读别人的代码时追逐它们,而对我来说,给别人带来这样的痛苦远非如此。我希望有一个创意技巧涉及结构/联合/你有什么能更优雅地隐藏这个对象的分裂性质(理想情况下是一个对象的简单成员)。
答案 0 :(得分:8)
我不认为有一种'好'的方式,实际上我不会依赖于位域...有时最好只有一堆详尽的宏来做你想做的一切,文档他们很好,然后依靠他们封装了你的问题......
#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)))
请注意,为了便于阅读,我没有将类型转换和普通宏内容放入其中。是的,我在提及可读性方面具有讽刺意味......
答案 1 :(得分:0)
如果您不喜欢使用内联函数的宏,但您使用的宏解决方案很好。
答案 2 :(得分:0)
您的编译器是否支持匿名联盟?
我发现它是一个优雅的解决方案,摆脱了你的.bits
部分。它不符合C99,但大多数编译器都支持它。它成为了C11的标准。