(8)初始化系统时钟
一、系统时钟介绍
S5PV210 中包含 3 大类时钟:
1、MSYS:主系统时钟,用来给 cortex a8 处理器,dram 控制器,3D,IRAM,IROM,中断控制器等提供时钟;
2、DSYS:显示相关的时钟,用来给显示相关的部件提供时钟,包括 FIMC,FIMD,JPEG,and multimedile IPs;
3、PSYS:外围设备的时钟,用来给外围设备提供时钟,如 i2s, spi,i2c,uart 等。
Tiny 210外接的晶振频率(简称 Fin)为 24MHz,通过时钟控制逻辑 PLL 可以提高系统时钟。S5PV210 共有 4 个倍频器,即 PLL,包括 APLL(供 MSYS 使用),MPLL(供 DSYS 使用),EPLL(供 PSYS使用),VPLL(供 video 相关的时钟使用)。3 大类时钟 domain 中,可以使用不同的分频,使其给不同部件输出所需要的时钟,各类时钟的关系以及参考值设置如下图:
下面贴出数据手册的S5PV210 时钟设置参考图:
二、寄存器
1、时钟设置的关键性寄存器
1.1、xPLL_LOCK:xPLL_LOCK寄存器主要控制PLL锁定周期的。
1.2、xPLL_CON/xPLL_CON0/xPLL_CON1: PLL_CON寄存器主要用来打开/关闭PLL电路,设置PLL的倍频参数,查看PLL锁定状态等。
1.3、CLK_SRCn(n:0 ~ 6):CLK_SRC寄存器是用来设置时钟来源的,对应时钟框图的MUX开关。
1.4、CLK_SRC_MASKn:CLK_SRC_MASK决定MUX开关N选1后是否能继续通过。默认的时钟都是打开的,好处是不会因为某个模块的时钟关闭而导致的莫名其妙的问题,坏处是功耗控制不精细、功耗高。
1.5、CLK_DIVn:各模块的分频器参数配置。
1.6、CLK_GATE_x: 类似于CLK_SRC_MASK,对时钟进行开关控制。
1.7、CLK_DIV_STATn、CLK_MUX_STATn:这两类状态位寄存器,用来查看DIV和MUX的状态是否已经完成还是在进行中。
总结:其中最重要的寄存器有三类:CON、SRC、DIV。其中CON决定PLL倍频到多少,SRC决定走那一条路,DIV决定分频多少。
2、汇编实现时钟设置代码详解
2.1、时钟设置的步骤分析
第一步:先选择不使用PLL。让外部24MHz原始时钟直接过去。
第二步:设定锁定时间。默认值为0x0FFF,保险起见我们设置0xFFFF。
第三步:设置分频系统,决定由PLL出来的最高时钟如何分频得到各个分时钟。
第四步:设置PLL,主要是设置PLL的倍频系统,决定由输入端24MHz的原始频率可以得到多大的输出频率。我们按照默认设置输出为ARMCLK为1GHz。
第五步:打开PLL。前面四步已经设置好了所有的开关和分频系数,本步骤打开PLL后PLL开始工作,锁定频率后输出,然后经过分频得到各个频率。
总结:以上5步,其实真正涉及到的寄存器只有5个而已。
3、CLK_SRC寄存器(CLK_SRC0, R/W, Address = 0xE010_0200)的设置(寄存器是用来设置时钟来源的,对应时钟框图的MUX开关.对应数据手册378)
CLK_SRC寄存器其实是用来设置MUX开关的。在这里先将该寄存器设置为全0,主要是bit0和bit4设置为0,表示APLL和MPLL暂时都不启用。
4、CLK_LOCK寄存器(APLL_LOCK, R/W, Address = 0xE010_0000)的设置(寄存器主要控制PLL锁定周期的.对应数据手册371)
设置PLL锁定延时的。官方推荐值为0xFFF,为了万无一失我们设置为0xFFFF。
5、CLK_DIV0寄存器设置(该寄存器是控制分频的.对应数据手册387)
6、PLL_CON寄存器( PLL_CON寄存器主要用来打开/关闭PLL电路,设置PLL的倍频参数,查看PLL锁定状态等.对应数据手册372)
7、设置各种时钟开关,使用PLL
三、代码
1、start.S
/*
* 代码:初始化时钟系统
* 日期:2020.7.12
* 作者:glass love
*
*/
//要开icache就将 CONFIG_SYS_ICACHE_OFF写为1,关则写为0
#define CONFIG_SYS_ICACHE 1
.globl _start
_start:
/**********************关看门狗*******************************/
//通过查阅数据手册知道控制看门狗开关的寄存器是:
//Watchdog Timer Control Register (WTCON, R/W, Address =0xE2700000 )
//WTCON寄存器的bit[0]位是启用或禁用复位信号的看门狗定时器输出位
//1为启用,0为禁止
//因此只需要往WTCON中写入0x0即可
ldr r0, =0x00000000
ldr r1, =0xE2700000
str r0, [r1]
/**********************开icache*********************************/
//打开icache可以提高运行速度
//读出协处理器cp15的c1的值到r0中
mrc p15, 0, r0, c1, c0, 0
#if CONFIG_SYS_ICACHE
//将cp15协处理器的bit[12]置一(开icache)
orr r0, r0, #0x00001000
#else
//将cp15协处理器的bit[12]清零(关icache)
bic r0, r0, #0x00001000
#endif
//将r0中的值写入到cp15协处理器的c1中
mcr p15, 0, r0, c1, c0, 0
/***********************设置栈****************************/
//IROM 里的固定代码设置的 sp 就等于 0xD003_7D80,
//所以我们设置栈一般就指向0xD003_7D80,以调用c函数
ldr sp, =0xD0037D80
/**********************初始化时钟***********************/
bl clock_init
/*********************LED闪烁**************************/
bl led_blink
//汇编死循环
b .
2、clock.S
// 时钟控制器基地址
#define ELFIN_CLOCK_POWER_BASE 0xE0100000
// 时钟相关的寄存器相对时钟控制器基地址的偏移值
#define APLL_LOCK_OFFSET 0x00
#define MPLL_LOCK_OFFSET 0x08
#define APLL_CON0_OFFSET 0x100
#define APLL_CON1_OFFSET 0x104
#define MPLL_CON_OFFSET 0x108
#define CLK_SRC0_OFFSET 0x200
#define CLK_SRC1_OFFSET 0x204
#define CLK_SRC2_OFFSET 0x208
#define CLK_SRC3_OFFSET 0x20c
#define CLK_SRC4_OFFSET 0x210
#define CLK_SRC5_OFFSET 0x214
#define CLK_SRC6_OFFSET 0x218
#define CLK_SRC_MASK0_OFFSET 0x280
#define CLK_SRC_MASK1_OFFSET 0x284
#define CLK_DIV0_OFFSET 0x300
#define CLK_DIV1_OFFSET 0x304
#define CLK_DIV2_OFFSET 0x308
#define CLK_DIV3_OFFSET 0x30c
#define CLK_DIV4_OFFSET 0x310
#define CLK_DIV5_OFFSET 0x314
#define CLK_DIV6_OFFSET 0x318
#define CLK_DIV7_OFFSET 0x31c
#define CLK_DIV0_MASK 0x7fffffff
#define APLL_MDIV 0x7d
#define APLL_PDIV 0x3
#define APLL_SDIV 0x1
#define MPLL_MDIV 0x29b
#define MPLL_PDIV 0xc
#define MPLL_SDIV 0x1
#define set_pll(mdiv, pdiv, sdiv) (1<<31 | mdiv<<16 | pdiv<<8 | sdiv)
#define APLL_VAL set_pll(APLL_MDIV,APLL_PDIV,APLL_SDIV)
#define MPLL_VAL set_pll(MPLL_MDIV,MPLL_PDIV,MPLL_SDIV)
.global clock_init
clock_init:
ldr r0, =ELFIN_CLOCK_POWER_BASE
// 1 设置各种时钟开关,暂时不使用PLL
ldr r1, =0x0
// 芯片手册P378 寄存器CLK_SRC:Select clock source 0 (Main)
str r1, [r0, #CLK_SRC0_OFFSET]
// 2 设置锁定时间,使用默认值即可
// 设置PLL后,时钟从Fin提升到目标频率时,需要一定的时间,即锁定时间
ldr r1, =0x0000FFFF
str r1, [r0, #APLL_LOCK_OFFSET]
str r1, [r0, #MPLL_LOCK_OFFSET]
// 3 设置分频
// 清bit[0~31]
ldr r1, [r0, #CLK_DIV0_OFFSET]
ldr r2, =CLK_DIV0_MASK
bic r1, r1, r2
ldr r2, =0x14131440
orr r1, r1, r2
str r1, [r0, #CLK_DIV0_OFFSET]
// 4 设置PLL
// FOUT = MDIV*FIN/(PDIV*2^(SDIV-1))=0x7d*24/(0x3*2^(1-1))=1000 MHz
ldr r1, =APLL_VAL
str r1, [r0, #APLL_CON0_OFFSET]
// FOUT = MDIV*FIN/(PDIV*2^SDIV)=0x29b*24/(0xc*2^1)= 667 MHz
ldr r1, =MPLL_VAL
str r1, [r0, #MPLL_CON_OFFSET]
// 5 设置各种时钟开关,使用PLL
ldr r1, [r0, #CLK_SRC0_OFFSET]
ldr r2, =0x10001111
orr r1, r1, r2
str r1, [r0, #CLK_SRC0_OFFSET]
mov pc, lr
//0x14131440对应二进制数0001 0100 0001 0011 0001 0100 0100 0000
//ARMCLK = MOUT_MSYS / (APLL_RATIO + 1) APLL_RATIO对应bit[2:0]
//ARMCLK = MOUT_MSYS / (0 + 1)=MOUT_MSYS /1
//SCLKA2M = SCLKAPLL / (A2M_RATIO + 1) A2M_RATIO对应bit[6:4]
//SCLKA2M = SCLKAPLL / (4 + 1)=SCLKAPLL /5
//HCLK_MSYS = ARMCLK / (HCLK_MSYS_RATIO + 1) HCLK_MSYS_RATIO对应bit[10:8]
//HCLK_MSYS = ARMCLK / (4 + 1)=ARMCLK /5
//PCLK_MSYS = HCLK_MSYS / (PCLK_MSYS_RATIO + 1) PCLK_MSYS_RATIO对应bit[14:12]
//PCLK_MSYS = HCLK_MSYS / (1 + 1)=HCLK_MSYS /2
//HCLK_DSYS = MOUT_DSYS / (HCLK_DSYS_RATIO + 1) HCLK_DSYS_RATIO对应bit[19:16]
//HCLK_DSYS = MOUT_DSYS / (3 + 1)=MOUT_DSYS /4
//PCLK_DSYS = HCLK_DSYS / (PCLK_DSYS_RATIO + 1) PCLK_DSYS_RATIO对应bit[22:20]
//PCLK_DSYS = HCLK_DSYS / (1 + 1)=HCLK_DSYS /2
//HCLK_PSYS = MOUT_PSYS / (HCLK_PSYS_RATIO + 1) HCLK_PSYS_RATIO对应bit[27:24]
//HCLK_PSYS = MOUT_PSYS / (4 + 1)=MOUT_PSYS /5
//PCLK_PSYS = HCLK_PSYS / (PCLK_PSYS_RATIO + 1) PCLK_PSYS_RATIO对应bit[30:28]
//PCLK_PSYS = HCLK_PSYS / (1 + 1)=HCLK_PSYS /2
//上面的公式就是计算分频的(ldr r2, =0x14131440,也是对0x14131440的理解)
3、Makefile
led_clock: start.o led.o clock.o
arm-linux-ld -Tdram.lds -o led_clock.elf $^
arm-linux-objcopy -O binary led_clock.elf led_clock.bin
arm-linux-objdump -D led_clock.elf > led_clock_elf.dis
gcc mkv210_image.c -o mk210
./mk210 led_clock.bin 210.bin
%.o : %.S
arm-linux-gcc -o aaa@qq.com $< -c -nostdlib
%.o : %.c
arm-linux-gcc -o aaa@qq.com $< -c -nostdlib
clear:
rm *.o *.elf *.bin *.dis mk210 -f
4、led.c
#define GPJ2CON 0xE0200280
#define GPJ2DAT 0xE0200284
#define rGPJ2CON (*(volatile unsigned long*)0xE0200280)
#define rGPJ2DAT (*(volatile unsigned long*)0xE0200284)
void delay(unsigned int r0)
{
volatile int count = r0;
while(count--);
}
//点亮led的函数
void led_blink()
{
//初始化引脚,使对应的GPIO引脚为输出模式
rGPJ2CON = 0x00001111;
//让LED闪烁起来
while(1)
{
//led亮
rGPJ2DAT = 0x0;
//延时
delay(0x100000);
//led灭
rGPJ2DAT = 0xf;
//延时
delay(0x100000);
}
}
5、write2sd
6、mkv210_image.c
上一篇: 按键处理及外部中断
下一篇: Uboot学习笔记(一)ARM汇编