目標:
1、了解S3C2440的時鍾體系結構
2、掌握通過設置MPLL改變系統時鍾的方法
一、S3C2440的時鍾體系結構
什么是時鍾體系?
ARM的時鍾系統包括4部分,分為晶體振盪器、喚醒定時器、鎖相環(PLL)和VPB分頻器,是一種電路。
時鍾系統有什么作用?
時鍾是嵌入式系統的脈搏,處理器內核在時鍾驅動下完成指令執行,狀態變換等動作。
外設部件在時鍾的驅動下完成各種工作,比如串口數據的發送、A/D轉換、定時器計數等等。
時鍾體系是怎么運作的?
當系統復位或者處理器從掉電模式喚醒時,“喚醒定時器”要對輸入的時鍾信號做計數延時,使芯片內部的部件有時間進行初始化。
然后Fosc(時鍾源)被PLL(鎖相環/倍頻器)提高到一個符合用戶需要的頻率Fcclk,Fcclk用於CPU內核。因為CPU內核通常比外設部件的工作速度要快,
用戶可以通過設置VPB分頻器,把Fcclk信號降低到一個合適的值Fpclk,該信號用於外設部件。如下圖:
再看看2440的結構圖
由上圖,可知時鍾控制邏輯給整個芯片提供了3種時鍾:
FCLK:幀時鍾,用於CPU核;(如何選擇CPU核的工作時鍾是FCLK還是HCLK?)
HCLK:總線時鍾,用於AHB高速總線上的設備,比如CPU核,存儲器控制器、中斷控制器、LCD控制器等
PCLK:外設時鍾,用於APB總線上的設備,如看門狗,IIS,IIC,PWM,UART,GPIO,SPI,MMC等。
對於2440 外接的晶振僅12MHz, 但CPU核的主頻可以高達400MHz,總線時鍾可以達136MHz, 而外設時鍾可達68MHz,那如何設置才能使不同的時鍾輸出不同的時鍾頻率呢?
通過一些硬件單元來處理,PLL--Phase Locked Loop -- 鎖相環,
2440有兩個鎖相環,MPLL -- Main PLL ,UPLL -- USB PLL,兩者設置方法相同,以MPLL為例。
從芯片手冊上可以看到,時鍾模塊發生框圖:
在該圖的左上角,晶振和一個外部時鍾接在一個選擇器上,這個選擇器通過OM[3:2]的值來決定選擇哪個時鍾源。然后生成的MPLL(Main PLL)和UPLL(USB PLL),MPLL直接提供給FCLK,通過HDIVN分頻給HCLK,通過PDIVN分頻給PCLK,再傳給下面的各個設備:
那如何設置PLL?
二、PLL的設置過程
1)上電幾毫秒后,晶振(SOC)輸出穩定,FCLK 等於晶振頻率,nRESET信號在電壓穩定后恢復高電平,CPU開始執行指令。
2)在nRESET信號的上升沿,引腳OM[2:3]的電平被內部電路捕獲后,就可以操作PLL。在程序開頭啟動MPLL,設置MPLL相關寄存器后,需要等待一段時間(Lock Time),MPLL輸出才穩定。在Lock Time內,FCLK停振,CPU停止工作。Lock Time由寄存器LOCKTIME設定。
3)Lock Time之后,MPLL輸出正常,CPU工作在新的FCLK下。
三、設置寄存器,調整FCLK: HCLK : PCLK ,使CPU工作於400MHz的頻率下
調整FCLK: HCLK : PCLK = 400MHz : 100MHz : 50MHz
—— MPLLCON --FCLK = 400MHz
—— CLKDIVN -- HCLK = FCLK / 4 PCLK = FCLK / 8
(LOCKTIME寄存器使用默認值)
1)時鍾分頻控制寄存器 --- CLKDIVN
不涉及UCLK,[3]取默認值。HCLK 取FCLK/4,[2:1] = 10,且CAMDIVN[9]默認為0。
PCLK = HCLK / 2 = FCLK / 8,故[0] =1
故:CLKDIVN = 0x5;
2)設置MPLLCON,主分頻控制寄存器
先設置MPLLCON,使FCLK=400MHz,
直接查看芯片手冊上的PLL值選擇表
12MHz的晶振,輸出頻率為400MHz時的參數:MDIV=92(0x5c),PDIV =1, SDIV=1
驗證一下:
m = 92+8=100 , p = 1+2=3 , s = 1
MPLL = (2 * 100 * 12MHz) / (3 * 2^1) = 400MHz
MPLLCON = (92 << 12) | (1 << 4) | (1<<0)
3)設置CPU為異步總線模式
基本設置已經完成,在芯片手冊上還有一些注意事項:
1-- CLKDIVN的值不應該超過HCLK和PCLK的最小值
2-- 若HDIVN不為0,CPU總線模式應該使用以下指令使其從快速總線模式改為異步總線模式(S3C2440不支持同步總線模式)
若HDIVN不為0,並且CPU總線模式為快速總線模式,CPU就會運行在HCLK,由此CPU主頻會為FCLK下的一半或一半多。
其中:#R1_nF:OR:R1_iA 為0xC000 0000
實例:
在實現LED循環點燈的程序上實驗,觀察CPU主頻從12MHz改為400MHz前后的變化
12MHz下:

