S3C2440 初始化时钟
一、S3C2440 时钟
1、时钟树
- 从s3c2440的技术手册的时钟树可以了解到,它的时钟分四大块
FCLK
,HCLK
,PCLK
,UCLK
。FCLK
主要用于CPU
的运行时钟,HCLK
主要用于内存控制器,中断控制器等一些高速设备的时钟,PCLK
主要用于GPIO控制器,I2C控制器等一些较低速设备的时钟,UCLK
主要给USB控制器提供时钟的。时钟树如下图:
2、S3C2440支持的时钟速率
- 1、
S3C2440
的最高速率:FCLK
最高支持400MHz
,HCLK
最高支持136MHz
,PCLK
最高支持68MHz
。- 2、在本实验将
FCLK
设置为400MHz
,HCLK
设置为100MHz
,PCLK
设置为50MHz
。
二、设置时钟
1、时钟源的选择
- 1、
s3c2440
的时钟源,可以从晶振里来也可以从外部时钟来。来源于哪里可以由OM2
和OM3
管脚设置。- 2、由于我的板子接了
12MHz
的晶振,所以将晶振设置为输入的时钟源;OM2
和OM3
都设置为0。
2、锁相环设置(MPLLCON寄存器)
MPLLCON
寄存器:
- MPLL 时钟的计算公式:
S3C2440
技术手册提供了配置值,直接使用它的配置值,这里就不自己计算了。
3、配置HCLK和PCKL分频(CLKDIVN寄存器)
4、设置锁存存时间(LOCKTIME)
- 由于配置锁相环的时候需要等待它输出稳定的时钟,等待的这段时间由
LOCKTIME
配置
LOCKTIME
寄存器使用默认的值0xFFFFFFFF
即可
5、设置异步模式
S3C2440
技术手册有说明,如果FCLK
的时钟频率不等于HCLK
的频率时需要配置成异步模式,否则CPU
使用HCLK
作为运行的时钟频率。设置的方法手册上也有,需要通过设置它的协处理器。
- 对于
#R1_nF:OR:R1_iA
这个立即数是什么,在S3C2440
手册上没有给出。一般这些跟CPU核相关的设置,我们可以去看它的CPU核的技术文档(S3C2440
的CPU核是arm920t
)。在arm920t
技术文档找到了nF
和iA
。如下图:
三、程序编写
1、汇编程序(Startup.S)
- 因为要设置
p15
协处理器,所以初始化时钟用汇编语言来写。
.text /*定义代码段*/
.global _start /*标号_start是GNU连接器用来指定第一个要执行所必须的(只能出现在一个模块),.global将_start声明为全局可见*/
_start:
/* 关看门狗 */
ldr r1,=0x53000000
mov r0,#0x00000000
str r0,[r1]
/* 设置栈 */
/* 自动分辨是nor flash 启动还是nand flash启动 */
/* 先将一个数写道0地址,然后读出来判断跟写入的值是否一样;跟写入的一样则是nand flash启动,跟写入的值不一样则是nor flash 启动 */
mov r1, #0
ldr r0, [r1] /* 读出原来的值备份 */
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2=[0] */
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */
ldr sp, =0x40000000+0x1000 /* 先假设是nor启动,0x40000000 + 4k处 */
moveq sp, #0x1000 /* nand启动, 将栈设置在4k处 */
streq r0, [r1] /* 恢复原来的值 */
#if 1
/* 初始化时钟 */
/* 设置锁存时间 LOCKTIME=0xffffffff */
ldr r1,=0x4C000000
ldr r0,=0xffffffff
str r0,[r1]
/* 设置HDIVN,PDIVN分频系数,让FCLK:HCLK:PCLK=1:4:8 */
ldr r1,=0x4C000014
mov r0,#0x00000005
str r0,[r1]
/* 设置Mpll锁相环 MDV=0x5c,PDIV=1,SDIV=1 */
ldr r1,=0x4C000004
ldr r0,=((0x5c<<12)|(1<<4)|(1<<0))
str r0, [r1]
/* 设置锁相环时钟源,使能锁相环 */
ldr r1,=0x4C000010
mov r0,#0x00000004
str r0,[r1]
/* 设置时钟为异步模式 */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000
mcr p15,0,r0,c1,c0,0
#endif
b main
loop:
b loop
2、主程序(main.c)
main
函数里面主要做了led灯闪烁的事情
/* 定义GPFCON 寄存器 */
#define GPFCON *((volatile unsigned long*)0x56000050)
/* 定义GPFDAT 寄存器 */
#define GPFDAT *((volatile unsigned long*)0x56000054)
void delay(int tim){
while(tim--);
}
int main(){
/* 清零 */
GPFCON &= ~(0x03 << 8);
/* 设置为output */
GPFCON |= (0x01<<8);
while(1){
/* 将 GPF4 输出低电平 */
GPFDAT &= ~(1<<4);
delay(0x10000);
/* 将 GPF4 输出高电平 */
GPFDAT |= (1<<4);
delay(0x10000);
}
return 0;
}
3、Makefile
all:
arm-linux-gcc -c Startup.S -o Startup.o
arm-linux-gcc -c main.c -o main.o
arm-linux-ld -Ttext 0 Startup.o main.o -o Led.elf
arm-linux-objcopy -O binary -S Led.elf Led.bin
arm-linux-objdump -D Led.elf > led.dis
clean:
rm *.o *.elf *.bin *.dis
四、实验现象
通过初始化时钟后和不初始化时钟的led灯闪烁的快慢成都不一样。通过现象可以看到初始化时钟后led灯闪烁的更快,也就是说CPU的运行频率更快了。