【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
3)对正点原子Linux感兴趣的同学可以加群讨论:935446741
4)关注正点原子公众号,获取最新资料更新
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

第十六章主频和时钟配置实验

在前几章实验中我们都没有涉及到I.MX6U的时钟和主频配置操作,全部使用的默认配置,默认配置下I.MX6U工作频率为396MHz。但是I.MX6U系列标准的工作频率为528MHz,有些型号甚至可以工作到696MHz。本章我们就学习I.MX6U的时钟系统,学习如何配置I.MX6U的系统时钟和其他的外设时钟,使其工作频率为528MHz,其他的外设时钟源都工作在NXP推荐的频率。

16.1 I.MX6U时钟系统详解
I.MX6U的系统主频为528MHz,有些型号可以跑到696MHz,但是默认情况下内部bootrom会将I.MX6U的主频设置为396MHz,这个我们在9.2小节已经讲过了。我们在使用I.MX6U的时候肯定是要发挥它的最大性能,那么主频肯定要设置到528MHz(其它型号可以设置更高,比如696MHz),其它的外设时钟也要设置到NXP推荐的值。I.MX6U的系统时钟在《I.MX6ULL/I.MX6UL参考手册》的第10章“Chapter 10Clock and Power Management”和第18章“Chapter 18Clock Controller Module (CCM)”这两章有详细的讲解。
16.1.1系统时钟来源
打开I.MX6U-ALPHA开发板原理图,开发板时钟原理图如图16.1.1.1所示:

【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.1.1 开发板时钟原理图
从图16.1.1.1可以看出I.MX6U-ALPHA开发板的系统时钟来源于两部分:32.768KHz和24MHz的晶振,其中32.768KHz晶振是I.MX6U 的RTC时钟源,24MHz晶振是I.MX6U内核和其它外设的时钟源,也是我们重点要分析的。
16.1.2 7路PLL时钟源
I.MX6U的外设有很多,不同的外设时钟源不同,NXP将这些外设的时钟源进行了分组,一共有7组,这7组时钟源都是从24MHz晶振PLL而来的,因此也叫做7组PLL,这7组PLL结构如图16.1.1.2所示:

