完成IMX6UL开发板的各路PLL时钟配置

        在之前我们将开发板上的内核主频配置完成了,但是在之前分析时钟源和时钟树的时候发现还有其他PLL时钟源,所以这一节实验还是要在上一节配置完成内核时钟频率的基础上继续配置剩下的各路PLL时钟源,以及时钟树,受限于篇幅原因所以分开呈两节

       如下方上一节我们配置是内核时钟,ARM PLL,PLL4.5.6.7在目前都不会使用到,他们的作用博主在IMX6ULL时钟树原理讲解(超详细)-CSDN博客这期帖子有描述过
 

        USB1 PLL(PLL3)其实并不需要被我们配置,虽然它像ARM PLL一样有一个DIV_SELECT选择段,但是按照手册上的意思来说,该锁相环在正常情况下应始终设置为480MHz


        那么对于System(PLL2)也是同样的,并不需要被配置,还是只是需要默认528MHz就可以了,最后一段话的意思在于
        虽然这个PLL确实有一个DIV_SELECT寄存器字段,但是这个PLL的目的是只会在528mhz的默认频率下运行

       两个PLL时钟源的时钟频率都是被默认设置好了,但是从时钟源中分频出去的PFD是需要我们进行配置的,例如PLL2下的PFD0等,它们各自都有四个PFD,一共就是8个需要进行初始化

        首先需要找到PLL2控制PFD的相关寄存器,下面的英文也说明了
        PFD_528控制寄存器用于控制PFD(相位频率检测器)时钟生成。该寄存器控制3相分数时钟分频器。这些寄存器中的值决定了分数时钟频率。大意也就是控制PLL2的PFD的寄存器

        那么在这里也可以看到,总共是由四个PFD,我们在时钟树上也看到了确实是有四个PFD相对应上

        这里博主截取PFD0_FRAC的配置方法,其他PFD的配置方式与PFD0也是相同的,这里就以PFD0开始举例
        首先先看到第0位,配置计算公式为528*18/PFD0_FRAC,PFD0_FRAC在12-35范围内。
那么我们将PLL0设置为时钟树上对应的352MHz,设PFD0_FRAC为X 那么计算公式就应该为
352 = 528*18/X,解得X=27,说明要将PFD0_FRAC设置为27,了解到这些后直接开始写入代码

        下面来一段一段的解释做了什么事情,因为我们需要将整个寄存器的值读取出来,在图上的寄存器中我需要修改的只有各个PFDx_FRAC的值,像6位7位的值并不需要去修改,而且目前我们并不知道6位7位原始值是多少,所以为了防止修改0到5位的时候防止将其他位修改,以及规范性考虑,我们将此寄存器的所有位全部读出来,存储在变量中,在变量中完成操作后,再把变量的值传入寄存器。在之前并没有使用过这样的操作,为什么在这里开始使用这么繁琐的写法呢。因为在之前的实验中并未设计到太过复杂的位运算,其次就是位运算的目的也比较通俗易懂,但是如下图可以看到进行了很多位操作,也为了增加可读性也就是每一段代码的作用所以将其以变量的形式来进行操作,具体的效果且听博主开始分析


        首先创建一个无符号的32位变量,为什么创建这个变量来存储此寄存器,首先unsigned是无符号,对于寄存器通常用于存储数值或状态信息,绝大情况都是非负的,且int对应的正好是32位的大小可以完全表示和存储寄存器的值,而不会浪费内存或丢失数据。
        然后将控制PFD的寄存器的值读取到reg中,正好32位对应32位
        下一步就开始在变量中进行我们的位操作,右边位运算结果为C0C0C0C0 = 11000000110000001100000011000000,这就是寄存器原本的值,这些空余的0就是我们要写入的各个PFD的值,然后我们按位与,相当于不改变右边位运算的值,那么对于寄存器部分位的重置就完成了
        接下来开始写入各个PFD的值,当时举例只计算了PFD0的值,因为全部放出来太占空间了,根据手册当中计算出,PFD3_FREC = 32MHz    PFD2_FREC = 24MHz   PFD1_FREC = 16MHz 
