GD32F103时钟配置分析

一,时钟树

1.1、介绍几个关键字含义(由红色方框框选):

IRC8M:高速内部时钟(这个是芯片自带的内部时钟,精度较低,一般很少使用)

HXTAL:高速外部时钟(外部晶振,精度高)

LXTAL :低速内部时钟(外部低速晶振,精度高,常用于看门狗)

IRC40K:低速内部时钟(精度低,常用于看门狗,一般很少使用)

1.2、淡蓝方框框选的表示寄存器名称

1.3、紫色箭头表示时钟最大频率,这里可以看到AHB最大时钟时108MHz

1.4、绿色线路表示我程序初始化的线路

从上面时钟树我们要配置时钟,选择HXTAL(外部高速振荡器)作为时钟源步骤如下:

PREDV0(分频因子)->PLLSEL(时钟源选择)->PLLMF时钟倍频因子(这里需要注意PLLMF寄存器不是在连续的寄存器上,是由PLLMF[3:0]+PLLMF[4]<<4=PLLMF寄存器)->系统时钟选择(SCS)->AHB预分频选择->APB1预分频选择,APB2预分频选择

1.5、蓝色椭圆框选定时器,这里需要注意

TIMER1,2,3,4,5,6,11,12,13 if(APB1 prescale = 1)×1 else ×2

这里的意思是TIMER1,2,3…..13 如果APB1分频系数为1,定时器时钟频率就是APB1的频率,如果APB1分频系数不为1,定时器频率是APB1的两倍

二、基础寄存器介绍

RCU基地址:0x4002 1000

2.1、控制寄存器(RCU_CTL)

位/位域

名称

描述

[31:26]

保留

必须保持复位值。https://www.gd32mcu.com/cn/

[25]

PLLSTB

PLL时钟稳定标志位

硬件置1来表示PLL输出时钟是否稳定待用

0:PLL未稳定

1:PLL已稳定

[24]

PLLEN

PLL使能

软件置位或复位,当PLL时钟做为系统时钟时该为不能被复位。当进入深度睡眠或待机模式时有硬件复位

0:PLL被关闭

1:PLL被打开

[23:20]

保留

必须保持复位值

[19]

CKMEN

HXTAL时钟监视器使能

0:禁用高速4~16MHz晶振振荡器(HXTAL)时钟监视器

1:使能高速4~16MHz晶振振荡器(HXTAL)时钟监视器

当硬件检测到HXTAL时钟被阻塞在低或高状态时,内部硬件自动切换系统时钟到IRC8M时钟。恢复原来系统时钟的方式有以下几种:外部复位,上电复位,软件清CKMIF位。

注意:使能HXTAL时钟监视器以后,硬件无视控制位IRC8MEN的状态,自动使能IRC8M时钟。

[18]

HXTALBPS

高速晶体振荡器(HXTAL)时钟旁路模式使能

只有在HXTALEN位为0时HXTALBPS位才可写

0:禁用HXTAL旁路模式

1:使能HXTAL旁路模式 HXTAL输出时钟等于输入时钟

[17]

HXTALSTB

高速晶体振荡器(HXTAL)时钟稳定标志位

硬件置‘1’来子时HXTAL振荡器时钟是否稳定待用

0:HXTAL振荡器未稳定

1:HXTAL振荡器已稳定

[16]

HXTALEN

高速晶体振荡器(XTAL)使能

软件置位或复位,如果HXTAL时钟作为系统时钟或者当PLL时钟做为系统时钟时,其作为PLL的输入时钟,该位不能被复位。进入深度睡眠或待机模式时硬件自动复位

0:高速4~16MHz晶体振荡器被关闭

1:高速4~16MHz晶体振荡器被打开

[15:8]

IRC8MCALIB[7:0]

内部8MHz RC振荡器校准值寄存器

上电时自动加载这些位

[7:3]

IRC8MADJ[4:0]

内部8MHz RC振荡器时钟调整值

这些位由软件置位,最终调整值为IRC8MADJ[4:0]位域的当前值加上IRC8MCALIB[7:0]位域的值。最终调整值应该调整IRC8M到8MHz ± 1%

[2]

保留

必须保持复位值

[1]

IRC8MSTB

IRC8M内部8MHz RC振荡器稳定标志位

硬件置‘1’来指示IRC8M振荡器时钟是否稳定待用

0:IRC8M振荡器未稳定

1:IRC8M振荡器已稳定

[0]

IRC8MEN

内部8MHz RC振荡器使能

软件置位或复位,如果IRC8M时钟作为系统时钟时,该位不能被复位。当从深度睡眠或待机模式返回,或当CKMEN置位同时用作系统时钟的HXTAL振荡器发生故障时,该位由硬件置1来启动IRC8M振荡器

0:内部8MHz RC振荡器被关闭

1:内部8MHz RC振荡器被打开