【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.2.1初级PLLs时钟源生成图
图16.1.2.1展示了7个PLL的关系,我们依次来看一下这7个PLL都是什么做什么的:
、 ARM_PLL(PLL1),此路PLL是供ARM内核使用的,ARM内核时钟就是由此PLL生成的,此PLL通过编程的方式最高可倍频到1.3GHz。
、528_PLL(PLL2),此路PLL也叫做System_PLL,此路PLL是固定的22倍频,不可编程修改。因此,此路PLL时钟=24MHz * 22 =528MHz,这也是为什么此PLL叫做528_PLL的原因。此PLL分出了4路PFD,分别为:PLL2_PFD0~PLL2_PFD3,这4路PFD和528_PLL共同作为其它很多外设的根时钟源。通常528_PLL和这4路PFD是I.MX6U内部系统总线的时钟源,比如内处理逻辑单元、DDR接口、NAND/NOR接口等等。
、USB1_PLL(PLL3),此路PLL主要用于USBPHY,此PLL也有四路PFD,为:PLL3_PFD0~PLL3_PFD3,USB1_PLL是固定的20倍频,因此USB1_PLL=24MHz *20=480MHz。USB1_PLL虽然主要用于USB1PHY,但是其和四路PFD同样也可以作为其他外设的根时钟源。
④、USB2_PLL(PLL7,没有写错!就是PLL7,虽然序号标为4,但是实际是PLL7),看名字就知道此路PLL是给USB2PHY使用的。同样的,此路PLL固定为20倍频,因此也是480MHz。
⑤、ENET_PLL(PLL6),此路PLL固定为20+5/6倍频,因此ENET_PLL=24MHz *(20+5/6) = 500MHz。此路PLL用于生成网络所需的时钟,可以在此PLL的基础上生成25/50/100/125MHz的网络时钟。
⑥、VIDEO_PLL(PLL5),此路PLL用于显示相关的外设,比如LCD,此路PLL的倍频可以调整,PLL的输出范围在650MHz~1300MHz。此路PLL在最终输出的时候还可以进行分频,可选1/2/4/8/16分频。
⑦、AUDIO_PLL(PLL4),此路PLL用于音频相关的外设,此路PLL的倍频可以调整,PLL的输出范围同样也是650MHz~1300MHz,此路PLL在最终输出的时候也可以进行分频,可选1/2/4分频。
16.1.3时钟树简介
在上一小节讲解了7路PLL,I.MX6U的所有外设时钟源都是从这7路PLL和有些PLL的PFD而来的,这些外设究竟是如何选择PLL或者PFD的?这个就要借助《IMX6ULL参考手册》里面的时钟树了,在“Chapter 18Clock Controller Module (CCM)”的18.3小节给出了I.MX6U详细的时钟树图,如图16.1.3.1所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.3.1 I.MX6U时钟树
在图16.1.3.1中一共有三部分:CLOCK_SWITCHER、CLOCK ROOT GENERATOR和SYSTEM CLOCKS。其中左边的CLOCK_SWITCHER就是我们上一小节讲解的那7路PLL和8路PFD,右边的SYSTEM CLOCKS就是芯片外设,中间的CLOCK ROOT GENERATOR是最复杂的!这一部分就像“月老”一样,给左边的CLOCK_SWITCHER和右边的SYSTEM CLOCKS进行牵线搭桥。外设时钟源是有多路可以选择的,CLOCK ROOT GENERATOR就负责从7路PLL和8路PFD中选择合适的时钟源给外设使用。具体操作肯定是设置相应的寄存器,我们以ESAI这个外设为例,ESAI的时钟图如图16.1.3.2所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.3.2 ES AI时钟
在图16.1.3.2中我们分为了3部分,这三部分如下:
①、此部分是时钟源选择器,ESAI有4个可选的时钟源:PLL4、PLL5、PLL3_PFD2和pll3_sw_clk。具体选择哪一路作为ESAI的时钟源是由寄存器CCM->CSCMR2的ESAI_CLK_SEL位来决定的,用户可以*配置,配置如图16.1.3.3所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.3.3寄存器CSCMR2的ESAI_CLK_SEL位
②、此部分是ESAI时钟的前级分频,分频值由寄存器CCM_CS1CDR的ESAI_CLK_PRED来确定的,可设置1~8分频,假如现在PLL4=650MHz,我们选择PLL4作为ESAI时钟,前级分频选择2分频,那么此时的时钟就是650/2=325MHz。
③、此部分又是一个分频器,对②中输出的时钟进一步分频,分频值由寄存器CCM_CS1CDR的ESAI_CLK_PODF来决定,可设置1~8分频。假如我们设置为8分频的话,经过此分频器以后的时钟就是325/8=40.625MHz。因此最终进入到ESAI外设的时钟就是40.625MHz。
上面我们以外设ESAI为例讲解了如何根据图16.1.3.1来设置外设的时钟频率,其他的外设基本类似的,大家可以自行分析一下其他的外设。关于外设时钟配置相关内容全部都在《I.MX6ULL参考手册》的第18章。
16.1.4内核时钟设置
I.MX6U的时钟系统前面几节已经分析的差不多了,现在就可以开始设置相应的时钟频率了。先从主频开始,我们将I.MX6U的主频设置为528MHz,根据图16.1.3.2的时钟树可以看到ARM内核时钟如图16.1.4.1所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.4.1 ARM内核时钟树
在图16.1.4.1中各部分如下:
①、内核时钟源来自于PLL1,假如此时PLL1为996MHz。
②、通过寄存器CCM_CACRR的ARM_PODF位对PLL1进行分频,可选择1/2/4/8分频,假如我们选择2分频,那么经过分频以后的时钟频率是996/2=498MHz。
③、大家不要被此处的2分频给骗了,此处没有进行2分频(我就被这个2分频骗了好久,主频一直配置不正确!)。
④、经过第②步2分频以后的498MHz就是ARM的内核时钟,也就是I.MX6U的主频。
经过上面几步的分析可知,假如我们要设置内核主频为528MHz,那么PLL1可以设置为1056MHz,寄存器CCM_CACRR的ARM_PODF位设置为2分频即可。同理,如果要将主频设置为696MHz,那么PLL1就可以设置为696MHz,CCM_CACRR的ARM_PODF设置为1分频即可。现在问题很清晰了,寄存器CCM_CACCR的ARM_PODF位很好设置,PLL1的频率可以通过寄存器CCM_ANALOG_PLL_ARMn来设置。接下来详细的看一下CCM_CACRR和CCM_ANALOG_PLL_ARMn这两个寄存器,CCM_CACRR寄存器结构如图16.1.4.2所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.4.2寄存器CCM_CACRR
寄存器CCM_CACRR只有ARM_PODF位,可以设置为07,分别对应18分频。如果要设置为2分频的话CCM_CACCR就要设置为1。再来看一下寄存器CCM_ANALOG_PLL_ARMn,此寄存器结构如图16.1.4.3所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.4.3寄存器CCM_ANALOG_PLL_ARMn
在寄存器CCM_ANALOG_PLL_ARMn中重要的位如下:
ENABLE: 时钟输出使能位,此位设置为1使能PLL1输出,如果设置为0的话就关闭PLL1输出。
DIV_SELECT: 此位设置PLL1的输出频率,可设置范围为:54~108,PLL1 CLK = Fin*div_seclec/2.0,Fin=24MHz。如果PLL1要输出1056MHz的话,div_select就要设置为88。
在修改PLL1时钟频率的时候我们需要先将内核时钟源改为其他的时钟源,PLL1可选择的时钟源如图16.1.4.4所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.4.4 PLL1时钟开关
①、pll1_sw_clk也就是PLL1的最终输出频率。
②、此处是一个选择器,选择pll1_sw_clk的时钟源,由寄存器CCM_CCSR的PLL1_SW_CLK_SEL位决定pll1_sw_clk是选择pll1_main_clk还是step_clk。正常情况下应该选择pll1_main_clk,但是如果要对pll1_main_clk(PLL1)的频率进行调整的话,比如我们要设置PLL1=1056MHz,此时就要先将pll1_sw_clk切换到step_clk上。等pll1_main_clk调整完成以后再切换回来。
③、此处也是一个选择器,选择step_clk的时钟源,由寄存器CCM_CCSR的STEP_SEL位来决定step_clk是选择osc_clk还是secondary_clk。一般选择osc_clk,也就是24MHz的晶振。
这里我们就用到了一个寄存器CCM_CCSR,此寄存器结构如图16.1.4.5所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.4.5寄存器CCM_CCSR结构图
寄存器CCM_CCSR我们只用到了STEP_SEL、PLL1_SW_CLK_SEL这两个位,一个是用来选择step_clk时钟源的,一个是用来选择pll1_sw_clk时钟源的。
到这里,修改I.MX6U主频的步骤就很清晰了,修改步骤如下:
①、设置寄存器CCSR的STEP_SEL位,设置step_clk的时钟源为24M的晶振。
②、设置寄存器CCSR的PLL1_SW_CLK_SEL位,设置pll1_sw_clk的时钟源为step_clk=24MHz,通过这一步我们就将I.MX6U的主频先设置为24MHz,直接来自于外部的24M晶振。
③、设置寄存器CCM_ANALOG_PLL_ARMn,将pll1_main_clk(PLL1)设置为1056MHz。
④、设置寄存器CCSR的PLL1_SW_CLK_SEL位,重新将pll1_sw_clk的时钟源切换回pll1_main_clk,切换回来以后的pll1_sw_clk就等于1056MHz。
⑤、最后设置寄存器CCM_CACRR的ARM_PODF为2分频,I.MX6U的内核主频就为1056/2=528MHz。
16.1.5PFD时钟设置
设置好主频以后我们还需要设置好其他的PLL和PFD时钟,PLL1上一小节已经设置了,PLL2、PLL3和PLL7固定为528MHz、480MHz和480MHz,PLL4~PLL6都是针对特殊外设的,用到的时候再设置。因此,接下来重点就是设置PLL2和PLL3的各自4路PFD,NXP推荐的这8路PFD频率如表16.1.5.1所示:
PFD NXP推荐频率值
PLL2_PFD0 352MHz
PLL2_PFD1 594MHz
PLL2_PFD2 400MHz(实际为396MHz)
PLL2_PFD3 297MHz
PLL3_PFD0 720MHz
PLL3_PFD1 540MHz
PLL3_PFD2 508.2MHz
PLL3_PFD3 454.7MHz
表16.1.5.1NXP推荐的PFD频率
先设置PLL2的4路PFD频率,用到寄存器是CCM_ANALOG_PFD_528n,寄存器结构如图16.1.5.1所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.5.1寄存器CCM_ANALOG_PFD_528n结构
从图16.1.5.1可以看出,寄存器CCM_ANALOG_PFD_528n其实分为四组,分别对应PFD0~PFD3,每组8个bit,我们就以PFD0为例,看一下如何设置PLL2_PFD0的频率。PFD0对应的寄存器位如下:
PFD0_FRAC: PLL2_PFD0的分频数,PLL2_PFD0的计算公式为52818/PFD0_FRAC,此为可设置的范围为12~35。如果PLL2_PFD0的频率要设置为352MHz的话PFD0_FRAC=52818/352=27。
PFD0_STABLE: 此位为只读位,可以通过读取此位判断PLL2_PFD0是否稳定。
PFD0_CLKGATE: PLL2_PFD0输出使能位,为1的时候关闭PLL2_PFD0的输出,为0的时候使能输出。
如果我们要设置PLL2_PFD0的频率为352MHz的话就需要设置PFD0_FRAC为27,PFD0_CLKGATE为0。PLL2_PFD1PLL2_PFD3设置类似,频率计算公式都是528*18/PFDX_FRAC(X=13),因此PLL2_PFD1=594MHz的话,PFD1_FRAC=16;PLL2_PFD2=400MHz的话PFD2_FRAC不能整除,因此取最近的整数值,即PFD2_FRAC=24,这样PLL2_PFD2实际为396MHz;PLL2_PFD3=297MHz的话,PFD3_FRAC=32。
接下来设置PLL3_PFD0~PLL3_PFD3这4路PFD的频率,使用到的寄存器是CCM_ANALOG_PFD_480n,此寄存器结构如图16.1.5.2所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.5.2寄存器CCM_ANALOG_PFD_480n结构
从图16.1.5.2可以看出,寄存器CCM_ANALOG_PFD_480n和CCM_ANALOG_PFD_528n的结构是一模一样的,只是一个是PLL2的,一个是PLL3的。寄存器位的含义也是一样的,只是频率计算公式不同,比如PLL3_PFDX=480*18/PFDX_FRAC(X=0~3)。如果PLL3_PFD0=720MHz的话,PFD0_FRAC=12;如果PLL3_PFD1=540MHz的话,PFD1_FRAC=16;如果PLL3_PFD2=508.2MHz的话,PFD2_FRAC=17;如果PLL3_PFD3=454.7MHz的话,PFD3_FRAC=19。
16.1.6 AHB、IPG和PERCLK根时钟设置
7路PLL和8路PFD设置完成以后最后还需要设置AHB_CLK_ROOT和IPG_CLK_ROOT的时钟,I.MX6U外设根时钟可设置范围如图16.1.6.1所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.6.1外设根时钟可设置范围
图16.1.6.1给出了大多数外设的根时钟设置范围,AHB_CLK_ROOT最高可以设置132MHz,IPG_CLK_ROOT和PERCLK_CLK_ROOT最高可以设置66MHz。那我们就将AHB_CLK_ROOT、IPG_CLK_ROOT和PERCLK_CLK_ROOT分别设置为132MHz、66MHz、66MHz。AHB_CLK_ROOT和IPG_CLK_ROOT的涉及如图16.1.6.2所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.6.2总线时钟图
图16.1.6.2就是AHB_CLK_ROOT和IPG_CLK_ROOT的时钟图,图中分为了3部分。
①、此选择器用来选择pre_periph_clk的时钟源,可以选择PLL2、PLL2_PFD2、PLL2_PFD0和PLL2_PFD2/2。寄存器CCM_CBCMR的PRE_PERIPH_CLK_SEL位决定选择哪一个,默认选择PLL2_PFD2,因此pre_periph_clk=PLL2_PFD2=396MHz。
②、此选择器用来选择periph_clk的时钟源,由寄存器CCM_CBCDR的PERIPH_CLK_SEL位与PLL_bypass_en2组成的或来选择。当CCM_CBCDR的PERIPH_CLK_SEL位为0的时候periph_clk=pr_periph_clk=396MHz。
③、通过CBCDR的AHB_PODF位来设置AHB_CLK_ROOT的分频值,可以设置1~8分频,如果想要AHB_CLK_ROOT=132MHz的话就应该设置为3分频:396/3=132MHz。图16.1.2中虽然写的是默认4分频,但是I.MX6U的内部bootrom将其改为了3分频!
④、通过CBCDR的IPG_PODF位来设置IPG_CLK_ROOT的分频值,可以设置1~4分频,IPG_CLK_ROOT时钟源是AHB_CLK_ROOT,要想IPG_CLK_ROOT=66MHz的话就应该设置2分频:132/2=66MHz。
最后要设置的就是PERCLK_CLK_ROOT时钟频率,其时钟结构图如图16.1.6.3所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.6.3 PERCLK_CLK_ROOT时钟结构
从图16.1.6.3可以看出,PERCLK_CLK_ROOT来源有两种:OSC(24MHz)和IPG_CLK_ROOT,由寄存器CCM_CSCMR1的PERCLK_CLK_SEL位来决定,如果为0的话PERCLK_CLK_ROOT的时钟源就是IPG_CLK_ROOT=66MHz。可以通过寄存器CCM_CSCMR1的PERCLK_PODF位来设置分频,如果要设置PERCLK_CLK_ROOT为66MHz的话就要设置为1分频。
在上面的设置中用到了三个寄存器:CCM_CBCDR、CCM_CBCMR和CCM_CSCMR1,我们依次来看一下这些寄存器,CCM_CBCDR寄存器结构如图16.1.6.4所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.6.4寄存器CCM_CBCDR结构
寄存器CCM_CBCDR各个位的含义如下:
PERIPH_CLK2_PODF:periph2时钟分频,可设置07,分别对应18分频。
PERIPH2_CLK_SEL:选择peripheral2的主时钟,如果为0的话选择PLL2,如果为1的话选择periph2_clk2_clk。修改此位会引起一次与MMDC的握手,所以修改完成以后要等待握手完成,握手完成信号由寄存器CCM_CDHIPR中指定位表示。
PERIPH_CLK_SEL:peripheral主时钟选择,如果为0的话选择PLL2,如果为1的话选择periph_clk2_clock。修改此位会引起一次与MMDC的握手,所以修改完成以后要等待握手完成,握手完成信号由寄存器CCM_CDHIPR中指定位表示。
AXI_PODF:axi时钟分频,可设置07,分别对应18分频。
AHB_PODF:ahb时钟分频,可设置07,分别对应18分频。修改此位会引起一次与MMDC的握手,所以修改完成以后要等待握手完成,握手完成信号由寄存器CCM_CDHIPR中指定位表示。
IPG_PODF:ipg时钟分频,可设置03,分别对应14分频。
AXI_ALT_CLK_SEL:axi_alt时钟选择,为0的话选择PLL2_PFD2,如果为1的话选择PLL2_PFD1。
AXI_CLK_SEL:axi时钟源选择,为0的话选择periph_clk,为1的话选择axi_alt时钟。
FABRIC_MMDC_PODF:fabric/mmdc时钟分频设置,可设置07,分别对应18分频。
PERIPH2_CLK2_PODF:periph2_clk2的时钟分频,可设置07,分别对应18分频。
接下来看一下寄存器CCM_CBCMR,寄存器结构如图16.1.6.5所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.6.5寄存器CCM_CBCMR结构
寄存器CCM_CBCMR各个位的含义如下:
LCDIF1_PODF:lcdif1的时钟分频,可设置07,分别对应18分频。
PRE_PERIPH2_CLK_SEL:pre_periph2时钟源选择,00选择PLL2,01选择PLL2_PFD2,10选择PLL2_PFD0,11选择PLL4。
PERIPH2_CLK2_SEL:periph2_clk2时钟源选择为0的时候选择pll3_sw_clk,为1的时候选择OSC。
PRE_PERIPH_CLK_SEL:pre_periph时钟源选择,00选择PLL2,01选择PLL2_PFD2,10选择PLL2_PFD0,11选择PLL2_PFD2/2。
PERIPH_CLK2_SEL:peripheral_clk2时钟源选择,00选择pll3_sw_clk,01选择osc_clk,10选择pll2_bypass_clk。
最后看一下寄存器CCM_CSCMR1,寄存器结构如图16.1.6.6所示:
【正点原子Linux连载】第十六章主频和时钟配置实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

