当前位置: 移动技术网 > IT编程>开发语言>Java > (8)初始化系统时钟

(8)初始化系统时钟

2020年07月13日  | 移动技术网IT编程  | 我要评论

一、系统时钟介绍
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 $@ $< -c -nostdlib

%.o : %.c
	arm-linux-gcc -o $@ $< -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

本文地址:https://blog.csdn.net/weixin_47123600/article/details/107300276

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网