前面已经完成了 PLL1 和 8 路 PFD 的初始化,至于其他 PLL 路,等实际需要的时候再初始化也不迟。接下来我们就挑选几个具体的外设时钟进行配置。
假设我们要初始化下面两个根时钟PERCLK_CLK_ROOT、IPG_CLK_ROOT。(中途可能还涉及到根时钟 AHB_CLK_ROOT)
目录
一、路线选择
1、PERCLK_CLK_ROOT 路线分析
2、IPG_CLK_ROOT 路线分析
3、AHB_CLK_ROOT 路线分析
二、寄存器分析
1、AHB_CLK_ROOT 路线寄存器分析
(1) CBCMR 寄存器的 PRE_PERIPH_CLK_SEL 位
(2) CBCDR 寄存器的 PERIPH_CLK_SEL 位
(3) 总结
2、IPG_CLK_ROOT 路线寄存器分析
3、PERCLK_CLK_ROOT 路线寄存器分析
一、路线选择
1、PERCLK_CLK_ROOT 路线分析
PERCLK_CLK_ROOT 的时钟来源有两个,一个是直接来自于晶振 OSC,一个是来自于 IPG_CLK_ROOT。选择哪一个由 CSCMR1 寄存器的 PERCLK_CLK_SEL 位决定,分频数由 PERCLK_PODF 位来决定。(我们实际进行的是“选择2”)
配置步骤如下:
- 第一步,设置 CSCMR1 寄存器的 PERCLK_CLK_SEL 位,选择时钟源 IPG_CLK_ROOT
- 第二步,设置 CSCMR1 寄存器的 PERCLK_PODF 位,设置成 1 分频
2、IPG_CLK_ROOT 路线分析
IPG_CLK_ROOT 的时钟源只有一个,来自于上面的 AHB_CLK_ROOT,一环扣一环,所以接下来的关键就是分析 AHB_CLK_ROOT 的时钟来源。
配置步骤如下:
- 只需要设置 CBCDR 寄存器的 IPG_PODF 位,设置成 2 分频
3、AHB_CLK_ROOT 路线分析
按照箭头顺序,配置步骤如下:
- 设置 CBCMR 寄存器的 PRE_PERIPH_CLK_SEL 位,选择 PLL2_PFD2 时钟源
- 设置 CBCDR 寄存器的 PERIPH_CLK_SEL 位,选择下面那条路线
注意:原本要设置 CBCDR 的 AHB_PODF 位,虽然这里 CBCDR 写的是 4 分频,但是内部 boot rom已初始化成了 3 分频,所以这一步就无需做
二、寄存器分析
1、AHB_CLK_ROOT 路线寄存器分析
这一步要做的是:
- 设置 CBCMR 寄存器的 PRE_PERIPH_CLK_SEL 位,选择 PLL2_PFD2 时钟源
- 设置 CBCDR 寄存器的 PERIPH_CLK_SEL 位,设为 0
(1) CBCMR 寄存器的 PRE_PERIPH_CLK_SEL 位
我们先找到 CBCMR 寄存器的 PRE_PERIPH_CLK_SEL 位,设为 01。
(2) CBCDR 寄存器的 PERIPH_CLK_SEL 位
然后是 CBCDR 寄存器的 PERIPH_CLK_SEL 位,设为 0。这里需要搭配总线设计图来看(在第18章的functional description)
我们还需要注意到 PERIPH_CLK_SEL 位的第二个提示信息(NOTE),无论哪个选择都会涉及与MMDC的握手,这个需要参考 CCDR 和 CDHIPR 寄存器的 handshake bypass 和 busy字段。
CCDR 的 MMDC_CH0_ MASK 位
凡是涉及 mmdc_ch0_axi_podf 字段或者 periph_clk_sel 字段的变化,都需要修改下面这个字段,是允许握手还是屏蔽握手。很显然选择允许,MMDC_CH0_ MASK 位 设为 0。
CDHIPR 的 PERIPH_CLK_ SEL_BUSY 位
判断 PERIPH_CLK_SEL 是否在处理其他选择器的选择事件。
- 0:如果处于空闲状态,便可以处理 CBCDR 寄存器的 PERIPH_CLK_SEL 位的选择
- 1:如果处于忙线状态,说明需要先等待上一个选择器的占用结束
(3) 总结
寄存器: CCM_CBCMR
基地址: 0x20C4018
初始化操作:
CCM_CBCMR &= ~(3 << 18); // 19-18 位清零
CCM_CBCMR |= (1 << 18); // 19-18 设为01
/*
* 在操作CCM_CBCDR之前需要做两步:
* 1、允许和MMDC握手 —— CCDR 寄存器
* 2、检查 PERIPH_CLK_SEL 是否忙线 —— CDHIPR 寄存器
*
* 做完上述两步以后才是操作 CCM_CBCDR 寄存器
*/
寄存器: CCM_CCDR
基地址: 0x20C4004
初始化操作:
CCM_CCDR &= ~(1 << 17); // 第 17 位置0
寄存器: CCM_CDHIPR
基地址: 0x20C4048
初始化操作:
while((CCM_CDHIPR >> 5) & 0x01 != 0); // 为 0 时表示空闲,跳出循环
寄存器: CCM_CBCDR
基地址: 0x20C4014
初始化操作:
CCM_CBCDR &= ~(1 << 25); // 第 25 位设为0
2、IPG_CLK_ROOT 路线寄存器分析
这一步比较简单,只需要找到 CBCDR 寄存器的 IPG_PODF 位,设置成 2 分频
寄存器: CCM_CBCDR
基地址: 0x20C4014
初始化操作:
CCM_CBCDR &= ~(3 << 8); // 9-8 位清零
CCM_CBCDR |= (1 << 8); // 9-8 设为01
3、PERCLK_CLK_ROOT 路线寄存器分析
这一步要做的是:
- 第一步,设置 CSCMR1 寄存器的 PERCLK_CLK_SEL 位,选择时钟源 IPG_CLK_ROOT
- 第二步,设置 CSCMR1 寄存器的 PERCLK_PODF 位,设置成 1 分频
寄存器: CCM_CSCMR1
基地址: 0x20C401C
初始化操作:
CCM_CSCMR1 &= ~(0x7F); // 6-0 位清零