硬件驱动——I.MX6ULL裸机启动(4)(时钟设置和EPIT定时器设置)
※重点:
1 PLL:phase clocked loop 锁向环,是频率合成电路,可以产生倍频
Perscaler:预分频器,可以将一个频率按整数倍降低频,和PPL的作用是相反的
PFD:phase fractional divider 相位分数分频器,是PPL基础上的分频模块,相当于在PPL的输出再乘以一个分数再输出,既可以放大,也可以缩小
2. IMX6ULL中有7个PLL,8个PFD
3. 通过查询时钟树进行配置
(1)切换到模式 先把CPU时钟切到step_clk(外部24MHZ晶振),这样在修改PPL时不会影响系统运行
(2)配置ARM PLL 把PLL_ARM设置为倍频系数88,对应频率1056MHZ 然后通过CACRR再除以2,最终得到528MHZ的CPU主频
(3)初始化PFD 对PLL2(528MHZ)和PLL3(480MHZ)的PFD进行配置 4.配置根时钟 对AHB_CLK等3个根时钟进行配置
一.时钟设置定义和概念
这里的时钟指的是系统运行的频率,我们通常所讲的主频,其实指的就是内核运行程序时的速度问题。IMX的时钟是分离出多个不同的时钟出来,分别加以处理。产生出多路不同频率时钟以提供给不同的外设使用,这个概念称为时钟树,这是因为无论哪种Soc通常都是由一个Soc外部的晶体振荡器提供最基本的频率,之后通过Soc内部复杂的电路进行分离的,形式上很像一棵树的样子,所以被称为时钟树。
PLL:锁相环,是一种电路,简单来说就是用来倍频的特殊电路。PLL可以把一个低频升到较高的一个频率了
PDF:相位分数分频器,将PLL输出在乘以一个分数,然后在作为输出
分频器:和PLL的作用相反,它能够把一个高频降低为一个低频。可以把分频器理解为输入的频率需要除以一个数在输出。
二.时钟设置
在默认配置下IMX6ULL工作频率为396MHz,我们之前的程序就运行在这个频率下。但是IMX6ULL系列标准的工作频率为528MHz,有些型号甚至可以工作到696MHz。其他外设时钟也要设置为NXP推荐的值。
7个PLL:
(1)ARM PLL(PLL1),此路PLL是供ARM内核使用的,ARM内核时钟就是由此PLL生成的,此PLL通过编程的方式最高可倍频1.3GMHz
(2)528 PLL(PLL2),此路PLL也叫做system PLL,此路PLL是固定的22倍频,不可编程修改。因此,此路PLL时钟是24MHz * 22 = 528MHz,此PLL分出了4路PFD,分别为PLL2 PFD0~PFD3
(3)USB1 PLL(PLL3),此路PLL主要用于USBPHY,此PLL也有四路PFD,USB1 PLL是固定的20倍频,因此USB1 PLL = 24MHz * 20 = 480MHz。USB1 PLL 虽然主要用于USB1 PHY,但是其他四路PFD同样也可以作为其他外设的根时钟源。
(4)USB2 PLL(PLL7),此PLL是给USB2PHY使用的。此路PLL固定位20倍频,因此也是480MHz,此PLL其实相当于是USB1 PLL的旁路输出,一般用于检测
(5)ENET_PLL(PLL6),此路 PLL 固定为 (20+5/6)* 倍频,因此 ENET_PLL=24MHz * (20+5/6) = 500MHz。此路 PLL 用于生成网络所需的时钟,可以在此 PLL 的基础上生成 25/50/100/125MHz的网络时钟。
(6)VIDEO_PLL(PLL5),此路 PLL 用于显示相关的外设,比如 LCD,此路 PLL 的倍频可以调整,PLL 的输出范围在 650MHz~1300MHz。此路 PLL 在最终输出的时候还可以进行分频,可选 1/2/4/8/16 分频。
(7)AUDIO_PLL(PLL4),此路 PLL 用于音频相关的外设,此路 PLL 的倍频可以调整,PLL的输出范围同样也是 650MHz~1300MHz,此路 PLL 在最终输出的时候也可以进行分频,可选1/2/4 分频。
图示我们分为了 3 部分,这三部分如下:
① 、此部分是时钟源选择器,ESAI 有 4 个可选的时钟源:PLL4、PLL5、PLL3_PFD2 和pll3_sw_clk 。 具 体 选 择 哪 一 路 作 为 ESAI 的 时 钟 源 是 由 寄 存 器 CCM->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。
三.时钟设置相关代码
#include "beep.h"
#include "led.h"
#include "key.h"
#include "core_ca7.h"
#include "interrupt.h"
#include "clock.h"void delay(unsigned int n)
{while(n--); //进行延时}int main(void)
{init_clock();system_interrupt_init();init_beep();init_led();init_key();while(1){delay(0xFFFFF);}return 0;
}
#include "clock.h"
#include "MCIMX6Y2.h"void enable_clocks(void) //将所有时钟都打开
{CCM->CCGR0 = 0xFFFFFFFF;CCM->CCGR1 = 0xFFFFFFFF;CCM->CCGR2 = 0xFFFFFFFF;CCM->CCGR3 = 0xFFFFFFFF;CCM->CCGR4 = 0xFFFFFFFF;CCM->CCGR5 = 0xFFFFFFFF;CCM->CCGR6 = 0xFFFFFFFF;
}void init_clock(void)
{//初始化ARM时钟unsigned int t;CCM->CCSR &= ~(1 << 8);CCM->CCSR |= (1 << 2);t = CCM_ANALOG->PLL_ARM;t &= ~(3 << 14);t |= (1 << 13);t &= ~(0x7F << 0); //0111 1111t |= (88 << 0);CCM_ANALOG->PLL_ARM = t;CCM->CACRR |= (1 << 0);CCM->CCSR &= ~(1 << 2);//初始化PP2的PFDt = CCM_ANALOG->PFD_528;t &= ~((1 << 7) | (1 << 15) | (1 << 23) | (1 << 31));t &= ~((0x3F << 0) | (0X3F << 8) | (0x3F << 16) | (0X3F << 24));t |= (27 << 0) | (16 << 8) | (24 << 16) | (32 << 24);CCM_ANALOG->PFD_528 = t;//初始化PP3的PFDt = CCM_ANALOG->PFD_480;t &= ~((1 << 7) | (1 << 15) | (1 << 23) | (1 << 31));t &= ~((0x3F << 0) | (0X3F << 8) | (0x3F << 16) | (0X3F << 24));t |= (12 << 0) | (16 << 8) | (17 << 16) | (19 << 24);CCM_ANALOG->PFD_480 = t;//设置三个根时钟t = CCM->CBCMR;t &= ~(3 << 18);t |= (1 << 18);CCM->CBCMR = t;t = CCM->CBCDR;t &= ~(1 << 25);t &= ~(7 << 10);t |= (2 << 10);t &= ~(3 << 8);t |= (1 << 8);CCM->CBCDR = t;t = CCM->CSCMR1;t &= ~(1 << 6);t &= ~(0x3F << 0);CCM->CSCMR1 = t;enable_clocks();
}
四.EPIT定时器
定时器是最常用的外设,常常需要使用定时器来完成精准的定时功能。
EPIT:就是增强的周期中断定时器,它主要是完成周期性中断定时的。
EPIT是一个32位定时器,在处理器几乎不用介入的情况下提供精准的定时中断,软件使能以后EPIT就会开始运行
EPIT的特点:
1.时钟源可选的32位向下计数器
2.12位的分频器
3.当计数值和比较值相等的时候产生中断
(1)这是一个多路选择器,用来选择EPIT定时器的时钟源,EPIT共有三个时钟源可选择,ipg_clk、ipg_clk_32k 和 ipg_clk_highfreq,ipg_clk我们之前已经配置好了——66MHz;
(2)这是一个12位的分频器,负责对时钟源进行分频,12位对应的值是0~4095,对应着1~4096分频
(3)经过分频的时钟进入到EPIT内部,在EPIT内部有三个重要的寄存器:计数寄存器,加载寄存器和比较寄存器,这三个寄存器都是32位的。EPIT是一个向下计数器,也就是说给它一个初值,他就会从这个给定的初值开始递减,直到减为0,计数寄存器里面保存的就是当前的计数值。如果 EPIT 工作在 set-and-forget 模式下,当计数寄存器里面的值减少到 0,EPIT 就会重新从加载寄存器读取数值到计数寄存器里面,重新开始向下计数;
(4)比较器,比较寄存器里面保存的数值用于和计数寄存器里面的计数值比较,如果相等的话就会产生一个比较事件
(5)EPIT可以设置引脚输出,如果设置了的话就会通过指定的引脚输出信号
(6)产生比较中断,也就是定时中断
此外,EPIT 定时器有两种工作模式:set-and-forget 和 free-running,这两个工作模式的区
别如下:
set-and-forget 模式:此模式下,在此模式下 EPIT 的计数器从加载寄存器 EPITx_LR 中获取
初始值,不能直接向计数器寄存器写入数据。不管什么时候,只要计数器计数到 0,那么就会从加载寄存器 EPITx_LR 中重新加载数据到计数器中,周而复始。
free-running 模式:此模式下,当计数器计数到 0 以后会重新从0XFFFFFFFF 开始计数,并
不是从加载寄存器 EPITx_LR 中获取数据。
五.EPIT定时器的设置步骤
1. 设置 EPIT1 的时钟源;
2. 设置分频值 ;
3. 设置工作模式;
4. 设置计数器的初始值来源;
5. 使能比较中断;
6. 设置加载值和比较值;
7. 设置EPIT1 中断服务函数;
8. 使能 EPIT1 定时器。
六.EPIT定时器的相关代码
#include "epit.h"
#include "MCIMX6Y2.h"
#include "led.h"
#include "core_ca7.h"
#include "interrupt.h"void epit1_interrupt_handler(void)
{if(((EPIT1->SR) & (1 << 0)) != 0){led_nor();EPIT1->SR |= (1 << 0);}
}void init_epit1(void)
{EPIT1->CR = 0;EPIT1->CR |= (1 << 24);EPIT1->CR |= (1 << 17);EPIT1->CR |= (65 << 4);EPIT1->CR |= (1 << 3);EPIT1->CR |= (1 << 2);EPIT1->CR |= (1 << 1);EPIT1->LR = 1000000;EPIT1->CMPR = 0;system_interrupt_register(EPIT1_IRQn,epit1_interrupt_handler);GIC_EnableIRQ(EPIT1_IRQn);EPIT1->CR |= (1 << 0);
}
#include "beep.h"
#include "led.h"
#include "key.h"
#include "core_ca7.h"
#include "interrupt.h"
#include "clock.h"
#include "epit.h"void delay(unsigned int n)
{while(n--); //进行延时}int main(void)
{init_clock();system_interrupt_init();init_beep();init_led();init_key();init_epit1();while(1){delay(0xFFFFF);}return 0;
}