2.2、时钟配置寄存器0 RCU_CFG0

位/位域

名称

描述

[31:29]

保留

必须保持复位值

[28]

ADCPSC[2]

ADCPSC的第2位

参考寄存器RCU_CFG0的14到15位

[27]

PLLMF[4]

PLLMF的第四位

参考寄存器RCU_CFG0的18到21位

[26:24]

CKOUT0SEL[2:0]

CKOUT0时钟源选择

由软件置位或清零

0xx:无时钟输出

100:选择系统时钟CK_SYS

101:选择内部8M RC振荡器时钟

110:选择高速晶体振荡器时钟(HXTAL)

111:选择(CK_PLL/2)时钟

[23:22]

USBDPSC[1:0]

USBD的时钟分频系数

由软件置位或清零。USBD的时钟必须为48MHz,当USBD时钟使能的时候,这些位无法修改

00:CKUSBD=CK_PLL/1.5

01:CKUSBD=CK_PLL

10:CKUSBD=CK_PLL/2.5

11:CKUSBD=CK_PLL/2

[21:18]

PLLMF[3:0]

PLL时钟倍频因子

寄存器RCU_CFG027位共同构成倍频因子,由软件置位或清零

注意:PLL输出时钟频率不能超过108MHz

00000:(PLL源时钟*2)

00001:(PLL源时钟*3)

00010:(PLL源时钟*4)

00011:(PLL源时钟*5)

00100:(PLL源时钟*6)

00101:(PLL源时钟*7)

00110:(PLL源时钟*8)

00111:(PLL源时钟*9)

01000:(PLL源时钟*10)

01001:(PLL源时钟*11)

01010:(PLL源时钟*12)

01011:(PLL源时钟*13)

01100:(PLL源时钟*14)

01101:(PLL源时钟*6.5)

01110:(PLL源时钟*16)

01111:(PLL源时钟*16)

10000:(PLL源时钟*17)

10001:(PLL源时钟*18)

10010:(PLL源时钟*19)

10011:(PLL源时钟*20)

10100:(PLL源时钟*21)

10101:(PLL源时钟*22)

10110:(PLL源时钟*23)

10111:(PLL源时钟*24)

11000:(PLL源时钟*25)

11001:(PLL源时钟*26)

11010:(PLL源时钟*27)

11011:(PLL源时钟*28)

11100:(PLL源时钟*29)

11101:(PLL源时钟*30)

11110:(PLL源时钟*31)

11111:(PLL源时钟*32)

[17]

PREDV0

PREDV0分频因子

由软件置位或清零,PLL未使能时,可以修改这些位

0:PREDV0输入源时钟未分频

1:PREDV0输入源时钟2分频

[16]

PLLSEL

PLL时钟源选择

由软件置位或复位,控制PLL时钟源

0:(IRC8M/2)被选择为PLL时钟的时钟源

1:HXTAL时钟被选择为PLL时钟的时钟源

[15:14]

ADCPSC[1:0]

ADC的时钟分频系数

寄存器RCU_CFG028为共同构成分频因子,由软件置位或清零

000:CK_ADC=CK_APB2/2

001:CK_ADC=CK_APB2/4

010:CK_ADC=CK_APB2/6

011:CK_ADC=CK_APB2/8

100:CK_ADC=CK_APB2/2

101:CK_ADC=CK_APB2/12

110:CK_ADC=CK_APB2/8

111:CK_ADC=CK_APB2/16

[13:11]

APB2PSC[2:0]

APB2预分频选择

由软件置位或清零,控制APB2时钟分频因子

0xx:选择CK_AHB时钟不分频

100:选择CK_AHB时钟2分频

101:选择CK_AHB时钟4分频

110:选择CK_AHB时钟8分频

111:选择CK_AHB时钟16分频

[10:8]

AHB1PSC[2:0]

APB1预分频选择

由软件置位或清零,控制APB1时钟分频因子

0xx:选择CK_AHB时钟不分频

100:选择CK_AHB时钟2分频

101:选择CK_AHB时钟4分频

110:选择CK_AHB时钟8分频

111:选择CK_AHB时钟16分频

[7:4]

AHBPSC[3:0]

AHB预分频选择

由软件置位或清零,控制AHB时钟分频因子

0xxx:选择CK_SYS时钟不分频

1000:选择CK_SYS时钟2分频

1001:选择CK_SYS时钟4分频

1010:选择CK_SYS时钟8分频

1011:选择CK_SYS时钟16分频

1100:选择CK_SYS时钟64分频

1101:选择CK_SYS时钟128分频

1110:选择CK_SYS时钟256分频

1111:选择CK_SYS时钟512分频

[3:2]

SCSS[1:0]

系统时钟选择状态

由硬件置位或清零,标识当前系统时钟的时钟源

00:选择CK_IRC8M时钟作为CK_SYS时钟源

