【ARM自学笔记】ARM7时钟简述及配置

本文详细介绍了NXP I.MX6U芯片的时钟系统,包括PLL、PFD的工作原理和配置方法。内容涵盖内核时钟、外设时钟源的选择、分频设置,以及如何通过寄存器配置实现528MHz的内核时钟。同时,文章提供了设置AHB、IPG和PERCLK时钟的步骤,并给出了相应的代码示例。
摘要由CSDN通过智能技术生成

简述

所使用的芯片为NXP的I.MX6U。

硬件分析

原理图如下:
在这里插入图片描述

分为两个来源:

  • 32.768KHz晶振(I.MX6U的RTC时钟源)
  • 24MHz晶振(I.MX6U内核和外设的时钟源)

流程分析

PLL时时钟源

PLL(锁相环): 锁相回路(PLL:Phase-lockedloops)是一种利用反馈控制原理实现的频率及相位的同步技术,其作用是将电路输出的时钟与其外部的参考时钟保持同步。当参考时钟的频率或相位发生改变时,锁相回路会检测到这种变化,并且通过其内部的反馈系统来调节输出频率,直到两者重新同步,这种同步又称为“锁相”。
PFD: 鉴频鉴相器(phase frequencydetector)简称PFD,可以比较两种输入信号的相位误差及频率误差。鉴频鉴相器是异步电路,最早是由四颗触发器构成。逻辑电路可以判断二个信号中,哪一个比较早出现零交越,以及哪一个信号较常出现零交。用在锁相环的应用当中,就算是频率不同,也可以达到锁相的作用。总之,是PLL的一个关键部件,PLL包括PFD。

NXP将外设时钟源分为了7组,如下图是时钟生成图:
在这里插入图片描述

  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)**给USB2PHY使用的。同样的,此路PLL固定为20倍频,因此也是480MHz。
  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 分频。

时钟树

在这里插入图片描述

分为三个部分:

CLOCK_SWITCHERCLOCK ROOT GENERATORSYSTEM CLOCKS
7路PLL和8路PFD(见上)负责从7路PLL和8路PFD中选择最合适的时钟源给外设用(相当于将左右进行“搭桥”)芯片外设

这么复杂的图如何去看?
图中大致分为两种部件,一种是橘色的梯形,一种是写着"/x"的方型图标。

  1. 橘色的梯形是时钟源选择器,左边的连线是可以选择的时钟源。
  2. "/x"的方型图标,是分频器。x并不表示分频的具体频数

内核时钟设置

配置主频

如上图中时钟数中可以得:
在这里插入图片描述

  1. 内核时钟树源来自于PLL1,假如此时PLL1为996MHz。
  2. 通过寄存器 CCM_CACRR 的 ARM_PODF 位对 PLL1 进行分频,可选择 1/2/4/8 分频,此处2分频,频率也就是498MHz。
  3. 此处并没有进行2分频。
  4. 此时为主频为498MHz。

如果我们要设置内核主频为528MHz,那么PLL1的时钟就要设置为1056MHz。

  1. 查看手册可以通过寄存器 CCM_ANALOG_PLL_ARMn来配置PLL1的频率。如图寄存器数据手册:
    在这里插入图片描述
  • 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时钟频率的时候要先将内核时钟源改为其它的时钟源,可选时钟源如下)
在这里插入图片描述

  1. pll1_sw_clk 也就是 PLL1 的最终输出频率。
  2. 此处是一个选择器。选择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 调整完成以后再切换回来。
  3. 此处也是一个选择器,选择 step_clk 的时钟源,由寄存器 CCM_CCSR 的 STEP_SEL 位来决定 step_clk 是选择 osc_clk 还是 secondary_clk。一般选择 osc_clk,也就是 24MHz 的晶振。

寄存器CCM_CCSR结构图如下:
在这里插入图片描述

寄存器CCM_CCSR只用到了STEP_SEL、 PLL1_SW_CLK_SEL 这两个位。
STEP_SEL用来选择step_clk时钟源
PLL1_SW_CLK_SEL用来选择p1ll_sw_clk时钟源

  1. 通过CCM_CACRR 寄存器进行2分频。 结构图如下:
    在这里插入图片描述

只用ARM_PODF位,可设置07,分别对应18分频。

以上是主要的两个过程,全部过程如下:

  1. 设置寄存器 CCSR 的 STEP_SEL 位,设置 step_clk 的时钟源为 24M 的晶振。
  2. 设置寄存器 CCSR 的 PLL1_SW_CLK_SEL 位,设置 pll1_sw_clk 的时钟源为step_clk=24MHz,通过这一步我们就将 I.MX6U 的主频先设置为 24MHz,直接来自于外部的24M 晶振。
  3. 设置寄存器 CCM_ANALOG_PLL_ARMn,将 pll1_main_clk(PLL1)设置为 1056MHz。
  4. 设置寄存器 CCSR 的 PLL1_SW_CLK_SEL位,重新将pll1_sw_clk的时钟源切换回pll1_main_clk,切换回来以后的pll1_sw_clk 就等于 1056MHz。
  5. 最后设置寄存器 CCM_CACRR 的 ARM_PODF 为 2 分频, I.MX6U 的内核主频就为1056/2=528MHz。

