一、内核主频配置原理
通过上一篇博客的分析,知道了系统内核主频是由PLL1进行设置,时钟源PLL1通过设置寄存器CACRR的位[ARM_PODF]来进行频率的分频,注意图片中灰色的/2并没有真的2分频,因为如果分频都是控制寄存器的某个位来进行的,但是这个灰色的/2上并没有显示对应的寄存器位。举个例子如果我们想要得到528MHz的系统内核主频,设置的分频为2分频,那么PLL1的频率就不是图中显示的996MHz了,而是528MHz * 2 = 1056Mhz。查看参考手册知道PLL的输出时钟频率的范围是650MHz到1.3GHz,1056MHz是满足频率选取范围的。
下面我将以内核主频为528MHz为例子来配置内核主频并且代码实现也是设置的内核主频为528MHz。
要设置528MHz的系统内核主频,那么就要设置寄存器CACRR的ARM_PODF为2分频,然后再设置PLL1的频率为1056MHz。寄存器CACRR的位ARM_PODF如图所示:
要设置2分频,就要将ARM_PODF位设置为001。
设置PLL1为1056MHz
上图为时钟切换器(CCM_CLK_Switcher),其子模块接收PLL输出时钟或者PLL旁路时钟,
1:内核系统时钟模块pll1_sw_clk可以通过修改寄存器CCSR的位pll1_sw_clk_set来选择pll1_main_clk和step_clk。
2:当我们在修改系统内核主频的过程中需要给IMX6U一个临时的时钟,系统内核主频,即CPU的时钟频率。修改系统内核主频时启用临时时钟频率是为了确保系统的稳定性和可靠性。举个例子,做心脏移植手术,你不能在把患者的心脏取下来的时候不给患者一个能够维持患者生理机能的东西吧?这样的话患者不就死翘翘了?所以这里需要来一个临时时钟用来维护系统的稳定性。
3:通过寄存器CCSR来控制多路选择器选择step_clk来作为系统内核当前的时钟。但是呢step_clk又是通过一个多路选择器来选择时钟的,但是这都不重要,只是一个临时的时钟怎么选都无所谓,但是这里肯定最好选晶振的osc_clk时钟源。因为这路时钟是从24MHz晶振直接传下来的频率。
4:时钟切换成功之后就可以修改PLL1的频率了,PLL1从24MHz参考时钟合成低抖动时钟。PLL1的时钟输出频率范围为650 MHz至1.3 GHz。输出频率由7位寄存器字段CCM_ANALOG_PLL_ARM[DIV_SELECT]选择。所以接下来看寄存器CCM_ANALOG_PLL_ARM
如图:
通过CCM_ANALOG_PLL_ARM寄存器的DIV_SELECT位(bit6~0)来设置PLL1的频率,公式为:
Output = Fref*DIV_SEL/2 1056=24*DIV_SEL/2=>DIEV_SEL=88。
设置CCM_ANALOG_PLL_ARM寄存器的DIV_SELECT位=88即可。PLL1=1056MHz还要设置CCM_ANALOG_PLL_ARM寄存器的ENABLE位(bit13)为1,也就是使能输出。
5 、在切换回PLL1之前,设置置CACRR寄存器的ARM_PODF=1!!比如说内核只支持最大528MHz(只是比如,实际多少我也不知道),如果没进行分频直接1056MHz进内核会出问题。
其实重点就是这几个多路选择器有点绕,但其实也不是很绕。
原理我们分析完了,接下来就是实际动手修改主频。
二、代码实现
我在上一篇博客的代码的bsp_clk文件的基础上加了一个初始化系统内核时钟的函数
//初始化时钟
void Clk_Init(void)
{
//初始化IMX6U主频为528MHz,先判断内核时钟使用的是哪个时钟,如果是PLL1,就需要切换成另一路step_clk临时时钟
if(((CCM->CCSR)>>2 &0X1) == 0)
{
//要先设置step_sel选择临时晶振为OSC24MHz
CCM->CCSR &= ~(1<<8);
//再设置pll1_sw_clk_sel切换成临时时钟
CCM->CCSR |= 1<<2;
}
//切换完成后就可以设置了,设置PLL_ARM的DIV_SELECT bit[6:0],根据计算的DIV_SELECT = 88,二进制为101 1000
//当看到十进制数88与十六进制数0x7F进行位与操作时,实际上是在对这两个数的二进制表示进行操作。
CCM_ANALOG->PLL_ARM = (1<<13) | ((88<<0) & 0x7f);
//下面这么多行代码用上面这一行代替,包括使能位
// CCM_ANALOG->PLL_ARM |= 1<<3;
// CCM_ANALOG->PLL_ARM |= 1<<4;
// CCM_ANALOG->PLL_ARM |= 1<<6;
// CCM_ANALOG->PLL_ARM &= ~(1<<0);
// CCM_ANALOG->PLL_ARM &= ~(1<<1);
// CCM_ANALOG->PLL_ARM &= ~(1<<5);
//接着使能pll1时钟
// CCM_ANALOG->PLL_ARM |= 1<<13;
//设置完成后不要着急切换时钟,要先进行pll1分频,设置寄存器CACRR的位ARM_PODF bit[2:0],二分频 001
//下面三行代码可以用 CCM->CACRR = 1;代替,因为剩余位没有用
CCM->CACRR |= 1<<0;
CCM->CACRR &= ~(1<<1);
CCM->CACRR &= ~(1<<2);
//将临时时钟切换成pll1
CCM->CCSR &= ~(1<<2);
}
并在main函数中调用了这个函数,代码进行编译,编译成功
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6u -I bsp/clk -I bsp/delay -I bsp/led -I bsp/beep -I bsp/key -I bsp/gpio -o obj/ledc_stm32.o project/ledc_stm32.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6u -I bsp/clk -I bsp/delay -I bsp/led -I bsp/beep -I bsp/key -I bsp/gpio -o obj/main.o project/main.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6u -I bsp/clk -I bsp/delay -I bsp/led -I bsp/beep -I bsp/key -I bsp/gpio -o obj/bsk_clk.o bsp/clk/bsk_clk.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6u -I bsp/clk -I bsp/delay -I bsp/led -I bsp/beep -I bsp/key -I bsp/gpio -o obj/bsp_delay.o bsp/delay/bsp_delay.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6u -I bsp/clk -I bsp/delay -I bsp/led -I bsp/beep -I bsp/key -I bsp/gpio -o obj/bsp_led.o bsp/led/bsp_led.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6u -I bsp/clk -I bsp/delay -I bsp/led -I bsp/beep -I bsp/key -I bsp/gpio -o obj/bsp_beep.o bsp/beep/bsp_beep.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6u -I bsp/clk -I bsp/delay -I bsp/led -I bsp/beep -I bsp/key -I bsp/gpio -o obj/bsp_key.o bsp/key/bsp_key.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -I imx6u -I bsp/clk -I bsp/delay -I bsp/led -I bsp/beep -I bsp/key -I bsp/gpio -o obj/bsp_gpio.o bsp/gpio/bsp_gpio.c
arm-linux-gnueabihf-ld -Timx6u.lds -o clk.elf obj/ledc_stm32.o obj/main.o obj/bsk_clk.o obj/bsp_delay.o obj/bsp_led.o obj/bsp_beep.o obj/bsp_key.o obj/bsp_gpio.o
arm-linux-gnueabihf-objcopy -O binary -S clk.elf clk.bin
arm-linux-gnueabihf-objdump -D -m arm clk.elf > clk.dis
运行成功,由于delay延时函数是在396MHz下设置的函数,原先设置的是每隔2000ms led闪烁一次 在设置完成内核主频是528MHz,我们可以在原先396MHz下录一段led灯闪烁的视频对比当前528MHz下led闪烁的时间间隔。会发现led灯的闪烁时间间隔会短于2000ms。(我本来是设置的500ms的间隔时间,但是500ms的间隔时间对比的不是太明显,所以这里增加为2000ms);不过可以设置成内核时钟为696MHz会很明显观察到变化。