【韦东山旧1期学习笔记】05.S3C2440系统时钟实验

S3C2440时钟发生器结构

在这里插入图片描述
        通过S3C2440芯片手册第7章时钟和电源管理中的7.1时钟发生器框图可知,其时钟源有俩个,一个是外部晶振(XTIpll),一个是外部时钟(EXTCLK)。具体使用的是哪个时钟源,是由模式控制引脚OM3和OM2两个引脚共同决定的。在系统启动时,在复位信号nRESET信号的上升沿,S3C2440将OM3和OM2引脚状态锁存。具体如下图所示:
在这里插入图片描述
        从时钟发生器框图中可以看出,如果使用外部晶振,则S3C2440内部还接有一个OSC晶振放大器。之后是两个锁相环PLL(Phase-Locked-Loop)。这两个PLL(MPLL、UPLL)用于产生所需的更高频率的时钟。MPLL用于生成FCLK,HCLK和PCLK。UPLL则专用于USB模块。
        对于有MPLL分出来的三种时钟:FCLK用于CPU核;HCLK用于AHB总线上的设备,比如CPU核、存储器控制器、中断控制器、LCD控制器、DMA控制器和USB主机模块等;PCLK用于APB总线上的设备,比如看门狗、IIS、IIC、PWM定时器、MMC接口、ADC、UART、GPIO、RTC和SPI等设备。
        需要注意的是,当系统启动时,系统不会使用MPLL作为系统时钟,而是直接使用外部晶振或外部时钟。只有当MPLLCON寄存器被设置了之后,系统才会使用MPLL作为系统时钟。所以即使不需要更改MPLLCON的默认值,也需要将其再次写入该寄存器。

外部时钟源选择

结合JZ2440开发板电路图时钟相关引脚如下所示:
在这里插入图片描述
在这里插入图片描述
从上图的红框处可以看出,OM[3:2]被硬件强制置为00,这样,MPLL和UPLL均使用外部12MHz晶振。
在这里插入图片描述

上电时序

在这里插入图片描述

  1. 当使用外部晶振时,外部晶振会在上电若干毫秒(milliseconds)之后开始振荡。此时,虽然当复位信号nRESET恢复为高电平时,PLL会按照默认配置值开始工作,但是由于PLL在上电复位后工作不稳定,所以系统直接使用外部输入的时钟Fin替代MPLL作为系统时钟,此时FCLK=Fin,即12MHz。
  2. 当使用软件设置了PLLCON寄存器后,经过一定的锁定时间,PLL才会稳定输出,系统此时才会使用PLL作为系统时钟。在锁定时间内,FCLK停振,CPU停止工作。锁定时间的长短由LOCKTIME寄存器的值决定。
  3. 锁定时间之后,PLL工作正常,自此之后,FCLK=PLL

寄存器设置

  1. 设置锁定时间计数寄存器 (LOCKTIME)
    在这里插入图片描述
    由S3C2440用户手册可知,LOCKTIME寄存器中,位[31:16]用于UPLL,位[15:0]用于MPLL。
    由手册可知,设置的LTIME值需满足 时钟周期*LTIME>300μs。初始值为最大值0xFFFF,即65535,而外接的晶振为12MHz,即时钟周期为0.0833μs,故锁定时间为0.0833μs*65535=‭5459μs,符合要求。故使用默认值即可。

    写入值之后,系统会自动插入一个PLL LOCK TIME,也就是LOCKTIME 寄存器中设置的300us。300us 后,PLL 就开始正常工作。

  2. 设置PLL控制寄存器 (MPLLCON & UPLLCON)
    在这里插入图片描述
    即使不想改变复位后PLLCON 的值,也仍然需要将默认值写入PLLCON寄存器中。写入后,系统会自动插入一个PLL LOCK TIME,之后,PLL 开始正常工作。 如果是想更改PLLCON的值,可按照下图所述方法进行设置。
    在这里插入图片描述
    从上图可以看到,1≤MDIV≤248,1≤PDIV≤62,且最终设置的FCLK输出必须位于闭区间[200MHz, 600MHz]之内。由于这里我们不使用USB模块,为简单起见,我们只设置MPLLCON。如果要设置USB时钟,则应该先设置UPLLCON,之后间隔7个NOP指令时间,再设置MPLLCON。

    除了通过公式计算之外,手册还提供了预置值表格,如下所示:
    在这里插入图片描述
    通过手册我们可知,FCLK支持的最大工作频率为400MHz,HCLK为136MHz,PCLK为68MHz。为达到系统最佳性能,选取FCLK=400MHz,即MDIV=0x5C,PDIV=0x1,SDIV=0x1。
    即MPLLCON = (0x5C << 12) | (0x1 << 4) | (0x1);

  3. 设置时钟控制寄存器(CLKCON)
    CLKCON控制各种模块如UART、IIC等的时钟电路开关以及系统的SLEEP、IDLE模式,以便降低系统功耗。通俗来讲,就是可以通过设置这个寄存器来关掉用不到的设备。默认值是全部时钟电路打开,系统工作在正常模式。这里我们使用默认值。
    在这里插入图片描述

  4. 设置CLKSLOW寄存器
    用来选择系统是否进入慢模式,以及是否关闭MPLL 或UPLL,和设置在慢模式下的分频率。我们不使用慢模式,故不设置该寄存器。
    在这里插入图片描述

  5. 设置时钟比例控制寄存器CLKDIVN和照相机时钟比例寄存器CAMDIVN
    这两个寄存器配合使用,来确定FCLK,HCLK,PCLK 的比例。
    在这里插入图片描述
    在这里插入图片描述
    由于查看手册,FCLK支持的最大工作频率为400MHz,HCLK为136MHz,PCLK为68MHz。由于HDIVN的比例系数只有1、1/2、1/3、1/4、1/6、1/8,故符合最大频率要求的分频系数为HDIVN=1/4。此时HCLK=100MHz,在此基础上,PCLK应设置为50MHz,即分频系数为PDIVN=1/2。

CLKDIVN=(0x2 << 1) | (0x1);
CAMDIVN&=(~(0x1 << 9));
由于此时HDIVN不为0,故还要参考如下说明:
在这里插入图片描述

  1. CLKDIVN需要合理设置,使得HCLK和PCLK不会超出最大工作频率;
  2. 如果HDIVN不为0,则需要将CPU的总线模式从快速总线模式切换到异步总线模式。涉及到的代码如下:
MMU_SetAsyncBusMode:
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0

如果HDIVN不为0,且CPU的总线模式为快速总线模式,那么,CPU将使用HCLK作为系统时钟。
上述代码中最奇怪的地方就是R1_nF:OR:R1_iA。由于该指令是设置p15协处理器的c1寄存器,故参考ARM920T(Rev 1)Technical Reference Manual手册来获取有关15号协处理器的c1寄存器的相关信息。
查阅手册第2章程序员模型 可知,15号协处理器的所有寄存器如下所示:
在这里插入图片描述
其c1寄存器如下所示:
在这里插入图片描述
现在我们可知R1_nF:OR:R1_iA指的是c1寄存器的第nF位和第iA位,也就是第31位和第30位。关于这两位的设置参考下表:
在这里插入图片描述
可知,我们把nF位和iA位设置均置1,即可将CPU总线模式切换到异步总线模式。即将c1的第31位和第30位置1即可。故对于异步总线模式,#R1_nF:OR:R1_iA等于 #((0x1<< 31) | (0x1 << 30)),即#0xc0000000。

代码实验

如上分析,最为合适的时钟分频结果为FCLK=400MHz,HCLK=100MHz,PCLK=50MHz。
按照逻辑,应该先设置好锁定时间和分频比例系数,之后在设置MPLLCON,之后即可使新的系统时钟频率生效。
因此,我们基于上一篇文章的GPIO实验,修改CRT0.S文件如下所示:

.text 
.global _start

