在讲述系统时钟之前,因为这些设备都是挂靠在系统时钟上的,所以必须先说系统时钟,S3C2440的时钟系统如下
外部时钟源分两种,晶振或者外部频率,由om3-2选择,时钟电路根据两种选择也有两种
我们来分析时钟图可以得到以下结论: 经过选择的外部时钟进入MPLL,进行锁相环倍频,经过锁相环之后的时钟MPLL_IN分成了三股,分别是FCLK,HCLK,PCLK.这其中HCLK和PCLK又是从HCLK分频得到的,最后ARM920T这个系统内核模块得到了两个时钟HCLK和FCLK,DMA控制器,LCD控制器,内存控制器,总线控制器,外部nand控制器和TIC,摄像头接口都来自于HCLK时钟,LCD控制器,nand控制器,cam摄像头控制器的时钟可以选择从总线上切断开来,IIC WDT IIS PWM SDI GPIO ADC RTC UART012 SPI AC97这些外设都挂接在PCLK总线上,而且除了WDT外,都可以断开和总线的连接,另外USB时钟直接从MPLL_IN倍频得到,生成UCLK,USB主机时钟可以选择UCLK或者HCLK,USB设备时钟可选UCLK或者PCLK,所以,有时候初次接触芯片看图是一个很好的选择,文字偏于细节,看图能很快浏览全貌,通过我们的分析可以得到下面这个再次简化的文档
输入时钟 |
MPLL |
FCLK |
HCLK |
内存控制器,中断控制器,nand控制器,tic控制器等,系统内核920T,DMA控制器 |
PCLK |
基本外设RTC,UART,spi等外设 |
|||
只供给系统内核, |
||||
USB PLL |
UCLK |
USB主机时钟,usb设备时钟 |
这就是系统时钟的基本分布,剩下的细节无非就是如何分频,如何启用时钟停止时钟,锁相环配置等等了,首先关注MPLL
这是PLL的锁定时间,一般设置小一点查询是否锁定成功就好
这三个值搭配就可以选择不同的MCLK了,数据手册上有公式,经过MPLL倍频,就成功地获得了系统主时钟FCLK(也就是ARM920T时钟)
通过这个寄存器进行时钟的使能与切断,要使用外设的时候不要忘记这个寄存器
该寄存器确认以一种什么样的比例将FCLK分频为HCLK和PCLK
到这里基本上就描述完了系统时钟,只要弄清楚框架其实时钟也挺简单的,分频系数,功率控制,最主要要弄清楚哪个设备用了哪个时钟就OK了
接下来展示一段代码用来计算系统时钟(在2440init.s里面就有设置时钟的过程,可以参考)
Clock.c
#include "clock.h" #define FIN (12000000) U32 FCLK; U32 HCLK; U32 PCLK; U32 UCLK; void CalcBusClk(void) //计算总线频率 { U32 val,UPLL; U8 m, p, s; val = rMPLLCON; m = (val >> 12) & 0xff; p = (val >> 4) & 0x3f; s = val & 3; FCLK = ((m+8)*(FIN/100)*2)/((p+2)*(1<<s))*100; val = rCLKDIVN; m = (val >> 1) & 3; p = val & 1; val = rCAMDIVN; s = val >> 8; switch (m) { case 0: HCLK = FCLK; break; case 1: HCLK = FCLK >> 1; break; case 2: if(s & 2) HCLK = FCLK >> 3; else HCLK = FCLK >> 2; break; case 3: if(s & 1) HCLK = FCLK / 6; else HCLK = FCLK / 3; break; } if(p) PCLK = HCLK >> 1; else PCLK = HCLK; val = rUPLLCON; m = (val >> 12) & 0xff; p = (val >> 4) & 0x3f; s = val & 3; UPLL = ((m+8)*FIN)/((p+2)*(1<<s)); UCLK = (rCLKDIVN&8)?(UPLL>>1):UPLL; } //************************[ HCLK, PCLK ]*************************** void ChangeClockDivider(int hdivn_val,int pdivn_val) { int hdivn=2, pdivn=0; // hdivn_val (FCLK:HCLK)ratio hdivn // 11 1:1 (0) // 12 1:2 (1) // 13 1:3 (3) // 14 1:4 (2) // pdivn_val (HCLK:PCLK)ratio pdivn // 11 1:1 (0) // 12 1:2 (1) switch(hdivn_val) { case 11: hdivn=0; break; case 12: hdivn=1; break; case 13: case 16: hdivn=3; break; case 14: case 18: hdivn=2; break; } switch(pdivn_val) { case 11: pdivn=0; break; case 12: pdivn=1; break; } rCLKDIVN = (hdivn<<1) | pdivn; switch(hdivn_val) { case 16: // when 1, HCLK=FCLK/8. rCAMDIVN = (rCAMDIVN & ~(3<<8)) | (1<<8); break; case 18: // when 1, HCLK=FCLK/6. rCAMDIVN = (rCAMDIVN & ~(3<<8)) | (1<<9); break; } if(hdivn!=0) MMU_SetAsyncBusMode(); else MMU_SetFastBusMode(); } //**************************[ UPLL ]******************************* void ChangeUPllValue(int mdiv,int pdiv,int sdiv) { rUPLLCON = (mdiv<<12) | (pdiv<<4) | sdiv; } //***************************[ MPLL ]**************************** void ChangeMPllValue(int mdiv,int pdiv,int sdiv) { rMPLLCON = (mdiv<<12) | (pdiv<<4) | sdiv; } //*************************时钟配置函数***************************** void SetClock(u8 mpll) { if(mpll == MPLL271) { ChangeMPllValue(173,2,2); } else if(mpll == MPLL304) { ChangeMPllValue(68,1,1); } else if(mpll == MPLL405) { ChangeMPllValue(127,2,1); } else if(mpll == MPLL532) { ChangeMPllValue(125,1,1); } ChangeClockDivider(14,12); //设置分频比为1:4:8 fclk hclk pclk //并设置cpu异步总线模式 CalcBusClk(); //计算总线频率 }
Clock.h
#ifndef __CLOCK_H_ #define __CLOCK_H_ #include "def.h" #include "2440addr.h" #include "2440slib.h" #define FIN (12000000) //外部晶振 #define MPLL271 1 //几个典型的时钟宏定义 #define MPLL304 2 #define MPLL405 3 #define MPLL532 4 void CalcBusClk(void); //计算总线频率 void ChangeClockDivider(int hdivn_val,int pdivn_val);//计算分频数值 void ChangeUPllValue(int mdiv,int pdiv,int sdiv);//配置usb总线 void ChangeMPllValue(int mdiv,int pdiv,int sdiv); void SetClock(u8 mpll);//配置系统时钟 extern U32 FCLK; extern U32 HCLK; extern U32 PCLK; extern U32 UCLK; #endif
现在有了几个系统时钟的具体数字,我们就可以很好的进行外设时钟的配置了
注意,设置系统时钟的时候HCLK如果太大会导致取值SDRAM跟不上,为了解决这个问题,三星说了这样一个方案,大家要注意哦: