在前面的幾篇文章中,每當程式需要延時時,我們是利用循環語句來實作。這種方法的延時簡單,但不是很精确,就是說不能得到确切的一段時間的延時。是以當需要精确延時時,就不能采用這種方法了。一般是利用定時器來實作。在這裡,我們就介紹一下s3c2440定時器的使用方法。
在講解之前,先介紹一下s3c2440時鐘系統。一般來說,MCU的主時鐘源主要是外部晶振或外部時鐘,而用的最多的是外部晶振。在正确情況下,系統内所使用的時鐘都是外部時鐘源經過一定的處理得到的。由于外部時鐘源的頻率一般不能滿足系統所需要的高頻條件,是以往往需要PLL(鎖相環)進行倍頻處理。在s3c2440中,有2個不同的PLL,一個是MPLL,另一個是UPLL。UPLL是給USB提供48MHz。在這裡,我們主要介紹MPLL。外部時鐘源經過MPLL處理後能夠得到三個不同的系統時鐘:FCLK、HCLK和PCLK。FCLK是主頻時鐘,用于ARM920T核心;HCLK用于AHB總線裝置,如ARM920T,記憶體控制,中斷控制,LCD控制,DMA以及USB主子產品;PCLK用于APB總線裝置,如外圍裝置的看門狗,IIS,I2C,PWM,MMC接口,ADC,UART,GPIO,RTC以及SPI。這三個系統時鐘(FCLK、HCLK和PCLK)是有一定的比例關系,這種關系是通過寄存器CLKDIVN中的HDIVN位和PDIVN位來控制的,是以我們隻要知道了FCLK,再通過這兩位的控制,就能确定HCLK和PCLK。而FCLK是如何得到的呢?它是通過輸入時鐘(即外部時鐘源)的頻率,經過一個計算公式(具體公式請查閱資料手冊)得到的,這個計算公式還需要三個參數(MDIV、PDIV、SDIV),而這三個參數是經過寄存器MPLLCON配置得到的。最後,我們用最清晰的線路來繪制一下時鐘的産生過程:外部時鐘源→通過寄存器MPLLCON得到FCLK→再通過寄存器CLKDIVN得到HCLK和PCLK。這個配置過程在啟動檔案中就已完成。在本開發闆上,外部晶振為12MHz,進過MPLL倍頻以後得到400MHz的FCLK,而FCLK、HCLK、PCLK之間的比例關系為1:4:8,是以HCLK為100MHz,PCLK為50MHz。
s3c2440的時鐘系統就介紹到這裡,我們再回到定時器的配置上來。如何才能得到精确的定時呢?那就要靠TCFG0和TCFG1這兩個寄存器來配置定時器的頻率,即要确定TCNTOn每遞減一個數所需要的時間,它們之間是倒數的關系。具體的計算公式為:
定時器輸出時鐘頻率=PCLK ÷ (prescaler+1) ÷ divider
其中prescaler值由TCFG0決定,divider值由TCFG1決定,而prescaler隻能取0~255之間的整數,divider隻能取2、4、8和16。比如已知PCLK為50MHz,而我們想得到某一定時器的輸出時鐘頻率為25kHz,則依據公式可以使prescaler等于249,divider等于8。有了這個輸出時鐘頻率,理論上我們通過設定寄存器TCNTBn就可以得到任意與0.04毫秒(1÷25000×1000)成整數倍關系的時間間隔了。例如我們想要得到1秒鐘的延時,則使TCNTBn為25000(1000÷0.04)即可。
下面我們通過一段程式來示範利用定時器得到精确延時。這裡我們用到的是定時器4。這段程式的作用是讓蜂鳴器每隔2秒鐘響一次,持續時間為0.5秒,蜂鳴器響的同時伴随着LED亮。
#define _ISR_STARTADDRESS 0x33ffff00
#define U32 unsigned int
#define
pISR_TIMER4 (*(unsigned
*)(_ISR_STARTADDRESS+0x58))
#define rSRCPND (*(volatile unsigned
*)0x4a000000) //Interrupt request status
#define rINTMSK (*(volatile unsigned
*)0x4a000008) //Interrupt mask control
#define rINTPND (*(volatile unsigned
*)0x4a000010) //Interrupt request status
#define rGPBCON (*(volatile unsigned
*)0x56000010) //Port B control
#define rGPBDAT (*(volatile unsigned
*)0x56000014) //Port B data
#define rGPBUP (*(volatile unsigned
*)0x56000018) //Pull-up control B
#define rTCFG0 (*(volatile unsigned
*)0x51000000) //Timer 0 configuration
#define rTCFG1 (*(volatile unsigned
*)0x51000004) //Timer 1 configuration
#define rTCON (*(volatile unsigned
*)0x51000008) //Timer control
#define rTCNTB4 (*(volatile unsigned
*)0x5100003c) //Timer count buffer
4
void __irq Timer4_ISR(void)
{
static int count;
count ++;
rSRCPND = rSRCPND |
(0x1<<14);
rINTPND = rINTPND |
//每隔2秒蜂鳴器響一次,持續時間為0.5秒,并伴随着LED亮
if (count % 4 ==0)
rGPBDAT
=
~0x1e0; //蜂鳴器響,LED亮
else if (count % 4 ==1)
= 0x1e0; //蜂鳴器不響,LED滅
}
void Main(void)
rGPBCON =
0x155555; //B0輸出,給蜂鳴器;B5~B8輸出,給LED
rGPBUP = 0x7ff;
rGPBDAT =
0x1e0; //蜂鳴器不響,LED滅
rINTMSK =
~(0x1<<14); //打開定時器4中斷
rTCFG0 &= 0xFF00FF;
rTCFG0 |=
0xf900; // prescaler等于249
rTCFG1 &=
~0xF0000;
rTCFG1 |=
0x20000; //divider等于8,則設定定時器4的時鐘頻率為25kHz
rTCNTB4 =
12500; //讓定時器4每隔0.5秒中斷一次
rTCON &= ~0xF00000;
rTCON |= 0x700000;
rTCON &= ~0x200000
; //定時器4開始工作
pISR_TIMER4 = (U32)Timer4_ISR;
while(1)
{
;
}