一、时钟系统初始化准备
- 关闭可能影响配置的模块(如Cache、MMU),避免时钟切换时内核不稳定;
- 明确配置目标(如内核主频528MHz、AHB=132MHz、IPG=66MHz)。
读取并修改SCTLR寄存器(CP15协处理器):
- 通过
MRC
指令读取CP15的SCTLR寄存器(系统控制寄存器); - 清零
V
位(bit13,允许异常向量表重定位)、I
位(bit12,关闭I Cache)、C
位(bit2,关闭D Cache)等,确保配置过程中无干扰。
确认外设时钟使能准备:
- 后续需通过
CCM_CCGR0~CCM_CCGR6
寄存器启用外设时钟,初始可先全局使能(如enable_clocks
函数,设置所有CCGR寄存器为0xFFFFFFFF
)。
二、配置核心PLL(重点为ARM_PLL、PLL2、PLL3)
- 配置ARM_PLL(PLL1)以设定内核主频;
- 确认PLL2(528MHz)、PLL3(480MHz)的固定倍频(无需修改,仅需后续配置其PFD)。
操作步骤(以"内核主频528MHz"为例)
切换内核临时时钟源(避免PLL配置时内核停摆):
- 配置
CCM_CCSR
寄存器(时钟控制状态寄存器):CCM->CCSR &= ~(1 << 8)
:设置STEP_SEL
位,使step_clk
时钟源为24MHz晶振;CCM->CCSR |= (1 << 2)
:设置PLL1_SW_CLK_SEL
位,将内核时钟切换到step_clk
(临时24MHz)。
配置ARM_PLL(PLL1)为1056MHz:
- 操作
CCM_ANALOG_PLL_ARM
寄存器(PLL1配置寄存器):- 清零
BYPASS
位(bit16),禁用PLL旁路; - 设置
DIV_SELECT
位(bit12~bit0):根据公式Fout = Fin × (DIV_SELECT / 2.0)
,代入Fout=1056MHz
、Fin=24MHz
,计算得DIV_SELECT=88
; - 置位
ENABLE
位(bit13),使能PLL1输出。
- 清零
设置内核分频(得到目标主频):
- 配置
CCM_CACRR
寄存器(ARM时钟分频寄存器):CCM->CACRR |= (1 << 0)
:设置ARM_PODF
位为2分频(1056MHz / 2 = 528MHz)。
切换回PLL1主时钟:
CCM->CCSR &= ~(1 << 2)
:将PLL1_SW_CLK_SEL
位清零,内核时钟切换回pll1_main_clk
(528MHz)。
三、配置PLL2与PLL3的PFD(相位分数分频器)
- 为外设提供多样化的根时钟源(如PLL2_PFD2供AHB根时钟),需按NXP推荐频率配置8路PFD(PLL2 4路+PLL3 4路)。
关键公式与配置步骤
PFD频率计算:
- PLL2_PFDn:
Fout = 528MHz × 18 / PFDn_FRAC
(PFDn_FRAC
范围12~35); - PLL3_PFDn:
Fout = 480MHz × 18 / PFDn_FRAC
(PFDn_FRAC
范围12~35)。
按NXP推荐值配置PFD:
PFD模块 | 推荐频率 | 计算得PFDn_FRAC |
操作寄存器 |
---|---|---|---|
PLL2_PFD0 | 352MHz | 27(528×18/27=352) | CCM_ANALOG->PFD_528 |
PLL2_PFD1 | 594MHz | 16(528×18/16=594) | CCM_ANALOG->PFD_528 |
PLL2_PFD2 | 396MHz | 24(528×18/24=396) | CCM_ANALOG->PFD_528 |
PLL2_PFD3 | 297MHz | 32(528×18/32=297) | CCM_ANALOG->PFD_528 |
PLL3_PFD0 | 720MHz | 12(480×18/12=720) | CCM_ANALOG->PFD_480 |
PLL3_PFD1 | 540MHz | 16(480×18/16=540) | CCM_ANALOG->PFD_480 |
PLL3_PFD2 | 508.2MHz | 17(480×18/17≈508) | CCM_ANALOG->PFD_480 |
PLL3_PFD3 | 454.7MHz | 19(480×18/19≈455) | CCM_ANALOG->PFD_480 |
寄存器操作细节:
- 先清零
PFDn_FRAC
位(如CCM_ANALOG->PFD_528 &= ~(0x3F << 0)
),避免残留值; - 清零
PFDn_CLKGATE
位(如CCM_ANALOG->PFD_528 &= ~(1 << 7)
),使能PFD输出。
四、配置AHB/IPG/PERCLK根时钟
- 为外设提供标准时钟(AHB=132MHz、IPG=66MHz、PERCLK=66MHz),需从PFD选择输入时钟并分频。
配置步骤(以"PLL2_PFD2=396MHz为输入"为例)
配置AHB_CLK_ROOT(132MHz):
- 选择输入时钟:配置
CCM_CBCMR
寄存器的PRE_PERIPH_CLK_SEL
位(bit19~18),选择PLL2_PFD2为输入; - 分频设置:配置
CCM_CBCDR
寄存器的AHB_PODF
位(bit12~10),设置为3分频(396MHz / 3 = 132MHz)。
配置IPG_CLK_ROOT(66MHz):
- 输入时钟为AHB_CLK_ROOT,配置
CCM_CBCDR
的IPG_PODF
位(bit9~8),设置为2分频(132MHz / 2 = 66MHz)。
配置PERCLK_CLK_ROOT(66MHz):
- 输入时钟为IPG_CLK_ROOT,配置
CCM_CSCMR1
的PERCLK_PODF
位(bit6~0),设置为1分频(66MHz / 1 = 66MHz)。
五、使能外设时钟
- 为具体外设(如GPIO、UART、PWM)启用时钟,避免未使用外设耗电。
操作方式
全局使能(调试阶段便捷使用):
- 通过
enable_clocks
函数,设置CCM_CCGR0~CCM_CCGR6
为0xFFFFFFFF
,启用所有外设时钟。
按需使能(量产阶段节能):
- 每个
CCM_CCGRx
寄存器的每2位控制一个外设时钟,例如:CCM_CCGR0
的bit31~30
控制GPIO2时钟,设置为11
(除停止模式外均使能);- 公式:
CCM->CCGRx |= (0x03 << n)
(n
为外设对应的位偏移)。
六、时钟配置验证
- 确认时钟配置是否生效(如内核主频、根时钟频率)。
验证方法
软件验证:
- 读取
CCM_CACRR
的ARM_PODF
位,确认分频是否正确; - 读取
CCM_ANALOG_PLL_ARM
的DIV_SELECT
位,确认PLL1倍频是否正确; - 通过延时函数间接验证。
硬件验证:
- 若开发板有示波器接口,可测量外设时钟引脚(如UART的PCLK),确认频率是否匹配配置值。
关键代码参考(基于clock.c
)
c
// 1. 全局使能外设时钟
void enable_clocks(void) {
CCM->CCGR0 = 0xFFFFFFFF;
CCM->CCGR1 = 0xFFFFFFFF;
CCM->CCGR2 = 0xFFFFFFFF;
CCM->CCGR3 = 0xFFFFFFFF;
CCM->CCGR4 = 0xFFFFFFFF;
CCM->CCGR5 = 0xFFFFFFFF;
CCM->CCGR6 = 0xFFFFFFFF;
}
// 2. 核心时钟配置(内核528MHz、AHB=132MHz、IPG=66MHz)
void init_clock(void) {
unsigned int t;
// 阶段2:切换内核临时时钟源(24MHz)
CCM->CCSR &= ~(1 << 8); // STEP_SEL=0,step_clk=24MHz
CCM->CCSR |= (1 << 2); // PLL1_SW_CLK_SEL=1,内核时钟=step_clk
// 阶段2:配置ARM_PLL(PLL1=1056MHz)
t = CCM_ANALOG->PLL_ARM;
t &= ~(3 << 14); // 清除BYPASS_CLK_SRC
t |= (1 << 13); // 使能PLL1
t &= ~(0x7F << 0); // 清除DIV_SELECT
t |= (88 << 0); // DIV_SELECT=88(1056MHz=24×88/2)
CCM_ANALOG->PLL_ARM = t;
CCM->CACRR |= (1 << 0); // ARM_PODF=2分频(528MHz)
// 阶段2:切换回PLL1主时钟
CCM->CCSR &= ~(1 << 2); // PLL1_SW_CLK_SEL=0,内核时钟=pll1_main_clk
// 阶段3:配置PLL2_PFD(528MHz衍生)
t = CCM_ANALOG->PFD_528;
t &= ~((0x3F << 0) | (0x3F << 8) | (0x3F << 16) | (0x3F << 24));
t &= ~((1 << 7) | (1 << 15) | (1 << 23) | (1 << 31)); // 使能PFD输出
t |= (27 << 0) | (16 << 8) | (24 << 16) | (32 << 24); // PFD0~PFD3配置
CCM_ANALOG->PFD_528 = t;
// 阶段3:配置PLL3_PFD(480MHz衍生)
t = CCM_ANALOG->PFD_480;
t &= ~((0x3F << 0) | (0x3F << 8) | (0x3F << 16) | (0x3F << 24));
t &= ~((1 << 7) | (1 << 15) | (1 << 23) | (1 << 31)); // 使能PFD输出
t |= (12 << 0) | (16 << 8) | (17 << 16) | (19 << 24); // PFD0~PFD3配置
CCM_ANALOG->PFD_480 = t;
// 阶段4:配置AHB/IPG/PERCLK根时钟
t = CCM->CBCMR;
t &= ~(3 << 18);
t |= (1 << 18); // PRE_PERIPH_CLK_SEL=PLL2_PFD2
CCM->CBCMR = t;
t = CCM->CBCDR;
t &= ~((7 << 10) | (3 << 8));
t |= (2 << 10) | (1 << 8); // AHB_PODF=3分频(132MHz)、IPG_PODF=2分频(66MHz)
CCM->CBCDR = t;
CCM->CSCMR1 &= ~(0X3F << 0); // PERCLK_PODF=1分频(66MHz)
// 阶段5:全局使能外设时钟
enable_clocks();
}