01:选择CK_HXTAL时钟作为CK_SYS时钟源

10:选择CK_PLL时钟作为CK_SYS时钟源

11:保留

[1:0]

SCS[1:0]

系统时钟选择

由软件配置选择系统时钟源。由于CK_SYS的改变存在固有的延迟,因此软件应当读SCSS位来确保时钟源切换是否结束。在从深度睡眠或待机模式中返回时,以及当HXTAL直接或间接作为系统时钟同时HXTAL时钟监视器检测到HXTAL故障时,强制选择IRC8M作为系统时钟。

00:选择CK_IRC8M时钟作为CK_SYS时钟源

01:选择CK_HXTAL时钟作为CK_SYS时钟源

10:选择CK_PLL时钟作为CK_SYS时钟源

11:保留

三、系统时钟初始化代码(SystemClock_Config)

这里外部高速晶振是8MHz 我们根据上述函数来计算我们系统时钟是多少,

8MHz/PREDV0*PLLMF=CK_PLL=CK_SYS

CK_SYS=8MHz/1*9=72MHz,所以我们这里配置的系统时钟频率时72MHz

CK_AHB=CK_SYS/1=72MHz,所以我们这里配置的AHB总线时钟频率时72MHz

CK_APB1=CK_AHB/2=36MHz,   APB1总线时钟频率是36MHz

CK_APB2=CK_AHB/1=72MHz,  APB2总线时钟频率是72MHz

下面我们来看下rcu_osci_on(RCU_HXTAL);这个函数是怎么实现的

3.1、先查看RCU_HXTAL这个传参是多少

由于代码我们查找对应参数如上,经过替换可以得到

RCU_HXTAL = RCU_REGIDX_BIT(0x00,16U);

                      =(((uint32_t)(regidx)<<6) | (uint32_t)(bitpos))

                     =(((uint32_t)(0x00)<<6) | (uint32_t)(16U))

                     =((0x0000 0000<<6) | (0x0000 0010)

                    =0x0000 0010

所以RCU_HXTAL =0x0000 0010也就是16

3.2、rcu_osci_on函数实现

这里先看右边,RCU_HXTAL=0x0000 0010所以osci=0x0000 0010,

所以RCU_BIT_POS(osci)=0x0000 0010 & 0x1F

                                           = 0x0000 0010

                                           =16

BIT(RCU_BIT_POS(osci)) = BIT(16)

                                          = 0x0000 0001 << 16

                                          = 0x0001 0000

RCU这里嵌套了很多层,这里就不详细描述,RCU = 0x4002 1000

左边RCU_REG_VAL(osci) = RCU_REG_VAL(0x0000 0010)

                                            = REG32(RCU +  (0x0000 0010 >> 6))

                                            = REG32(0x4002 1000 +0x0000 0000)

                                            = REG32(0x4002 1000)

REG32(addr)这个宏定义就是把addr这个数转换成地址

所以最后RCU_REG_VAL(osci) |= BIT(RCU_BIT_POS(osci))

              REG32(0x4002 1000) |= 0x0001 0000

也就是把0x0001 0000写入0x4002 1000这个地址中,由于0x4002 1000这个地址是RCU基地址,RCU_CTL偏移地址为0,0x4002 1000这个地址就是RCU_CTL的地址,这个语句就是往RCU_CTL这个寄存器写入0x0001 0000,把RCU_CTL这个寄存器16位写入1,这里查看RCU_CTL寄存器描述,可以得到这个语句就是打开(4 ~ 16Mhz晶体振荡器)

其他函数不详细介绍,请按照“下面我们来看下rcu_osci_on(RCU_HXTAL);这个函数是怎么实现的”步骤来进行分析。

四、SystemCoreClockUpdate函数介绍

由于篇幅有限这里只贴出了使用的代码"//……"这个符号表示中间还有一部分代码被省略了,具体请查看自己代码。

上述函数,获取RCU_CFG0的不同寄存器获取的结果如下:

在函数SystemClock_Config中我们设置RCU_CFG0寄存器中SCSS,PLLSEL,PLLMF位,具体设置请参考函数SystemClock_Config,这里读取出来的可以看出来和我们设置的一样,

HXTAL_VALUE这个宏定义是表示我们晶振是多少兆

由于在SystemClock_Config中我们设置了PLLMF倍频因子是9倍,所以系统时钟是SystemCoreClock=HXTAL_VALUE*9=72000000Hz(72MHz)

五、验证设置结果

通过系统函数获取时钟配置 uint32_t rcu_clock_freq_get(rcu_clock_freq_enum clock)函数可以用来获取时钟参数

这里可以看到我们设置了APB1总线,APB2总线,AHB总线和我们设置的一样,具体可以参考本文“三、系统时钟初始化代码(SystemClock_Config)

本人水平有限,如有错误,欢迎指正,原创不易,转载请注明出处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值