图16.1.6.6寄存器CCM_CSCMR1结构
此寄存器主要用于外设时钟源的选择,比如QSPI1、ACLK、GPMI、BCH等外设,我们重点看一下下面另个未:
PERCLK_CK_SEL:perclk时钟源选择,为0的话选择ipgclk,为1的话选择osc clk。
PERCLK_PODF:perclk的时钟分频,可设置07,分别对应18分频。
在修改如下时钟选择器或者分频器的时候会引起与MMDC的握手发生:
①、mmdc_podf
②、periph_clk_sel
③、periph2_clk_sel
④、arm_podf
⑤、ahb_podf
发生握手信号以后需要等待握手完成,寄存器CCM_CDHIPR中保存着握手信号是否完成,如果相应的位为1的话就表示握手没有完成,如果为0的话就表示握手完成,很简单,这里就不详细的列举寄存器CCM_CDHIPR中的各个位了。
另外在修改arm_podf和ahb_podf的时候需要先关闭其时钟输出,等修改完成以后再打开,否则的话可能会出现在修改完成以后没有时钟输出的问题。本教程需要修改寄存器CCM_CBCDR的AHB_PODF位来设置AHB_ROOT_CLK的时钟,所以在修改之前必须先关闭AHB_ROOT_CLK的输出。但是笔者没有找到相应的寄存器,因此目前没法关闭,那也就没法设置AHB_PODF了。不过AHB_PODF内部bootrom设置为了3分频,如果pre_periph_clk的时钟源选择PLL2_PFD2的话,AHB_ROOT_CLK也是396MHz/3=132MHz。
至此,I.MX6U的时钟系统就讲解完了,I.MX6U的时钟系统还是很复杂的,大家要结合《I.MX6ULL参考手册》中时钟相关的结构图来学习。本章我们也只是讲解了如何进行主频、PLL、PFD和一些总线时钟的设置,关于具体的外设时钟设置我们在学习到的时候在详细的讲解。
16.2硬件原理分析
时钟原理图分析参考16.1.1小节。
16.3实验程序编写
本实验对应的例程路径为:开发板光盘-> 1、裸机例程->8_clk。
本试验在上一章试验“7_key”的基础上完成,因为本试验是配置I.MX6U的系统时钟,因此我们直接在文件“bsp_clk.c”上做修改,修改bsp_clk.c的内容如下:

