系統定時器也位於“私有外設總線”(Private peripheral bus)內,其地址為0xE000E010~0xE000E0FF。下面先來看一下SysTick的內部結構,如下圖所示。


下面給出的是上表中控制寄存器SYST_CSR的全部位結構,其字節地址為0xE000E010。

(2)第1位(TICKINT)是SysTick的中斷使能位,置1使能中斷,置0禁止中斷。
(3)第2位(CLKSOURCE)是輸入時鍾的選擇位,置1時選擇系統時鍾做為計數脈沖,置0時選擇半系統時鍾做為計數脈沖的參考時鍾。
(4)第3到15位為保留位,不能對它們寫1。
(5)第16位(COUNTFLAG)是溢出標志位,當計數的值遞減到0時,該位被置1,在讀取該值后自動清零。
(6)第17到31位為保留位,不能對它們寫1。

(1)第0到23位(RELOAD)為重載的初值,即當計數器計數到0后重載到計數器中的值,一共24位。
(2)第24到31位為保留位,不能對它們寫1。
SYST_CVR是系統定時器的當前值寄存器,負責獲取SysTick的24位當前計數值,當對該寄存器進行寫操作時,該寄存器的數值將會被清零。下表給出了它的全部位結構,其字節地址為0xE000E018。
(1)第0到23位(CURRENT)為SysTick的當前值,一共24位。
(2)第24到31位為保留位,不能對它們寫1。
SYST_CALIB是系統定時器的校准值寄存器,負責SysTick的校准。下表給出了它的全部位結構,其字節地址為0xE000E01C。
(1)第31位(NOREF)指示處理器是否有基准時鍾,由芯片廠家出廠時決定。該位讀出值為1,指明無獨立的基准時鍾。
(2)第30位(SKEW)指示TENMS值是否精確。由於TENMS不可知,因此10ms不精確計時的校准值不能確定,這會影響SysTick作為軟件實時時鍾的適用性。該位讀出值為1,指明未提供TENMS值。
(3)第24到29位為保留位。
(4)第0到23位(TENMS)在發生系統時鍾扭曲錯誤時,在10ms時序下的校正值。該位讀出值為0,指明校准值未知。
typedef struct
{
__IOM uint32_t CTRL;
__IOM uint32_t LOAD;
__IOM uint32_t VAL;
__IM uint32_t CALIB;
} SysTick_Type;
注意,在上述定義中,結構體成員CTRL對應SYST_CSR寄存器,LOAD對應SYST_RVR寄存器,VAL對應SYST_CVR寄存器,CALIB對應SYST_CALIB寄存器。結構體中並沒有全部采用SysTick中的寄存器名稱。
#define SCS_BASE (0xE000E000UL)
#define SysTick_BASE (SCS_BASE + 0x0010UL)
#define SysTick ((SysTick_Type *) SysTick_BASE )
對於系統定時器SysTick產生的中斷,也有特定的入口函數,形式如下所示。
void SysTick_Handler(void)
{
系統定時中斷服務程序部分
}
(1)給SYST_RVR寄存器寫入定時器的初始值。
(2)寫SYST_CVR寄存器對計數值進行清零。
(3)設置SYST_CSR寄存器選擇相應的時鍾源,啟動定時並使能中斷。
SysTick定時的時長由系統時鍾頻率、系統時鍾選擇和載入的初始值共同決定。假設系統時鍾為24MHz,默認情況下SysTick的CLKSOURCE值為0,即選擇半系統時鍾頻率。這樣,輸入給定時器計數的時鍾就是24÷2=12MHz,計數周期為1/(12MHz)=1/12us,則計12次就是1us的定時,但實際上SysTick采用的是倒數計數方式,即從最大值依次遞減計數直到0產生溢出信號。所以12次計數實際上是0~11次,即最大值要減1,即12-1=11。同時要注意,由於計數位寬是24位,所以最大計數值不能超過2的24次方(即16777216),由此,可得出微秒級的初始值計算公式,如下:
LOAD=((12*us)-1),其中us取值范圍1~1398000
同理可得出毫秒級的初始值計算公式:
LOAD=((12000*ms)-1),其中ms取值范圍1~1398
上升到一般情況,定時初始值可用下面的公式來計算:

void delay_ms(uint32_t ms)
{
SysTick->LOAD = (((12000)*ms)-1); //載入初始值
SysTick->VAL = 0; //寫當前值寄存器使其清零
SysTick->CTRL |= (1<<0); //啟動定時器,選擇半系統時鍾
while(!(SysTick->CTRL & 0x10000)); //循環查詢,等待定時時間到
SysTick->CTRL &= ~(1<<0); //關閉定時器
}
在程序中,通過“while(!(SysTick ->CTRL & 0x10000));”這句來等待定時器溢出,其實就是循環查詢控制寄存器SYST_CSR中的COUNTFLAG位(第16位)是否被置1,若為0則循環等待直到1為止。注意,本函數延時的最大時長為1398ms,即傳遞給它的參數不能超過1398,否則不能實現延時功能。
//************************端口初始化***********************************
void Port_init(void)
{
LPC_GPIO_PORT->DIR0 = 0x1FFFFFFF; //設置端口為輸出方向
LPC_GPIO_PORT->PIN0 = 0x1FFFFFFF; //關閉所有LED
LPC_GPIO_PORT->CLR0 = (1<<7); //點亮第1個LED
}
//************************定時器初始化*********************************
void SysTick_init(void)
{
SysTick->LOAD = (((12000)*100)-1);
SysTick->VAL = 0;
SysTick->CTRL |= ((1<<1)|(1<<0));
}
//***************************主函數************************************
int main(void)
{
Port_init(); //調用端口初始化
SysTick_init(); //調用定時器初始化
while(1)
{
;
}
}
//************************定時器中斷***********************************
void SysTick_Handler(void)
{
static uint8_t i=0;
switch(i++)
{
case 0:
LPC_GPIO_PORT->SET0 = (1<<7); //熄滅第1個LED
LPC_GPIO_PORT->CLR0 = (1<<13); //點亮第2個LED
break;
case 1:
LPC_GPIO_PORT->SET0 = (1<<13); //熄滅第2個LED
LPC_GPIO_PORT->CLR0 = (1<<16); //點亮第3個LED
break;
case 2:
LPC_GPIO_PORT->SET0 = (1<<16); //熄滅第3個LED
LPC_GPIO_PORT->CLR0 = (1<<17); //點亮第4個LED
break;
case 3:
LPC_GPIO_PORT->SET0 = (1<<17); //熄滅第4個LED
LPC_GPIO_PORT->CLR0 = (1<<19); //點亮第5個LED
break;
case 4:
LPC_GPIO_PORT->SET0 = (1<<19); //熄滅第5個LED
LPC_GPIO_PORT->CLR0 = (1<<27); //點亮第6個LED
break;
case 5:
LPC_GPIO_PORT->SET0 = (1<<27); //熄滅第6個LED
LPC_GPIO_PORT->CLR0 = (1<<28); //點亮第7個LED
break;
case 6:
LPC_GPIO_PORT->SET0 = (1<<28); //熄滅第7個LED
LPC_GPIO_PORT->CLR0 = (1<<18); //點亮第8個LED
break;
case 7:
LPC_GPIO_PORT->SET0 = (1<<18); //熄滅第8個LED
LPC_GPIO_PORT->CLR0 = (1<<7); //點亮第1個LED
i = 0; //計數清零
break;
}
}