PFD时钟设置

PLL2、PLL3和PLL7都是固定值,而PLL4~PLL6是针对特殊外设的。
重点是PLL2和PLL3各自4路的PFD。
NXP推荐频率为:
在这里插入图片描述

PLL2用到寄存器是:CCM_ANALOG_PFD_528n。寄存器如下:
在这里插入图片描述

该寄存器分为4组,分为PFD0~3,每组8bit。

PDF0对应的寄存器位如下:

  • 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 的时候使能输出。

若不能取到推荐值,尽可能接近即可。

PLL3的结构也是这样的,只是频率计算公式不一样。PLL3的计算公式为:PLL3_PFDX=480*18/PFDX_FRAC(X=0~3)

AHB、 IPG 和 PERCLK 根时钟设置

要配置AHB_CLK_ROOT 和 IPG_CLK_ROOT的时钟。可设置范围如下:
在这里插入图片描述

根据上表,我们将AHB_CLK_ROOT、IPG_CLK_ROOT 和 PERCLK_CLK_ROOT 频率都调到最高。

AHB_CLK_ROOT 和 IPG_CLK_ROOT 的涉及如下图:
在这里插入图片描述

  1. 此选择器用来选择 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。
  2. 此选择器用来选择 periph_clk 的时钟源,由寄存器 CCM_CBCDR的PERIPH_CLK_SEL位与PLL_bypass_en2组成的或来选择。当 CCM_CBCDR 的 PERIPH_CLK_SEL 位为 0 的时候periph_clk=pr_periph_clk=396MHz。
  3. 通过 CBCDR 的 AHB_PODF 位来设置 AHB_CLK_ROOT 的分频值,可以设置 1~8 分频,如果想要 AHB_CLK_ROOT=132MHz 的话就应该设置为 3 分频: 396/3=132MHz。
  4. 通过 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 时钟频率。 结构图如下:
在这里插入图片描述

该时钟的来源有两个:

  • OSC(24MHz)
  • IPG_CLK_ROOT
    由寄存器CCM_CSCMR1 的 PERCLK_CLK_SEL 位来决定。如果为0的话时钟源就是IPG_CLK_ROOT,为1的话时钟源就是OSC。
    可以通过寄存器CCM_CSCMR1 的 PERCLK_PODF 位来设置分频。

上述CCM_CBCDR、 CCM_CBCMR 、 CCM_CSCMR1寄存器结构如下:
在这里插入图片描述

CCM_CBCDR

各位说明:

  • PERIPH_CLK2_PODF: periph2 时钟分频,可设置 0~7,分别对应 1~8 分频。
  • **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 时钟分频,可设置 0~7,分别对应 1~8 分频。
  • AHB_PODF: ahb 时钟分频,可设置07,分别对应18分频。修改此位会引起一次与MMDC的握手,所以修改完成以后要等待握手完成,握手完成信号由寄存器 CCM_CDHIPR 中指定位表示。
  • IPG_PODF: ipg 时钟分频,可设置 0~3,分别对应 1~4 分频。
  • AXI_ALT_CLK_SEL: axi_alt 时钟选择,为 0 的话选择 PLL2_PFD2,如果为 1 的话选择PLL3_PFD1。
  • AXI_CLK_SEL: axi 时钟源选择,为 0 的话选择 periph_clk,为 1 的话选择 axi_alt 时钟。
  • FABRIC_MMDC_PODF: fabric/mmdc 时钟分频设置,可设置 0~7,分别对应 1~8 分频。
  • PERIPH2_CLK2_PODF: periph2_clk2 的时钟分频,可设置 0~7,分别对应 1~8 分频。

CCM_CBCMR
在这里插入图片描述

各位说明:

  • LCDIF1_PODF: lcdif1 的时钟分频,可设置 0~7,分别对应 1~8 分频。
  • 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
在这里插入图片描述

  • PERCLK_CK_SEL: perclk 时钟源选择,为 0 的话选择 ipg clk,为 1 的话选择 osc clk
  • PERCLK_PODF: perclk 的时钟分频,可设置 0~7,分别对应 1~8 分频
  • 在修改如下时钟选择器或者分频器的时候会引起与 MMDC 的握手发生:
    1. mmdc_podf
    2. periph_clk_sel
    3. periph2_clk_sel
    4. arm_podf
    5. ahb_podf

编写代码

#include "bsp_clk.h"

/*
 * 使能I.MX6U所有外设时钟
 */
void clk_enable(void)
{
	CCM->CCGR0 = 0XFFFFFFFF;
	CCM->CCGR1 = 0XFFFFFFFF;
	CCM->CCGR2 = 0XFFFFFFFF;
	CCM->CCGR3 = 0XFFFFFFFF;
	CCM->CCGR4 = 0XFFFFFFFF;
	CCM->CCGR5 = 0XFFFFFFFF;
	CCM->CCGR6 = 0XFFFFFFFF;
}