PFD0_FREC = 27MHz ,下面的代码就是将各个PFD选择的值给写入进去

        

       最后可以看到修改的经过上面几步,这个变量的值里面的位数就和下面的计算器一样,我将其中上面位移的十进制转换成为了二进制了,实际上我们的代码在实际执行的时候也会这样

        然后我们将里面将变量中存储的值,重新写入到寄存中,也就是上图代码的最后一句,然后PLL2(System PLL)配置完成了

        接下来还有一个PLL3(Usb PLL)需要配置,方法还是和上面的PLL2一样,但是因为寄存器不一样,所以计算公式也可能不一样,所以现在需要去寻找控制PLL3的PFD的寄存器

        因为是同一种类型的寄存器,所以确实是长得几乎完全一样,但是这个时候我们就要去看一下它每个位的说明了,因为不一定计算公式肯定就一样

        事实证明也确实是这样,其中下面的计算公式是480*18/PFD0_FRAC,那么在配置思路和上面一样,公式我们重新计算一下,在这里设PFD0_FRAC为X,根据时钟树推荐的时钟频率PLL3的PFD0推荐为720MHz,720 = 480 *18 / X 解得X=12MHz,那么计算根据手册中的其他PLL3得PFD公式那么我们就可以计算出,PFD3 = 19MHz   PFD2 = 17MHz   PFD1 = 16MHz  PFD0 = 12MHz

        开始编写我们的代码,这里reg重新设置为0,读取CCM_ANALOG->PFD_480得寄存器后,然后就在变量中进行与上面同样得位操作,按照这个代码就可以直接完成,对于PLL3得PFD时钟频率的配置

        刚刚在设置PLL2和PLL3分别的PFD本质上其实都是在设置时钟树上的时钟源,也就是时钟切换器那部分,但是前面也分析过时钟树,时钟源的时钟频率会通过时钟树的 CLOCK Root Generator(时钟根生成器),将时钟输出给那些外设,所以还需要完成这些配置
        

        在这里我们大部分外设都会使用到IPG_CLK_ROOT和另外一个PERCLK_CLK_ROOT时钟频率,但是通过时钟树的走向,需要初始化这两个时钟,他们的时钟频率有两个来源,一个是OSC,另外一个就是往上走的AHB_CLK_ROOT
        所以我们我们需要初始化的外设时钟有三个分别就是AHB_CLK_ROOT,PERCLK_CLK_ROOT,IPG_CLK_ROOT

        在了解到了过后就需要知道如何配置这三个时钟的参数,例如它们的时钟频率应该配置到多少,查看手册可以看到,这里面吗有一个名为系统时钟频率的表,上面就对应了我们需要配置的三个外设时钟频率的值,左边的数据是做大,右边的就是最小,直接选择最大就可以了
        那么AHB_CLK_ROOT来举例,我们将它的时钟频率吧设置到132MHz,PERCLK_CLK_ROOT最大时66MHz,IPG_CLK_ROOT同样最大也设置为66MHz

        根据上面的时钟树,从AHB_CLK_ROOT倒推可以碰到第一个选择器,走上面是中原的来源就是OSC,但是我们需要的是PLL的PFD时钟源,所以走下面那条路,然后往下方走又会碰到一个选择器,这个就是选择哪个PLL的PFD作为自己的时钟源
        这里博主就选择PLL2的PFD2,400MHz,然后其实从这个时钟源出来后有一个分频器,这个可以分也可以不用分。
        那么思路现在已经很明确了,因为要先完成AHB_CLK_ROOT的初始化,我们在之前已经初始化完成了PLL2的PFD2,那么就要开始设置它的第一个选择器,也就是CBCMR{PRE_PERIPH_CLK_SEL},去找一下它的寄存器

        对照上面那张表格,将AHB_CLK_ROOT设置到132MHz,CBCMR{PRE_PERIPH_CLK_SEL},
