一年前我在做寄存器底层驱动的时候,在寄存器位域访问上给自己挖了一个很大的坑,最近工作中又碰到了这个问题,不过我发现很多细节都已经记不清了,所以又研究了一遍,看起来这个坑很容易跳进去,所以决定把这个过程记录下来,同时做一些验证和扩展。
现在流行的外设,硬件寄存器的地址往往是4字节对齐的32位,对应的数据位宽也是32位,寄存器作为软硬件的接口,映射的是硬件信号,然而硬件信号不都是32位,大多数情况下,表征一个硬件信号只需要一个或者几个位,这一个或者几个位构成了一个位域,往往是几个位域凑到一起,凑够32位,甚至为了保证对齐,中间还会增加一个或几个没有意义的位,这种结构很容易让人联想到这样的结构:
typedef struct {
union {
volatile unsigned int reg0;
struct {
volatile unsigned int field0:5;
volatile unsigned int rsvd_5:11;
volatile unsigned int field1:7;
volatile unsigned int rsvd_23:9;
} reg0_b;
};
}DDR_CTL_Type;
这个寄存器有两个有用的位域 field0
和 field1
,有两个保留的位域rsvd_5
和 rsvd_23
,这样可以通过 reg0
访问整个32位寄存器,通过 reg0_b.field0
访问 field0
这个位域,相比于把整个寄存器读出来然后再挑 field0
的办法,这种直接访问位域的办法更简洁优雅,