在GD32的库里面,经常看到
#define RCU_REGIDX_BIT(regidx, bitpos) (((uint32_t)(regidx)<<6) | (uint32_t)(bitpos))
#define RCU_REG_VAL(periph) (REG32(RCU + ((uint32_t)(periph)>>6)))
#define RCU_BIT_POS(val) ((uint32_t)(val) & 0x1FU)
这样的写法
GD32将寄存器地址偏移REGDIX 和 里面控制位BIT偏移组合成1个新的32bit数值!
所以这个比较绕。 它的操作,都是基于这个新的32bit值的。 比如向右>>6,就可以获得寄存器的地址偏移。 (REG32(RCU + ((uint32_t)(periph)>>6))) 则就得到了完整的寄存器地址。而((uint32_t)(val) & 0x1FU) 则可以得到位的偏移,然后通过BIT()这个偏移量,即可将具体的位置1.
还是很巧妙的。 里面运用的次数较多,所以这里单独来分析了。
下面是库文件中的具体例子:
将RCU的寄存器地址偏移REGDIX 和 里面控制位BIT偏移组合成1个新的32bit数值!
#define RCU_REGIDX_BIT(regidx, bitpos) (((uint32_t)(regidx)<<6) | (uint32_t)(bitpos))
RCU_REGIDX_BIT将寄存器偏移REGDIX和控制位BIT组合成一个32位的值,以后就可以用了
具体,方法就是,把REGDIX左移动了<<6位,然后把控制位偏移量bitpos拼到结尾上去的。
#define RCU_REG_VAL(periph) (REG32(RCU + ((uint32_t)(periph)>>6)))
把这个RCU_REGIDX_BIT向右>>6bit,去掉了位偏移,也就还原了REGIDX_BIT ,就得到了外设偏移 ,这样再加上RCU的基地址,这样就可以读出该寄存器的值了
#define RCU_BIT_POS(val) ((uint32_t)(val) & 0x1FU)
RCU_BIT_POS() 这个功能就是将上面组合好的32位值填到里面,它可以算出具体控制位的偏移。
0x1FU 表示限制后5位,为啥是5位? 0b000....1 1111
由于某个寄存器中,位的偏移最多就是[0~31], 所以用5bit就可以表示这个偏移位数了。
比如这里的IDX_AHBRST,它的偏移是十进制的17U,二进制为0b000....1 0001
这样在做RCU_BIT_POS()之后与0x1FU进行与运算,那就得到了17这个值然后执行BIT(17),则把第17位置1了。 也就是设置了该位的值。
这个是宏定义,IDX_AHBRST = 0x28U,
这个是另一个别名定义 periph
typedef enum
{
RCU_GPIOARST = RCU_REGIDX_BIT(IDX_AHBRST, 17U),
RCU_GPIOBRST = RCU_REGIDX_BIT(IDX_AHBRST, 18U),
RCU_GPIOCRST = RCU_REGIDX_BIT(IDX_AHBRST, 19U),
RCU_GPIOFRST = RCU_REGIDX_BIT(IDX_AHBRST, 22U),
}rcu_periph_reset_enum;