对应的是18-19位,我们写入11,11从分(/2)PLL2 PFD2导出时钟,01从PLL2 PFD2导出时钟,从PFD2传出来的是蓝色的线,有两条一条分(/2)的,一条是直接将PFD2的时钟频率传递进去,所以我们只需要那么知道原理后,就开始编写代码

        首先第一步要做的就是将此位寄存器清零,所以代码的第一步就是将其清零,3 = 0011将这两位向左移动18位,对应的就是18和19位,也也就是我们需要配置的位,我们的目的是使这两位清零,其他位保持不变,最后与上寄存器,得到的结果就是32位全部为0,达到了18.19位被清零(虽然本身就是为0,但是为了养成习惯,需要规范的写法),其他位也是仍然保持为0
        对于18和19位直接设置01,因为此处并不需要分频,所以直接通过就好,那么通过位运算01移动18位,也正好对应到18和19位
        这个时钟选择器就配置完成了


        继续分析时钟线进行下一步操作,时钟线继续往下走又会碰到一个时钟选择器,但是肯定是需要我们PFD2这个时钟频率来通过,所以开始操作这个时钟切换器,找到它的寄存器,

        可以看到第25是可以控制时钟选择器的时钟选择,设置1是从时钟源获取时钟,设置0是从PLL2获取时钟,那么肯定是设置0,只需要将第25位清零即可,代码如上图CCM->CBCDR &= ~(1<<25);就可以将此为清零,这个时钟选择器也完成了
        接下来时钟频率就需要顺着这个方向走。
        因为AHB_CLK_ROOT的最大时钟频率是132MHz,在上面我们将时钟频率修改成了396MHz,所以这里需要进行一次三分频,可以看到它的寄存器还是上面的CBCDR,AHB_PODF可以修改分频值,所以找到对应的
        将字节位设置为001就是我们想要的结果,所以要做的第一步就是清除这三位上面的值,然后再把需要的值给写入进去

        首先将需要的10-12位进行清零,然后再将010 =3 的值写入进去,这下就可以进行3位的分频了,在设置完这字节位后,我们看到这个字节位有一个提示提示我们要注意CDHIPR寄存器是否会产生握手忙音位

        
        首先按照它的提示去寻找一下此寄存器,对应的AHB_PODF_BUSY就是用来判断是否有忙音的问题,那么翻译一下意思就是1就是忙的意思,0就是不忙的意思,所以等到不忙的时候,也就是握手信号完成的时候才可以继续

        所以这里在下面的代码中,使用 while循环去判断一下,然后这个地方我在看教程的时候也忽略掉了,上面的PERIPH_CLK_SEL在配置的时候同样需要检查一下是否有握手忙音的问题,所以我们在下面代码中我将它补上了,同样也是这个寄存器

        

        就可以看到第五位是判断第五位和PERIPH_CLK_SEL是否会产生握手忙音的问题,所以代码中重新补上了

      截至到这里我们已经完成了AHB_CLK_ROOT,接下来就要完成另外两个外设时钟的初始化了,下方的PERCLK_CLK_ROOT和IPG_CLK_ROOT,我们先完成IPG_CLK_ROOT的初始化



        还是老套路开始寻找寄存器

        在这里就可以看到第三位就可以控制分频,我们代码也还是和就寄存器一样,因为我们现在使用的是AHB_CLK_ROOT132MHz,而IPG_CLK_ROOT最大也是132MHz所以只需要设置一份频就可


        先将对应的位进行清零,然后再通过位运算开始写值,就完成配置了

        剩下最后一个时钟也是一样PERCLK_CLK_ROOT,需要往上走,首先需要选择通过的是哪一个时钟但,然后还有一个分频器,对于PERCLK_CLK_ROOT最高频率是66MHz,这里需要一个二分频,找到对应控制分频的寄存器

        选择输出时钟的就是第6位,时钟选择,我们选择的肯定不是从时钟源产生的,所以在此位写0就可以了第0到6位


        然后再继续走还有一个分频需要设置一下,这里选择二分频,也是这个寄存器,但是在图中可以看到需要的是第0到5位,这里设置2分频就是000001

        这个就是最后的设置代码
        
        然后我们就完成了内核时钟,以及大部分常用的外设时钟的所有时钟频率配置,刚开始的时候确实比较难,主要是还要有对于时钟树的理解,但是边写边看后,就可以发现就是固定的套路,看多了过后也就是对于对应寄存器的几个操作而已
        只是可能有些位运算等这些细节计算其他的需要我们去注意一下,然后写完过后就可以使用交叉编译的方式烧写进入SD卡看看实验现象了

        这个地方博主提示一下,前面的内核时钟和这里的内核时钟对于我们外设时钟频率都会产生影响,如果发现自己灯的闪烁变慢了,在确保时钟频率,或者外设频率正确的情况下,可以通过另外一个来排除另外一个出错没有,那么这一节就讲到这里

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值