_start:

	/* 1.关闭看门狗 */
	ldr r0, =0x53000000
	ldr r1, =0
	str r1, [r0]

	/* 2.设置时钟 */
	/* 2.1 设置LOCKTIME(0x4C000000)=0xFFFFFFFF */
	ldr r0, =0x4C000000 
	ldr r1, =0xFFFFFFFF
	str r1, [r0]

	/* 2.2 设置CLKDIVN(0x4C000014) = 0x5 FCLK : HCLK : PCLK = 400m : 100m : 50m*/
	ldr r0, =0x4C000014
	ldr r1, =0x5
	str r1, [r0]

	/* 2.3 设置CPU处于异步模式 */
	mrc p15,0,r0,c1,c0,0
	orr r0,r0,#0xc0000000 /* #R1_nF:OR:R1_iA */
	mcr p15,0,r0,c1,c0,0

	/* 2.4 设置MPLLCON(0x4C000004)=(92<<12)   | (1 << 4) | (1 << 0)
	 *	   m = MDIV + 8 = 100
	 *	   p = PDIV + 2 = 3
	 *	   s = SDIV = 1	
	 * 	   Mpll = (2 * m * Fin) / (p * 2 ^ s)= (2 * 100 * 12) / (3 * 2 ^ 1) = 400MHZ
	 */
	ldr r0, =0x4C000004
	ldr r1, =(92<<12) | (1 << 4) | (1 << 0)
	str r1, [r0]

      /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
       * 然后CPU工作于新的频率FCLK
       */


	/* 3.设置栈
	 *   自动分辨NOR启动或者NAND启动
	 *   向0地址写入0,在读出来,如果写入则是NAND,否则是NOR
	 */
	ldr r0, =0
	ldr r1, [r0] /* 读出原来的值备份 */
	str r0, [r0] /* 向0地址写入0 */
	ldr r2, [r0] /* 再次读出来 */
	cmp r1, r2
	ldr sp, =0x40000000 + 4096 /* nor启动 */
	movne sp, #4096			   /* nand启动 */
	strne r1, [r0]			   /* 恢复原来的值 */


	bl main

halt:
	b halt

Clock.c文件如下所示:

#define GPFCON      (*(volatile unsigned int *)0x56000050)
#define GPFDAT      (*(volatile unsigned int *)0x56000054)

void  wait(volatile unsigned int delay) {
        unsigned int idx = delay;
        for(; idx > 0; idx--)
                ;
}

int main(void) {
        //设置GPF4、GPF5、GPF6为输出口
        GPFCON = ((0x1 << 8) | (0x1 << 10) | (0x1 << 12));    
        while(1) {
                //GPF4输出0,LED1点亮
                GPFDAT = ~(0x1 << 4);
                //加入延时,方便观察
                wait(60000);
                //GPF5输出0,LED2点亮
                GPFDAT = ~(0x1 << 5);
                //加入延时,方便观察
                wait(60000);
                //GPF6输出0,LED4点亮
                GPFDAT = ~(0x1 << 6);
                //加入延时,方便观察
                wait(60000);
        }

        return 0;
}

相应的Makefile文件如下所示:

Clock.bin : CRT0.o Clock.o
        arm-linux-ld -Ttext 0x0000000 -g $^ -o Clock.elf
        arm-linux-objcopy -O binary -S Clock.elf $@

CRT0.o : CRT0.S
        arm-linux-gcc -g -c -o $@ $^
Clock.o : Clock.c
        arm-linux-gcc -g -c -o $@ $^

clean:
        rm -f   Clock.bin Clock.elf *.o

程序编译后烧写进开发板,运行效果图如下所示:
在这里插入图片描述
而使用默认12MHz的时钟频率是的运行效果图如下所示:
在这里插入图片描述
可见,提升系统时钟频率后,性能提升效果明显。

发布了26 篇原创文章 · 获赞 2 · 访问量 1081

猜你喜欢

转载自blog.csdn.net/BakerTheGreat/article/details/104153762
今日推荐