嵌入式Linux裸机开发笔记8(IMX6ULL)主频和时钟配置实验(3)
8.3 实验程序编写
本试验在上一章试验“7_key”的基础上完成,因为本试验是配置 I.MX6U 的系统时钟,因
此我们直接在文件“bsp_clk.c”上做修改,修改 bsp_clk.c 的内容如下:
示例代码 16.3.1 bsp_clk.c 文件代码
1 #include "bsp_clk.h"
2
3 /***************************************************************
4
5 文件名 : bsp_clk.c
6
7
8 描述 : 系统时钟驱动。
9 其他 : 无
10
11
12
13
14添加了函数 imx6u_clkinit(),完成 I.MX6U 的系统时钟初始化
15 ***************************************************************/
16
17 /*
18 * @description : 使能 I.MX6U 所有外设时钟
19 * @param : 无
20 * @return : 无
21 */
22 void 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 */
39 void imx6u_clkinit(void)
40 {
41 unsigned int reg = 0;
42 /* 1、设置 ARM 内核时钟为 528MHz */
43 /* 1.1、判断当使用哪个时钟源启动的,正常情况下是由 pll1_sw_clk 驱动的,而
44 * pll1_sw_clk 有两个来源:pll1_main_clk 和 step_clk,如果要
45 * 让 I.MX6ULL 跑到 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
51 if((((CCM->CCSR) >> 2) & 0x1 ) == 0) /* pll1_main_clk */
52 {
53 CCM->CCSR &= ~(1 << 8); /* 配置 step_clk 时钟源为 24MHz 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 */
91 while(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 #if 0
100 /* 要先关闭 AHB_ROOT_CLK 输出,否则时钟设置会出错 */
101 CCM->CBCDR &= ~(7 << 10);/* CBCDR 的 AHB_PODF 清零 */
102 CCM->CBCDR |= 2 << 10; /* AHB_PODF 3 分频,AHB_CLK_ROOT=132MHz */
103 while(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 来初始化时钟,如下所示:
示例代码 8.3.2 main 函数
1 int main(void)
2 {
3int i = 0;
4int keyvalue = 0;
5unsigned char led_state = OFF;
6unsigned char beep_state = OFF;
7
8imx6u_clkinit(); /* 初始化系统时钟 */
9clk_enable();
/* 使能所有的时钟
*/
10led_init(); /* 初始化 led */
11beep_init(); /* 初始化 beep */
12key_init(); /* 初始化 key
*/
13
14/* 省略掉其它代码 */
15 }
上述代码的第 8 行就是时钟初始化函数,时钟初始化函数最好放到最开始的地方调用。