概述
当你会用手上的开发板,点个灯后,就可以学习时钟体系了。还不会或者有疑问的可以看看这篇
嵌入式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 验证效果所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复