在嵌入式开发中,时钟配置是至关重要的一部分。它影响整个系统的运行速度、定时和能耗等多个方面。
作为一位合格的嵌入式开发工程师,总不能连内核、外设、总线各自的频率都不清晰吧?
配置内核时钟
配置时钟最简单直接的方式就是看图了,下图的ARM
就表示内核的时钟,它是由PLL1
经过分频就得到,即改变PLL1
频率和分频系数即可配置内核时钟
-
注意:这里的第二个分频
ARM domain divider
实际是不会分频的
《IMX6ULL参考手册》——CCM Clock Tree
-
要改变PLL1频率需要先将内核使用的时钟源切换成其他时钟源,比如24MHz晶振
-
如下图可知,可以通过
CCSR
寄存器配置CCM->CCSR &= ~(1 << 8); // 配置step_clk时钟源为24MH OSC CCM->CCSR |= (1 << 2); // 配置pll1_sw_clk时钟源为step_clk
《IMX6ULL参考手册》——Figure 18-4
然后开始配置PLL1频率,这里Fin = 24MHz,将DIV_SELECT配置成66,及PLL1.Fout = 24*66/2MHz = 792MHz
CCM_ANALOG->PLL_ARM = (1 << 13) | (66UL << 0);
并配置CACRR[ARM_PODF]
将分频系数设置为1
CCM->CACRR = 0;
最后将时钟源切换回PLL1
,这时内核的时钟就是792MHz
了
CCM->CCSR &= ~(1 << 2);
配置其余PLL时钟
PPL2-5都是固定频率,PLL4-5用于音视频,这里不做讨论,仅分配PPL2-3的各个PFD
时钟 | 频率 |
---|---|
PLL2 | 528MHz |
PLL3 | 480MHz |
PLL4 | 650MHz |
PLL5 | 650MHz |
// 设置PLL2(SYS PLL)各个PFD
reg = CCM_ANALOG->PFD_528;
reg &= ~(0X3F3F3F3F); // 清除原来的设置
reg |= 32 << 24; // PLL2_PFD3=528*18/32=297Mhz
reg |= 24 << 16; // PLL2_PFD2=528*18/24=396Mhz(DDR使用的时钟,最大400Mhz)
reg |= 16 << 8; // PLL2_PFD1=528*18/16=594Mhz
reg |= 27 << 0; // PLL2_PFD0=528*18/27=352Mhz
CCM_ANALOG->PFD_528 = reg; // 设置PLL2_PFD0~3
// 设置PLL3(USB1)各个PFD
reg = CCM_ANALOG->PFD_480;
reg &= ~(0X3F3F3F3F); // 清除原来的设置
reg |= 19 << 24; // PLL3_PFD3=480*18/19=454.74Mhz
reg |= 17 << 16; // PLL3_PFD2=480*18/17=508.24Mhz
reg |= 16 << 8; // PLL3_PFD1=480*18/16=540Mhz
reg |= 12 << 0; // PLL3_PFD0=480*18/12=720Mhz
CCM_ANALOG->PFD_480 = reg; // 设置PLL3_PFD0~3
配置总线时钟
Clock Root | Default Frequency (MHz) | Maximum Frequency (MHz) |
---|---|---|
AXI_CLK_ROOT | 12 | 264 |
AHB_CLK_ROOT | 6 | 132 |
PERCLK_CLK_ROOT | 3 | 66 |
IPG_CLK_ROOT | 3 | 66 |
由上图可知:
-
AXI总线时钟由
CBCDR
寄存器的axi_alt_clk_sel
、axi_clk_sel
、axi_podf
控制 -
AHB总线时钟由
CBCMR[pre_periph_clk_sel]
和CBCDR
寄存器的periph_clk_sel
、ahb_podf控制
-
IPG总线(APB)由AHB分配而来,分配系数由
CBCDR[IPG_PODF]
控制,默认2分频 -
PERCLK_CLK_ROOT由IPG_CLK_ROOT分配而来,由
CSCMR1[PERCLK_CLK_SEL]
和CSCMR1[PERCLK_PODF]
设置AXI时钟(198MHz)
reg = CCM->CBCDR;
reg &= ~(1 << 7); // sel PLL2 PFD2 = 396MHz
reg |= 1 << 6; // AXI_SEL = 1 alt clk
reg &= ~(0x7 << 16);
reg |= 1 << 16; // AXI_PODF divide by 2 = 198MHz
CCM->CBCDR = reg;
while(CCM->CDHIPR); // 等待握手完成
设置AHB时钟(132MHz)
reg = CCM->CBCMR;
reg &= ~(3 << 18);
reg |= (1 << 18); // pre_periph_clk = PLL2_PFD2 = 396MHz
CCM->CBCMR = reg;
reg = CCM->CBCDR;
reg &= ~(1 << 25); // periph_clk = pre_periph_clk = 396MHz
reg &= ~(0x7 << 10);
reg |= 2 << 10; // AHB_PODF divide by 3 = 132MHz
CCM->CBCDR = reg;
while(CCM->CDHIPR); // 等待握手完成
设置IPG时钟(66MHz)
reg = CCM->CBCDR;
reg &= ~(3 << 8); // CBCDR的IPG_PODF清零
reg |= 1 << 8; // IPG_PODF divide by 2 = 66MHz
CCM->CBCDR = reg;
设置PERCLK_CLK_ROOT时钟(66MHz)
reg = CCM->CSCMR1;
reg &= ~(1 << 6); // PERCLK_CLK_ROOT时钟源为IPG
reg &= ~(7 << 0); // PERCLK_PODF位清零,即1分频
CCM->CSCMR1 = reg;