按照S5PV210的官方手册使用C语言配置系统时钟
--参考朱有鹏ARM裸机编程
我需要完成的目标就是设置系统时钟为官方手册一样的样子
了解一下S5PV210的时钟系统是怎么配置的?以便于在其他的处理器也进行这种配置。
1、实验的目标:
这里的数据手册可能比较老,我们设置为ARMCLK:1GHz比较好
2、S5PV210的时钟框图的理解:
(1)、由图中可以知道,我们可以通过很多地方得到外部晶振。
210的晶振是统一的24MHZ,我们使用XUSBXT1这个端口的信息。
(2)、里面有几个MUX梯形的东西,就是选择信号来源的地方,
可以进行编程选择。
(3)、我们目标是启动这么多外设的时钟,但是我们外设的时钟是怎么来的?
答案是通过外部的24MHZ,但是这个频率太慢了,所以我们需要倍频进行处理。
也就是通过这些PLL锁相环进行处理。
(4) 举一个例子:我们怎么得到PCLK_MSYS这个时钟的频率:
第一,我们是通过XUSBxt1从外部进来一个24MHZ
第二,我们通过选择开关1,进入得到FINPLL信号
第三,我们通过APLL进行锁相环的倍频处理,此时如果不在MUXapll进行选择开关1的话,
那么信号就无法传输到后面的程序
第四,我们需要经过一个名叫,MUXMSYS的选择开关,此时会经过一个分频器DIVAPLL
此时,如图所示,我们得到的ARMCLK的主频的时钟
第五,我们经过一个分频器DIVHCLKM分频,可以按照我们的要求分频出一个
HCLK_MSYS这个信号的时钟
第六:最后也是我们的目标的频率,PCLK_MSYS,我们通过DIVPCLKM
设置我们ARM的主频:我们需要设置的寄存器就是APLL的锁相环:
ARMCLK:
由数据手册的记录中可以知道,我们需要通过计算公式得到:
FOUT = 1000MHZ ,也就是说我们需要知道MDIV, PDIV, SDIV
FIN就是我们210规定的24MHZ;
根据数据手册的推荐,我们可以知道
MDIV = 125,PDIV = 3, SDIV=1的时候可以得到这个1GHz的主频
就这样我们可以通过上面来配置S5PV210的主频,接下来所有的分频频率都可以通过分频器的寄存器
来得到我们想要的频率:
可以从上面的图可以得出:DIVAPLL = 1,也就是它压根没有进行分频
但是我们HCLK_MSYS的目标的频率是200MHZ,所以我们需要
主频进行5分频才可以得到,所以DIVHCLK的分频器需要进行5分频
同理可以得到PCLK_CLK需要100MHZ的频率,所以DIVPCLK需要从HCLKCLK进行2分频可以得到。
这是配置有关时钟的寄出器的代码可以进行参考,但是关键还是读懂前面的那张图
//clock.c
// 时钟控制器基地址
#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
// 这些M、P、S的配置值都是查数据手册中典型时钟配置值的推荐配置得来的。
// 这些配置值是三星推荐的,因此工作最稳定。如果是自己随便瞎拼凑出来的那就要
// 经过严格测试,才能保证一定对。
#define APLL_MDIV 0x7d // 125
#define APLL_PDIV 0x3
#define APLL_SDIV 0x1
#define MPLL_MDIV 0x29b // 667
#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)
//这里是我们需要编写代码的地方:
#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)
//寄存器地址的定义:
#define REG_CLK_SRC0 (ELFIN_CLOCK_POWER_BASE + CLK_SRC0_OFFSET)
#define REG_APLL_LOCK (ELFIN_CLOCK_POWER_BASE + APLL_LOCK_OFFSET)
#define REG_MPLL_LOCK (ELFIN_CLOCK_POWER_BASE + MPLL_LOCK_OFFSET)
#define REG_CLK_DIV0 (ELFIN_CLOCK_POWER_BASE + CLK_DIV0_OFFSET)
#define REG_APLL_CON0 (ELFIN_CLOCK_POWER_BASE + APLL_CON0_OFFSET)
#define REG_MPLL_CON (ELFIN_CLOCK_POWER_BASE + MPLL_CON_OFFSET)
//定义一个东西取寄存器内部的内容:
#define rREG_CLK_SRC0 (*(volatile unsigned int *)REG_CLK_SRC0)
#define rREG_APLL_LOCK (*(volatile unsigned int *)REG_APLL_LOCK)
#define rREG_MPLL_LOCK (*(volatile unsigned int *)REG_MPLL_LOCK)
#define rREG_CLK_DIV0 (*(volatile unsigned int *)REG_CLK_DIV0)
#define rREG_APLL_CON0 (*(volatile unsigned int *)REG_APLL_CON0)
#define rREG_MPLL_CON (*(volatile unsigned int *)REG_MPLL_CON)
void clock_init(void)
{
//1、设置各种时钟开关,暂时不使用PLL
rREG_CLK_SRC0 = 0x0;
//2、设置锁定时间,使用默认值就可以了
//设置PLL后时钟从FIN提升到目标频率,需要一定的时间,集锁定时间
rREG_APLL_LOCK = 0x0000ffff;
rREG_MPLL_LOCK = 0x0000ffff;
//3、设置时钟的分频系数
//清除bit[0~31]
rREG_CLK_DIV0 = 0x14131440;
//4、设置PLL
rREG_APLL_CON0 = APLL_VAL;
rREG_MPLL_CON = MPLL_VAL;
//5/设置各种时钟开关,使用PLL
rREG_CLK_SRC0 = 0x10001111;
}