嵌入式学习 day50 IMX6ULL裸机开发 - 时钟
一、RTC
实时时钟,断电后由后备电池供电
二、Clock(节拍)
1、PLL(Phase Locked Loop)
锁相环,是一种电路,简单来说就是用来倍频的特殊电路。
2、PFD:Phase Fractional Dividers
即相位分数分频器,我们可以理解为将 PLL 输出再乘以一个分数(分数可以大于 1,也可以小于 1),然后再做为输出。
3、prescale:预分频器
顾名思义,和PLL的作用是相反的,它能够把一个高频降低成一个低频。可以把分频器理解为输入的频率需要除以一个数再输出。
4、外设时钟框架
(1) ARM_PLL(PLL1),此路 PLL 是供 ARM 内核使用的,ARM 内核时钟就是由此 PLL生成的,此 PLL 通过编程的方式最高可倍频到 1.3GHz。
(2)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 接口等等。
(3)USB1_PLL(PLL3),此路 PLL 主要用于 USBPHY,此 PLL 也有四路 PFD,为:
PLL3_PFD0~PLL3_PFD3,USB1_PLL 是固定的 20 倍频,因此 USB1_PLL=24MHz *20=480MHz。USB1_PLL 虽然主要用于USB1PHY,但是其和四路PFD 同样也可以作为其他外设的根时钟源。
(4)USB2_PLL(PLL7,注意是PLL7,虽然序号标为 4,但是实际是 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 分频。
5、设备树框架
这个图从左往右分为三个部分: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 中选择合适的时钟源给外设使用。
6、选择对应路线及配置
(1)ARM_CLK_ROOT
①PLL1相关配置
1)
2)
3)
设置二分频
4)
CCM->CCSR &= ~(1 << 8);
CCM->CCSR |= (1 << 2);
unsigned int t;
t = CCM_ANALOG->PLL_ARM;
t &= ~(3 << 14);
t |= (1 << 13);
t &= ~(0x7f << 0);
t |= (88 << 0);
CCM_ANALOG ->PLL_ARM = t;
CCM->CACRR &= ~(7 << 0);
CCM->CACRR |= (1 << 0);
CCM->CCSR &= ~(1 << 2);
(2)PLL2:CCM_ANALOG_PFD_528
t = CCM_ANALOG-> PFD_528;
t &= ~(0x3f << 0) | (0x3f << 8) | (0x3f << 16) | (0x3f << 24);
t |= (27 << 0) | (16 << 8) | (24 << 16) | (32 << 24);
CCM_ANALOG->PFD_528 = t;
(3)PLL3:CCM_ANALOG_PFD_480
t = CCM_ANALOG->PFD_480;
t &= ~(0x3f << 0) | (0x3f << 8) | (0x3f << 16) | (0x3f << 24);
t |= (12 << 0) | (16 << 8) | (17 << 16) | (19 << 24);
CCM_ANALOG->PFD_480 = t;
(4)AHB_CLK_ROOT
①
②
③
(5)IRG_CLK_ROOT
①
(5)PERCLK_CLK_ROOT
①
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 = CCM->CSCMR1;
t &= ~(1 << 6);
t &= ~(0x3f << 0);
CCM->CSCMR1 = t;
三、EPIT(是增强的周期中断定时器)
1、功能
它主要是完成周期性中断定时的
2、特点
(1)时钟源可选的 32 位向下计数器;
(2)12 位的分频值;
(3)当计数值和比较值相等的时候产生中断,
3、结构
(1)这是个多路选择器,用来选择 EPIT 定时器的时钟源,EPIT 共有 3 个时钟源可选择,ipg_clk、ipg_clk_32k 和 ipg_clk_highfreq,ipg_clk我们之前已经配置好了——66MHz;
(2) 这是一个 12 位的分频器,负责对时钟源进行分频,12 位对应的值是 0~4095,对应着1~4096 分频;
(3)经过分频的时钟进入到 EPIT 内部,在 EPIT 内部有三个重要的寄存器:计数寄存器(EPIT_CNR)、加载寄存器(EPIT_LR)和比较寄存器(EPIT_CMPR),这三个寄存器都是 32 位的。EPIT 是一个向下计数器,也就是说给它一个初值,它就会从这个给定的初值开始递减,直到减为 0,计数寄存器里面保存的就是当前的计数值。如果 EPIT 工作在 set-and-forget 模式下,当计数寄存器里面的值减少到 0,EPIT 就会重新从加载寄存器读取数值到计数寄存器里面,重新开始向下计数;
(4) 比较器,比较寄存器里面保存的数值用于和计数寄存器里面的计数值比较,如果相等的话就会产生一个比较事件;
(5)EPIT 可以设置引脚输出,如果设置了的话就会通过指定的引脚输出信号;
(6) 产生比较中断,也就是定时中断
注:此外,EPIT 定时器有两种工作模式:set-and-forget 和 free-running,这两个工作模式的区别如下:
(1)set-and-forget 模式:此模式下,在此模式下 EPIT 的计数器从加载寄存器 EPITx_LR 中获取初始值,不能直接向计数器寄存器写入数据。不管什么时候,只要计数器计数到 0,那么就会从加载寄存器 EPITx_LR 中重新加载数据到计数器中,周而复始。
(2)free-running 模式:此模式下,当计数器计数到 0 以后会重新从0XFFFFFFFF 开始计数,并不是从加载寄存器 EPITx_LR 中获取数据。
4、相关寄存器
(1)CR
CLKSRC(bit25:24):EPIT 时钟源选择位,为 0 的时候关闭时钟源,1 的时候选择选择Peripheral 时钟(ipg_clk),为 2 的时候选择 High-frequency 参考时钟(ipg_clk_highfreq),为 3 的时候选择 Low-frequency 参考时钟(ipg_clk_32k)。在本例程中,我们设置为 1,也就是选择 ipg_clk作为 EPIT 的时钟源,ipg_clk=66MHz;
PRESCALAR(bit15:4):EPIT 时钟源分频值,可设置范围 0~4095,分别对应 1~4096 分频;
RLD(bit3):EPIT 工作模式,为 0 的时候工作在 free-running 模式,为 1 的时候工作在 set-and-forget 模式。我们设置为 1,也就是工作在 set-and-forget 模式;
• OCIEN(bit2):比较中断使能位,为 0 的时候关闭比较中断,为 1 的时候使能比较中断,我们需要使能比较中断;
• ENMOD(bit1):设置计数器初始值,为 0 时计数器初始值等于上次关闭 EPIT 定时器以后计数器里面的值,为 1 的时候来源于加载寄存器;
• EN(bit0):EPIT 使能位,为 0 的时候关闭 EPIT,为 1 的时候使能 EPIT。
5、步骤
(1). 设置 EPIT1 的时钟源;
(2). 设置分频值 ;
(3). 设置工作模式;
(4). 设置计数器的初始值来源;
(5). 使能比较中断;
(6). 设置加载值和比较值;
(7). 设置EPIT1 中断服务函数;
(8). 使能 EPIT1 定时器。
void init_epit1(void)
{
unsigned int t;
t = EPIT1->CR;
t &= ~(3 << 24);
t |= (1 << 24);
t |= (1 << 17);
t &= ~(0xfff << 4);
t |= (65 << 4);
t |= (1 << 3) | (1 << 2) | (1 << 1);
EPIT1->CR = t;
EPIT1->LR = 1000000;
EPIT1->CMPR = 0;
EPIT1->CNR = 1000000;
EPIT1->CR |= (1 << 0);
system_interrupt_register(epit_interrupt_hander,EPIT1_IRQn);//注册中断服务函数
GIC_EnableIRQ(EPIT1_IRQn);
GIC_SetPriority(EPIT1_IRQn,0);
}
中断服务函数
void epit_interrupt_hander(void)
{
if((EPIT1->SR & (1 << 0)) != 0)
{
led_nor();
EPIT1->SR |= (1 << 0);
}
}
四、GPT(通用的目的寄存器)
GPT_CAPTURE 1和GPT_CAPTURE 2表示GPT的两个输入捕获通道,作用是考察这两个引脚电平变化之间的时间差的。我们不使用捕获功能。GPT_COMPARE1、2、3是GPT的三个比较输出通道,当计数器里面的值和输出比较寄存器里面的比较值相等就会触发输出比较中断,也可以修改三个引脚的电平。这里我们也不使用输出。我们的最终目的是实现一个较为精准的us级延时和ms级延时函数。
1、工作方式
(1)重新启动(restart)模式:在此模式下,当计数值和比较寄存器中的值相等的话计数值就会清零,然后重新从0X00000000 开始向上计数,只有比较通道 1 才有此模式!向比较通道 1 的比较寄存器写入任何数据都会复位 GPT 计数器。对于其他两路比较通道(通道 2 和 3),当发生比较事件以后不会复位计数器。
(2)自由运行(free-run)模式:当在此模式下,此模式适用于所有三个比较通道,当比较事件发生以后并不会复位计数器,而是继续计数,直到计数值为 0XFFFFFFFF,然后重新回滚到 0X00000000。
2、设计延时
按照我们的设计,如果设置 GPT 定时器的时钟源为 ipg_clk=66MHz,设置 66 分频,那么进入 GPT定时器的最终时钟频率就是 66/66=1MHz,周期为 1us。GPT 的计数器每计一个数就表示“过去”了 1us。如果计 10 个数就表示“过去”了 10us。通过读取寄存器 GPTx_CNT 中的值就知道计了个数,比如现在要延时 100us,那么进入延时函数以后纪录下寄存器 GPTx_CNT 中的值为 200,当 GPTx_CNT 中的值为 300 的时候就表示 100us 过去了,也就是延时结束。GPTx_CNT 是个32 位寄存器,如果时钟为 1MHz 的话,GPTx_CNT 最多可以实0XFFFFFFFFus=4294967295us≈4294s≈72min。也就是说 72 分钟以后 GPTx_CNT 寄存器就会回滚到 0X00000000,也就是溢出,所以需要在延时函数中要处理溢出的情况。
3、寄存器设置
(1)CR
· SWR(bit15):复位 GPT 定时器,向此位写 1 就可以复位 GPT 定时器,当 GPT 复位完成以后此为会自动清零。
• FRR(bit9):运行模式选择,当此位为 0 的时候比较通道 1 工作在重新启动(restart)模式。当此位为 1 的时候所有的三个比较通道均工作在自由运行模式(free-run)。
• CLKSRC(bit8:6):GPT 定时器时钟源选择位,为 0 的时候关闭时钟源;为 1 的时候选择ipg_clk 作为时钟源;为 2 的时候选择 ipg_clk_highfreq 为时钟源;为 3 的时候选择外部时钟为时钟源;为 4 的时候选择 ipg_clk_32k 为时钟源;为 5 的时候选择 ip_clk_24M 为时钟源。我们选择 ipg_clk 作为 GPT 定时器的时钟源,因此此位设置位 1(0b001)。
• ENMOD(bit1):GPT 使能模式,此位为 0 的时候如果关闭 GPT 定时器,计数器寄存器保存定时器关闭时候的计数值。此位为 1 的时候如果关闭 GPT 定时器,计数器寄存器就会清零。
• EN(bit):GPT 使能位,为 1 的时候使能 GPT 定时器,为 0 的时候关闭 GPT 定时器。
GPT Prescaler Register (GPTx_PR)用于保存分频值,PRESCALER(bit11:0),这就是 12 位分频
值,可设置 0~4095,分别对应 1~4096 分频
void init_gpt(void)
{
reset_gpt(GPT1);
unsigned int t;
t = GPT1->CR;
t &= ~(7 << 26);
t &= ~(3 << 18);
t |= (1 << 9);
t &= ~(7 << 6);
t |= (1 << 6);
t &= ~(1 << 1);
GPT1->CR = t;
t = GPT1->PR;
t &= ~(0xfff << 0);
t |= (65 << 0);
GPT1->PR = t;
GPT1->CR |= (1 << 0);
}
6、函数
void delay_us(unsigned int n)
{
unsigned int counter = 0;
unsigned int new_counter,old_counter;
old_counter = GPT1->CNT;
while(1)
{
new_counter = GPT1->CNT;
if(new_counter != old_counter)
{
if(new_counter < old_counter)
{
counter += 0xffffffff - old_counter + new_counter;
}
else
{
counter += new_counter - old_counter;
}
if(counter >= n)
{
return ;
}
old_counter = new_counter;
}
}
}
void delay_ms(unsigned int n)
{
while(n--)
{
delay_us(1000);
}
}