提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
在使用国产单片机GD32系列单片机,使用固件库编程的时候,经常遇到程序中的位带操作,固件库对寄存器每个关键位都进行的定义,例如:对时钟配置寄存器 0 (RCU_CFG0)某一位操作的话,直接对该寄存器进行位带操作就可以对某位写1或0。下面详细讲解,让你不再烦恼。
一、位带操作
1. 位带操作就是对某一位或是某连续的位进行操作,如下:
#define BIT(x) ((uint32_t)((uint32_t)0x01U<<(x)))
#define BITS(start, end) ((0xFFFFFFFFUL << (start)) & (0xFFFFFFFFUL >> (31U - (uint32_t)(end))))
第一个是对寄存器的某一位写1操作,使用1<<x位,其余位都为0。那具体怎么用呢?
还是对RCU_CFG0寄存器进行操作,比如对第17位进行操作
写1: RCU_CFG0 |= BIT(17);
写0: RCU_CFG0 &= ~ BIT(17);
如果操作多个不连续的位:
写1: RCU_CFG0 |= (BIT(17) | BIT(13) | BIT(13));
写0: RCU_CFG0 &= ~ (BIT(17) | BIT(13) | BIT(13));
2.连续的位操作
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0);
RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0);
/* CK_PLL = (CK_HXTAL/2) * 30 = 120 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5);
RCU_CFG0 |= RCU_PLL_MUL30;
比如上面这些大家应该熟悉不过了,都是采用了位带操作。拿最后一句来看,进入RCU_PLL_MUL30后:
#define RCU_PLL_MUL26 (PLLMF_4 | CFG0_PLLMF(9)) /*!< PLL source clock multiply by 26 */
#define RCU_PLL_MUL27 (PLLMF_4 | CFG0_PLLMF(10)) /*!< PLL source clock multiply by 27 */
#define RCU_PLL_MUL28 (PLLMF_4 | CFG0_PLLMF(11)) /*!< PLL source clock multiply by 28 */
#define RCU_PLL_MUL29 (PLLMF_4 | CFG0_PLLMF(12)) /*!< PLL source clock multiply by 29 */
#define RCU_PLL_MUL30 (PLLMF_4 | CFG0_PLLMF(13)) /*!< PLL source clock multiply by 30 */
#define RCU_PLL_MUL31 (PLLMF_4 | CFG0_PLLMF(14)) /*!< PLL source clock multiply by 31 */
#define RCU_PLL_MUL32 (PLLMF_4 | CFG0_PLLMF(15)) /*!< PLL source clock multiply by 32 */
#define RCU_PLL_MUL33 (PLLMF_5 | CFG0_PLLMF(0)) /*!< PLL source clock multiply by 33 */
#define RCU_PLL_MUL34 (PLLMF_5 | CFG0_PLLMF(1)) /*!< PLL source clock multiply by 34 */
#define RCU_PLL_MUL35 (PLLMF_5 | CFG0_PLLMF(2)) /*!< PLL source clock multiply by 35 */
#define RCU_PLL_MUL36 (PLLMF_5 | CFG0_PLLMF(3)) /*!< PLL source clock multiply by 36 */
#define RCU_PLL_MUL37 (PLLMF_5 | CFG0_PLLMF(4)) /*!< PLL source clock multiply by 37 */
#define RCU_PLL_MUL38 (PLLMF_5 | CFG0_PLLMF(5)) /*!< PLL source clock multiply by 38 */
#define RCU_PLL_MUL39 (PLLMF_5 | CFG0_PLLMF(6)) /*!< PLL source clock multiply by 39 */
可以看到有很多倍频的选择,以后都可能会用到,我们现在只看30倍频的配置方法:后面是两个语句的或运算,进入可以看到语句的定义
#define PLLMF_4 RCU_CFG0_PLLMF_4 /* bit 4 of PLLMF */
#define RCU_CFG0_PLLMF_4 BIT(27) /*!< bit 4 of PLLMF */
#define CFG0_PLLMF(13) (BITS(18,21) & ((uint32_t)(13) << 18))
现在可以知道一个是对PLLMF的第四位置1和对RCU_CFG0的18到21位置1并与13<<18求与,结果就是
是不是现在理解变得很容易,不放心可以自己动手算一下。
3.其它位操作
#define REG32(addr) (*(volatile uint32_t *)(uint32_t)(addr))
#define REG16(addr) (*(volatile uint16_t *)(uint32_t)(addr))
#define REG8(addr) (*(volatile uint8_t *)(uint32_t)(addr))
#define GET_BITS(regval, start, end) (((regval) & BITS((start),(end))) >> (start))
前面三个是获取某个寄存器的值,返回32,16,8位值
case SEL_PLL:
/* PLL clock source selection, HXTAL, IRC48M or IRC8M/2 */
pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL);
if(RCU_PLLSRC_HXTAL_IRC48M == pllsel){
/* PLL clock source is HXTAL or IRC48M */
pllpresel = (RCU_CFG1 & RCU_CFG1_PLLPRESEL);
if(RCU_PLLPRESRC_HXTAL == pllpresel){
/* PLL clock source is HXTAL */
ck_src = HXTAL_VALUE;
}else{
/* PLL clock source is IRC48 */
ck_src = IRC48M_VALUE;
}
比如在这段代码里: pllsel = (RCU_CFG0 & RCU_CFG0_PLLSEL);其中RCU_CFG0就是获取该寄存器的值再做与运算。
最后一个还不清楚具体怎么用,有知道的可以指教一下。