示例代码16.3.1 bsp_clk.c文件代码
1   #include "bsp_clk.h"
2
3/***************************************************************
4   Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
5文件名   : bsp_clk.c
6作者     : 左忠凯
7版本     : V1.0
8描述     : 系统时钟驱动。
9其他     : 无
10论坛     : www.openedv.com
11日志     : 初版V1.0 2019/1/3 左忠凯创建
12
13	V2.0     2019/1/3 左忠凯修改
14	添加了函数imx6u_clkinit(),完成I.MX6U的系统时钟初始化
15  ***************************************************************/
16
17/*
18   * @description : 使能I.MX6U所有外设时钟
19   * @param       : 无
20   * @return      : 无
21   */
22void clk_enable(void)
23{
24      CCM->CCGR0 =0XFFFFFFFF;
25      CCM->CCGR1 =0XFFFFFFFF;
26      CCM->CCGR2 =0XFFFFFFFF;
27      CCM->CCGR3 =0XFFFFFFFF;
28      CCM->CCGR4 =0XFFFFFFFF;
29      CCM->CCGR5 =0XFFFFFFFF;
30      CCM->CCGR6 =0XFFFFFFFF;
31}
32
33/*
34   * @description : 初始化系统时钟528Mhz,并且设置PLL2和PLL3各个
35                       PFD时钟,所有的时钟频率均按照I.MX6U官方手册推荐的值.
36   * @param       : 无
37   * @return      : 无
38   */
39void imx6u_clkinit(void)
40{
41unsignedint reg =0;
42/* 1、设置ARM内核时钟为528MHz */
43/* 1.1、判断当使用哪个时钟源启动的,正常情况下是由pll1_sw_clk驱动的,而
44       *      pll1_sw_clk有两个来源:pll1_main_clk和tep_clk,如果要
45       *      让内核跑到528M,那必须选择pll1_main_clk作为pll1的时钟源。
46       *      如果我们要修改pll1_main_clk时钟的话就必须先将pll1_sw_clk从
47       *      pll1_main_clk切换到step_clk,当修改完以后再将pll1_sw_clk切换
48       *      回pll1_main_cl,step_clk等于24MHz。
49       */
50
51if((((CCM->CCSR)>>2)&0x1)==0)	/*   pll1_main_clk? */
52{
53          CCM->CCSR &=~(1<<8);/* 配置step_clk时钟源为24MH OSC */
54          CCM->CCSR |=(1<<2);/* 配置pll1_sw_clk时钟源为step_clk */
55}
56
57/* 1.2、设置pll1_main_clk为1056MHz,也就是528*2=1056MHZ,
58       *      因为pll1_sw_clk进ARM内核的时候会被二分频!
59       *      配置CCM_ANLOG->PLL_ARM寄存器
60       *      bit13: 1 使能时钟输出
61       *      bit[6:0]: 88, 由公式:Fout = Fin * div_select / 2.0,
62       *      1056=24*div_select/2.0, 得出:div_select=88。
63       */
64      CCM_ANALOG->PLL_ARM =(1<<13)|((88<<0)&0X7F);
65      CCM->CCSR &=~(1<<2);/* 将pll_sw_clk时钟切换回pll1_main_clk */
66      CCM->CACRR =1;/* ARM内核时钟为pll1_sw_clk/2=1056/2=528Mhz */
67
68/* 2、设置PLL2(SYS PLL)各个PFD */
69      reg = CCM_ANALOG->PFD_528;
70      reg &=~(0X3F3F3F3F);	/* 清除原来的设置	*/
71      reg |=32<<24;	/* PLL2_PFD3=528*18/32=297Mhz   */
72      reg |=24<<16;	/* PLL2_PFD2=528*18/24=396Mhz  	*/
73      reg |=16<<8;	/* PLL2_PFD1=528*18/16=594Mhz   */
74      reg |=27<<0;	/* PLL2_PFD0=528*18/27=352Mhz   */
75      CCM_ANALOG->PFD_528=reg;	/* 设置PLL2_PFD0~3                	*/
76
77/* 3、设置PLL3(USB1)各个PFD */
78      reg =0;/* 清零   */
79      reg = CCM_ANALOG->PFD_480;
80      reg &=~(0X3F3F3F3F);	/* 清除原来的设置		*/
81      reg |=19<<24;	/* PLL3_PFD3=480*18/19=454.74Mhz    */
82      reg |=17<<16;	/* PLL3_PFD2=480*18/17=508.24Mhz    */
83      reg |=16<<8;	/* PLL3_PFD1=480*18/16=540Mhz       	*/
84      reg |=12<<0;	/* PLL3_PFD0=480*18/12=720Mhz       */
85      CCM_ANALOG->PFD_480=reg;/* 设置PLL3_PFD0~3               	*/
86
87/* 4、设置AHB时钟最小6Mhz,最大132Mhz */
88      CCM->CBCMR &=~(3<<18);/* 清除设置*/
89      CCM->CBCMR |=(1<<18);/* pre_periph_clk=PLL2_PFD2=396MHz */
90      CCM->CBCDR &=~(1<<25);/* periph_clk=pre_periph_clk=396MHz */
91while(CCM->CDHIPR &(1<<5));/* 等待握手完成 */
92
93/* 修改AHB_PODF位的时候需要先禁止AHB_CLK_ROOT的输出,但是
94       * 我没有找到关闭AHB_CLK_ROOT输出的的寄存器,所以就没法设置。
95       * 下面设置AHB_PODF的代码仅供学习参考不能直接拿来使用!!
96       * 内部boot rom将AHB_PODF设置为了3分频,即使我们不设置AHB_PODF,
97       * AHB_ROOT_CLK也依旧等于396/3=132Mhz。
98       */
99  #if0
100/* 要先关闭AHB_ROOT_CLK输出,否则时钟设置会出错 */
101     CCM->CBCDR &=~(7<<10);/* CBCDR的AHB_PODF清零 */
102     CCM->CBCDR |=2<<10;/* AHB_PODF 3分频,AHB_CLK_ROOT=132MHz */
103while(CCM->CDHIPR &(1<<1));/* 等待握手完成 */
104 #endif
105
106/* 5、设置IPG_CLK_ROOT最小3Mhz,最大66Mhz */
107     CCM->CBCDR &=~(3<<8);	/* CBCDR的IPG_PODF清零 */
108     CCM->CBCDR |=1<<8;	/* IPG_PODF 2分频,IPG_CLK_ROOT=66MHz */
109
110/* 6、设置PERCLK_CLK_ROOT时钟 */
111     CCM->CSCMR1 &=~(1<<6);/* PERCLK_CLK_ROOT时钟源为IPG */
112     CCM->CSCMR1 &=~(7<<0);/* PERCLK_PODF位清零,即1分频 */
113}
文件bsp_clk.c中一共有两个函数:clk_enable和imx6u_clkinit,其中函数clk_enable前面已经讲过了,就是使能I.MX6U的所有外设时钟。函数imx6u_clkinit才是本章的重点,imx6u_clkinit先设置系统主频为528MHz,然后根据我们上一小节分析的I.MX6U时钟系统来设置8路PFD,最后设置AHB、IPG和PERCLK的时钟频率。
在bsp_clk.h文件中添加函数imx6u_clkinit的声明,最后修改main.c文件,在main函数里面调用imx6u_clkinit来初始化时钟,如下所示:
示例代码16.3.2 main函数
1int main(void)
2{
3	int i =0;
4	int keyvalue =0;
5	unsignedchar led_state = OFF;
6	unsignedchar beep_state = OFF;
7
8	imx6u_clkinit();	/* 初始化系统时钟	*/
9	clk_enable();		/* 使能所有的时钟	*/
10	led_init();	/* 初始化led         	*/
11	beep_init();	/* 初始化beep        	*/
12	key_init();	/* 初始化key        	*/
13
14	/* 省略掉其它代码 */
15}
上述代码的第8行就是时钟初始化函数,时钟初始化函数最好放到最开始的地方调用。

16.4编译下载验证
16.4.1编写Makefile和链接脚本
因为本章是在试验“7_key”上修改的,而且本章试验没有添加任何新的文件,因此只需要修改Makefile的变量TARGET为“clk”即可,如下所示:
TARGET ?= clk
链接脚本保持不变。
16.4.2编译下载
使用Make命令编译代码,编译成功以后使用软件imxdownload将编译完成的clk.bin文件下载到SD卡中,命令如下:
chmod 777 imxdownload //给予imxdownload可执行权限,一次即可
./imxdownload clk.bin /dev/sdd //烧写到SD卡中
烧写成功以后将SD卡插到开发板的SD卡槽中,然后复位开发板。本试验效果其实和试验“7_key”一样,但是LED灯的闪烁频率相比试验“7_key”要快一点。因为试验“7_key”的主频是396MHz,而本试验的主频被配置成了528MHz,因此代码执行速度会变快,所以延时函数的运行就会加快。

上一篇:javascript的一些小demo


下一篇:PAT乙级 1026 程序运行时间 (15分)