/*
 * 初始化系统时钟,设置系统时钟为528Mhz,并且设置PLL2和PLL3各个PFD时钟,所有的时钟频率均按照I.MX6U官方手册推荐的值.
 */
void imx6u_clkinit(void)
{
	unsigned int reg = 0;
	/* 1、设置ARM内核时钟为528MHz */
	if((((CCM->CCSR) >> 2) & 0x1 ) == 0) 	// 当前pll1_sw_clk使用的pll1_main_clk
	{	
		CCM->CCSR &= ~(1 << 8);				// 配置step_clk时钟源为24MH OSC 	
		CCM->CCSR |= (1 << 2);				// 配置pll1_sw_clk时钟源为step_clk 
	}

	CCM_ANALOG->PLL_ARM = (1 << 13) | ((88 << 0) & 0X7F); 	// 配置pll1_main_clk=1056MHz 
	CCM->CCSR &= ~(1 << 2);									// 将pll_sw_clk时钟重新切换回pll1_main_clk 
	CCM->CACRR = 1;											// ARM内核时钟为pll1_sw_clk/2=1056/2=528Mhz 

	/* 2、设置PLL2(SYS PLL)各个PFD */
	reg = CCM_ANALOG->PFD_528;
	reg &= ~(0X3F3F3F3F);		// 清除原来的设置 
	reg |= 32<<24;				// PLL2_PFD3=528*18/32=297Mhz 	
	reg |= 24<<16;				// PLL2_PFD2=528*18/24=396Mhz(DDR使用的时钟,最大400Mhz) 
	reg |= 16<<8;				// PLL2_PFD1=528*18/16=594Mhz 	
	reg |= 27<<0;				// PLL2_PFD0=528*18/27=352Mhz  	
	CCM_ANALOG->PFD_528=reg;	// 设置PLL2_PFD0~3 		 		

	/* 3、设置PLL3(USB1)各个PFD */
	reg = 0;					// 清零  
	reg = CCM_ANALOG->PFD_480;
	reg &= ~(0X3F3F3F3F);		// 清除原来的设置 						
	reg |= 19<<24;				// PLL3_PFD3=480*18/19=454.74Mhz 
	reg |= 17<<16;				// PLL3_PFD2=480*18/17=508.24Mhz 
	reg |= 16<<8;				// PLL3_PFD1=480*18/16=540Mhz	
	reg |= 12<<0;				// PLL3_PFD0=480*18/12=720Mhz	 
	CCM_ANALOG->PFD_480=reg;	// 设置PLL3_PFD0~3 					

	/* 4、设置AHB时钟 最小6Mhz, 最大132Mhz (boot rom自动设置好了可以不用设置)*/
	CCM->CBCMR &= ~(3 << 18); 	// 清除设置
	CCM->CBCMR |= (1 << 18);	// pre_periph_clk=PLL2_PFD2=396MHz 
	CCM->CBCDR &= ~(1 << 25);	// periph_clk=pre_periph_clk=396MHz 
	while(CCM->CDHIPR & (1 << 5));// 等待握手完成 

#if 0
	/* 要先关闭AHB_ROOT_CLK输出,否则时钟设置会出错 */
	CCM->CBCDR &= ~(7 << 10);	// CBCDR的AHB_PODF清零 
	CCM->CBCDR |= 2 << 10;		// AHB_PODF 3分频,AHB_CLK_ROOT=132MHz 
	while(CCM->CDHIPR & (1 << 1));/
* 等待握手完成 */
#endif
	
	/* 5、设置IPG_CLK_ROOT最小3Mhz,最大66Mhz (boot rom自动设置好了可以不用设置)*/
	CCM->CBCDR &= ~(3 << 8);	// CBCDR的IPG_PODF清零 
	CCM->CBCDR |= 1 << 8;		// IPG_PODF 2分频,IPG_CLK_ROOT=66MHz
	
	/* 6、设置PERCLK_CLK_ROOT时钟 */
	CCM->CSCMR1 &= ~(1 << 6);	// PERCLK_CLK_ROOT时钟源为IPG 
	CCM->CSCMR1 &= ~(7 << 0);	// PERCLK_PODF位清零,即1分频 
}


主函数:

#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"


int main(void)
{
	int i = 0;
	int keyvalue = 0;
	unsigned char led_state = OFF;
	unsigned char beep_state = OFF;

	imx6u_clkinit();	// 初始化系统时钟 	
	clk_enable();		// 使能所有的时钟 	
	led_init();			// 初始化led 		


	while(1)			
	{	
		keyvalue = key_getvalue();
		if(keyvalue)
		{
			switch ((keyvalue))
			{
				case KEY0_VALUE:
					beep_state = !beep_state;
					beep_switch(beep_state);
					break;
			}
		}
		i++;
		if(i==50)
		{
			i = 0;
			led_state = !led_state;
			led_switch(LED0, led_state);
		}

		delay(10);
	}

	return 0;
}

编译烧录

在这里插入图片描述

结果

led闪烁更快。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ORI2333

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值