【发布时间】:2012-12-28 02:19:17
【问题描述】:
我正在寻找最优雅的接口上的输入,以放置一个内存映射的寄存器接口,其中目标对象在寄存器中被拆分:
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;
在这一点上,我已经失去了位域抽象试图提供的简单性/便利性。我考虑了宏观解决方案:
#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);
但我一般不喜欢这样的宏,我讨厌在阅读别人的代码时追逐它们,而且我绝不会给别人造成这样的痛苦。我希望有一个涉及结构/联合/what-have-you 的创意技巧来更优雅地隐藏该对象的拆分性质(理想情况下作为对象的简单成员)。
【问题讨论】:
-
坚持使用宏,真的。如果你想让你的代码花哨,假设你有 read_epsr / write_epsr 函数,让它们接受 struct 两个字段(T 和 ICC_IT)并在 funcs 中将其从 / 转换为 espr_t。
-
位域非常不可靠,尤其是以这种方式使用(指向另一个编译域或硬件中的某些东西)。硬件不是动态的,不会改变(不需要定义一次并使用很多)。无论如何,位域都会产生掩码和移位,只需直接或在宏中编码掩码和移位。
-
您是否有任何指针或参考说明它们何时真正成为问题?这是嵌入式领域的一个很常见的习语——当然不是我发明的......
-
Dan Saks 多年来在 www.embedded.com 上详细介绍了这个问题。这是他的一篇文章的link,但还有其他几篇文章可能会对您有所帮助。
-
只是一点评论。您无需命名结构中未使用的字段。删除 reserved0、reserved1 和 reserved2 名称绝对可以。这对于不需要设置这些字段的初始化程序有好处。
标签: c struct embedded unions bit-fields