欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

(8)初始化系统时钟

程序员文章站 2022-06-08 23:00:12
...

一、系统时钟介绍
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 中,可以使用不同的分频,使其给不同部件输出所需要的时钟,各类时钟的关系以及参考值设置如下图:
(8)初始化系统时钟
下面贴出数据手册的S5PV210 时钟设置参考图:
(8)初始化系统时钟
二、寄存器
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