1 #define __REG(x) (*(volatile unsigned int *)(x)) 2 #define GPFCON __REG(0x56000050) //Port F control 3 #define GPFDAT __REG(0x56000054) //Port F data 4 5 void delay(volatile int d) 6 { 7 while (d--); 8 } 9 10 int main(void) 11 { 12 int val = 0; /* val: 0b000, 0b111 */ 13 int tmp; 14 15 /* 設置GPFCON讓GPF4/5/6配置為輸出引腳 */ 16 GPFCON &= ~((3<<8) | (3<<10) | (3<<12)); 17 GPFCON |= ((1<<8) | (1<<10) | (1<<12)); 18 19 /* 循環點亮 */ 20 while (1) 21 { 22 tmp = ~val; 23 tmp &= 7; 24 GPFDAT &= ~(7<<4); 25 GPFDAT |= (tmp<<4); 26 delay(100000); 27 val++; 28 if (val == 8) 29 val =0; 30 31 } 32 33 return 0; 34 }

1 .text 2 .global _start 3 4 _start: 5 6 /* 關閉看門狗 */ 7 ldr r0, =0x53000000 8 ldr r1, =0 9 str r1, [r0] 10 11 /* 設置內存: sp 棧 */ 12 /* 分辨是nor/nand啟動 13 * 寫0到0地址, 再讀出來 14 * 如果得到0, 表示0地址上的內容被修改了, 它對應ram, 這就是nand啟動 15 * 否則就是nor啟動 16 */ 17 mov r1, #0 18 ldr r0, [r1] /* 讀出原來的值備份 */ 19 str r1, [r1] /* 0->[0] */ 20 ldr r2, [r1] /* r2=[0] */ 21 cmp r1, r2 /* r1==r2? 如果相等表示是NAND啟動 */ 22 ldr sp, =0x40000000+4096 /* 先假設是nor啟動 */ 23 moveq sp, #4096 /* nand啟動 */ 24 streq r0, [r1] /* 恢復原來的值 */ 25 26 27 bl main 28 29 halt: 30 b halt 31

1 all: 2 arm-linux-gcc -c -o led.o led.c 3 arm-linux-gcc -c -o start.o start.S 4 arm-linux-ld -Ttext 0 start.o led.o -o led.elf 5 arm-linux-objcopy -O binary -S led.elf led.bin 6 arm-linux-objdump -D led.elf > led.dis 7 clean: 8 rm *.bin *.o *.elf *.dis 9
400MHz下
修改start.s

1 .text 2 .global _start 3 4 _start: 5 6 /* 關閉看門狗 */ 7 ldr r0, =0x53000000 8 ldr r1, =0 9 str r1, [r0] 10 11 /* 設置MPLL, FCLK : HCLK : PCLK = 400MHz : 100MHz : 50MHz */ 12 /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ 13 ldr r0, =0x4C000000 14 ldr r1, =0xFFFFFFFF 15 str r1, [r0] 16 17 /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */ 18 ldr r0, =0x4C000014 19 ldr r1, =0x5 20 str r1, [r0] 21 22 /* 設置CPU工作於異步模式 */ 23 mrc p15,0,r0,c1,c0,0 24 orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA 25 mcr p15,0,r0,c1,c0,0 26 27 /* 設置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) 28 * m = MDIV+8 = 92+8=100 29 * p = PDIV+2 = 1+2 = 3 30 * s = SDIV = 1 31 * FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M 32 */ 33 ldr r0, =0x4C000004 34 ldr r1, =(92<<12)|(1<<4)|(1<<0) 35 str r1, [r0] 36 37 /* 一旦設置PLL, 就會鎖定lock time直到PLL輸出穩定 38 * 然后CPU工作於新的頻率FCLK 39 */ 40 41 42 43 /* 設置內存: sp 棧 */ 44 /* 分辨是nor/nand啟動 45 * 寫0到0地址, 再讀出來 46 * 如果得到0, 表示0地址上的內容被修改了, 它對應ram, 這就是nand啟動 47 * 否則就是nor啟動 48 */ 49 mov r1, #0 50 ldr r0, [r1] /* 讀出原來的值備份 */ 51 str r1, [r1] /* 0->[0] */ 52 ldr r2, [r1] /* r2=[0] */ 53 cmp r1, r2 /* r1==r2? 如果相等表示是NAND啟動 */ 54 ldr sp, =0x40000000+4096 /* 先假設是nor啟動 */ 55 moveq sp, #4096 /* nand啟動 */ 56 streq r0, [r1] /* 恢復原來的值 */ 57 58 59 bl main 60 61 halt: 62 b halt 63
可以觀察到在提高CPU時鍾頻率后,LED閃爍的頻率比之前快了很多。
參考: