我是靠谱客的博主 大意路灯,最近开发中收集的这篇文章主要介绍嵌入式Linux入门-读数据手册,设置时钟,让代码跑得更快1. 了解时钟体系2. 编程设置时钟3. 完整编程3.3 验证效果,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

当你会用手上的开发板,点个灯后,就可以学习时钟体系了。还不会或者有疑问的可以看看这篇

嵌入式Linux入门-从启动代码开始,真正从0开始点个灯

 嵌入式Linux学习系列全部文章:嵌入式Linux学习—从裸机到应用教程大全 

我不喜欢上来就讲怎么写代码,怎么操作,那和嵌入式学习方法其实是相违背的。

学习嵌入式,读Datasheet(数据手册)的能力必不可少,特别是读英语原版手册

所以呢,我们就从手册开始。

所用SOC:S3C2440,ARM架构。(再次说明,什么板子都差不多,关键在于学习流程和方法)

1. 了解时钟体系

让我们翻开芯片手册,找到时钟那一章。开头就非常明确的说明了,什么时钟用在什么地方(红框部分)。

FCLK用于CPU,HCLK用于AHB外设,PCLK用于APB总线外设。

AHB(Advanced High Performance Bus),高级高性能总线。也就是速度比较快,比如sdram。

APB(Advanced Peripheral Bus),外围总线的意思。速度相对慢,比如UART。

1.1 选择时钟源

我们接着往下看

 这一段告诉我们:接下来有个框图,展示了时钟体系结构。主时钟来自外部晶振或者外部时钟

从左上的入口开始看,可以看出时钟源有两种分别为 :晶振(通过OSC进入)、外部时钟。(跟文字描述对上了)
选择时钟源方法:通过设置OM3、OM2的引脚可以设置OM[3:2]的状态,来选择时钟源。

这是怎么知道的?框图上有,但是新手看框图可能看不明白,我们回到手册看

 文字描述很清晰地说明了,OM3和OM2引脚如何决定了选择时钟源。crystal就是晶振,EXTCLK就是external clock,外部时钟。

1.2 时钟频率的计算

接下来就看到了这里,这一段可能就比较模糊难懂了,不需要完全懂。

锁相环(PLL)就是用来使输入的时钟源产生不同频率的时钟的,我们结合之前的时钟体系大框图。

从入口看:通过OM的时钟源进入MPLL锁相环,MPLL直接提供给FCLK, FCLK通过HDIVN分频给HCLK,通过PDIVN分频给PCLK。

重要结论:FCLK=MPLL输出频率

不能完全看懂上面的英文,但是可以看到MPLL的计算公式!这就够了。

重要结论:通过设置MPLLCON里的P、M、S 的值,可以设置PLL的输出频率(MPLL)。

 1.3 时序

接着看会发现这样的图

通过上面时序图发现刚上电时FCLK就是外部晶振频率,12MHZ,但是运行一段时间后,通过程序改变其频率设置,但是会有一个锁定时间Lock Time,这个时间也是可以调节的,过来这个时间后FCLK才变成用户想要的频率。

其实仔细看手册的朋友,在看到这张图之前,就会发现前面早就有文字描述

就是前文提到过的内容下面还有NOTES:注意:

虽然MPLL在复位后就启动,但直到软件将有效的设置写入MPLLCON寄存器时,才将MPLL输出(MPLL)作为系统时钟。在此有效设置之前,外部crystal或EXTCLK源的时钟将直接用作系统时钟。即使用户不想更改MPLLCON寄存器的默认值,也应该将相同的值写入MPLLCON寄存器。

重要结论:LOCKTIME可设置,MPLL设置要放在最后,因为设置完结果locktime,就启用新频率了。

归纳上文

以外部时钟源为晶振为例:
1 上电,复位。
2 FCLK根据OM[3:2]的值, FCLK=晶振。
3 等待晶振发出时钟稳定后,释放nRESET,PLL锁存OM[3:2]的值。
4 配置MPLL,进入lock time,等待配置完成。此时的FCLK无输出,CPU停止。
5 配置完成,lock time 时间到,然后FCLK输出按照新配置的频率,CPU运行。

2. 编程设置时钟

目的:使FCLK=400MHz,HCLK=100MHz,PLCK=50MHz
使用时钟源为12MHz的晶振

根据前面分析可得:

1.OM[3:2]的状态设置为00,即OM3 、OM2引脚都接地。

2.设置LOCKTIME
4.设置时钟分频控制(CLKDIVN)寄存器的HDIVN、PDIVN
3.设置MPLLCON寄存器的P、M、S

还是那句话,要实现什么功能,无非就是

1.设置相应控制寄存器

2.设置相应数据寄存器

我们看懂了前文,就知道,我们下一步就该去数据手册找相应寄存器的地址如何配置了。

2.1 设置LOCKTIME

 LOCKTIME我们取默认值就行了,保险起见,通过程序设置一遍。

	ldr r0, =0x4C000000
	ldr r1, =0xFFFFFFFF
	str r1, [r0]

 2.2 设置CLKDIV

FCLK=400MHz,HCLK=100MHz,PLCK=50MHz

FCLK:HCLK:PLCK=8:2:1;当CAMDIVN[9]=1,HCLK=FCLK/8,查手册,发现默认值就是1,不用管。

因此:CLKDIVN地址为0x4C00014,值应当设置为0x5

	ldr r0, =0x4C000014
	ldr r1, =0x5
	str r1, [r0]

2.3 设置MPLL

Fin为晶振:12M,要想Mpll=400M,很简单,根据公式,凑一凑

      m = MDIV+8 = 92+8=100
      p = PDIV+2 = 1+2 = 3
      s = SDIV = 1
      FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M

MDIV=92,PDIV=1,SDIV=0.

从芯片手册找到MPLLCON的地址0x4C000004.

	ldr r0, =0x4C000004
	ldr r1, =(92<<12)|(1<<4)|(1<<0)
	str r1, [r0]

2.3 找找有没有别的注意事项

到这一步,看似已经完成了,但是还得翻翻手册,看看有没有一些需要设置的地方。

还真的有!

 如果HDIVN不是0, CPU总线模式必须按照如下指令从快速总线模式修改为异步总线模式(S3C2440不支持同步总线模式)。

手册把代码都给你了!直接用

	mrc p15,0,r0,c1,c0,0
	orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA
	mcr p15,0,r0,c1,c0,0

当然,通过时序图我们知道,MPLL设定好后就会进入LOCKTIME,所以这一步,应当放在设置MPLL之前.

3. 完整编程

3.1 启动代码

就是把上文中的连起来,前面加个关闭看门狗,后面加个设置栈,跳转就完事了。

.text
.global _start

_start:

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

	/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
	/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
	ldr r0, =0x4C000000
	ldr r1, =0xFFFFFFFF
	str r1, [r0]

	/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */
	ldr r0, =0x4C000014
	ldr r1, =0x5
	str r1, [r0]

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

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

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

	/* 设置内存: sp 栈 */
	ldr sp, =4096  /* nand启动 */	

	bl main

halt:
	b halt

3.2 C代码

一个让三个LED循环亮起来的小程序,时钟设置成功,运行速度快,就会循环得快。

#include <stdio.h>
void delay(volatile int d)
{
	while (d--);
}

int main(void)
{
	int val = 0;  /* val: 0b000, 0b111 */
	int tmp;
	unsigned int *GPFCON,*GPFDAT;
	GPFCON = 0x56000050;
	GPFDAT = 0x56000054;

	/* 设置GPFCON让GPF4/5/6配置为输出引脚 */
	*GPFCON &= ~((3<<8) | (3<<10) | (3<<12));
	*GPFCON |=  ((1<<8) | (1<<10) | (1<<12));

	/* 循环点亮 */
	while (1)
	{
		tmp = ~val;
		tmp &= 7;
		*GPFDAT &= ~(7<<4);
		*GPFDAT |= (tmp<<4);
		delay(100000);
		val++;
		if (val == 8)
			val =0;
		
	}

	return 0;
}

3.3 验证效果

把上述启动代码,和C代码编译链接得到二进制文件,烧录。

 再来看看把下面这段没有设置时钟的启动代码和C代码连接是什么效果。

.text
.global _start
_start:
//关闭看门狗
	LDR r0,=0x53000000
	MOV r1,#0
	STR r1,[r0]
//初始化堆栈
	LDR SP,=4096
//跳转至main
	BL main
//无限循环
halt:
	B halt

最后

以上就是大意路灯为你收集整理的嵌入式Linux入门-读数据手册,设置时钟,让代码跑得更快1. 了解时钟体系2. 编程设置时钟3. 完整编程3.3 验证效果的全部内容,希望文章能够帮你解决嵌入式Linux入门-读数据手册,设置时钟,让代码跑得更快1. 了解时钟体系2. 编程设置时钟3. 完整编程3.3 验证效果所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(37)

评论列表共有 0 条评论

立即
投稿
返回
顶部