文章目錄
- 前言
- 定時器是什么
- 初始化定時器
- 初始化的大概步驟
- TMOD寄存器
- C/T寄存器
- 觸發定時器
- 中斷是什么
- 中斷函數
- 定時器點亮led
- 總結
前言
在嵌入式系統的開發中,定時器是一個非常重要的組成部分。它們可以用于產生精確的時間延遲,或者在特定的時間間隔內觸發某些事件。在51單片機中,定時器的功能尤為強大,可以滿足各種復雜的時間控制需求。本文將介紹51單片機的定時器,包括它們是什么,以及如何觸發它們。
定時器是什么
定時器是單片機內部的一個重要模塊,它可以按照預設的時間間隔產生一個中斷。在51單片機中,有兩個定時器,分別是定時器0和定時器1。每個定時器都有一個16位的計數器,可以被配置為四種不同的工作模式,以滿足不同的應用需求。
初始化定時器
初始化的大概步驟
要觸發定時器,我們需要進行以下步驟:
-
設置定時器模式:通過TMOD寄存器來設置定時器的工作模式。例如,如果我們想將定時器0設置為模式1(16位定時/計數器模式),我們可以寫入
TMOD = 0x01;
。 -
加載定時初值:定時初值決定了定時器中斷的觸發頻率。我們可以通過TH0和TL0寄存器來設置定時器0的初值。例如,我們可以寫入
TH0 = 0xFC; TL0 = 0x18;
來設置定時初值,他就會以這個數開始–。在51單片機的定時器中,TH0是高8位,TL0是低8位。這兩個寄存器共同組成了一個16位的定時/計數器。當我們說"設置定時器的初值"時,通常是指同時設置這兩個寄存器的值。 -
開啟定時器中斷:通過IE寄存器來開啟定時器的中斷。例如,我們可以寫入
ET0 = 1; EA = 1;
來開啟定時器0的中斷。 -
啟動定時器:通過TR0位來啟動定時器0。例如,我們可以寫入
TR0 = 1;
來啟動定時器。
在帶有XX0中,0就是定時器編號,也可以寫1,因為有兩個定時器
初始化代碼:
void Timer0_Init(void) {TMOD = 0x01; // 設置定時器0為模式1TH0 = 0xFC; // 設置定時初值TL0 = 0x18; // 設置定時初值ET0 = 1; // 開啟定時器0中斷EA = 1; // 開啟總中斷TR0 = 1; // 啟動定時器0
}
TMOD寄存器
TMOD寄存器用于設置51單片機的定時器模式。它是一個8位的寄存器,分為兩個部分:高4位用于設置定時器1,低4位用于設置定時器0。
每個定時器的設置都包括兩個部分:工作模式(M1和M0)和工作方式(GATE和C/T)。以下是每個位的詳細解釋:
-
M1和M0:這兩個位用于設置定時器的工作模式。有四種模式可供選擇:
- 模式0(M1=0, M0=0):13位定時/計數器模式。在這種模式下,定時器有12位的計數器(由TL和TH的低4位組成),和一個5位的預分頻器(由TH的高4位組成)。
- 模式1(M1=0, M0=1):16位定時/計數器模式。在這種模式下,定時器有16位的計數器(由TL和TH組成)。
- 模式2(M1=1, M0=0):8位自動重裝定時/計數器模式。在這種模式下,定時器有8位的計數器(由TL組成),并且在溢出時會自動重裝TH的值。
- 模式3(M1=1, M0=1):只對定時器0有效。在這種模式下,定時器0被分為兩個8位的定時/計數器(由TL0和TH0組成)。
-
GATE:當GATE位被設置為1時,定時器/計數器只有在對應的INTx引腳(P3.2對于定時器0,P3.3對于定時器1)為高電平時才會運行。當GATE位被設置為0時,定時器/計數器會忽略INTx引腳的狀態。
C/T寄存器
- C/T:當C/T位被設置為1時,定時器會作為計數器使用,計數外部的脈沖信號。當C/T位被設置為0時,定時器會作為定時器使用,計數機器周期。
好的,讓我通過一些例子來解釋這個概念。
首先,我們來看當C/T位被設置為0時,定時器作為定時器使用的情況。在這種情況下,定時器會計數機器周期。例如,如果我們的單片機的時鐘頻率是12MHz,那么每個機器周期的時間就是1/12μs。是的,你的理解是正確的。在數字電路中,頻率(單位為赫茲,Hz)是指在一秒鐘內的周期數。所以,如果一個系統的頻率是x MHz(兆赫茲,即百萬赫茲),那么它在一秒鐘內會有x百萬個周期。
因此,每個周期的時間(也就是周期時間或者說是時鐘周期)就是一秒鐘的時間除以周期的數量。所以,對于x MHz的頻率,每個周期的時間就是1/x μs(微秒,即百萬分之一秒)。
用數學公式表示,就是:
T = 1 f T = \frac{1}{f} T=f1?
其中,T是周期時間,f是頻率。如果f的單位是MHz,那么T的單位就是μs。
如果我們設置定時器的初值為0xFFFF,那么定時器會在每個機器周期增加1,直到計數值溢出(達到0),這時就會觸發一個中斷。這就是我們通常說的定時器溢出,也是定時器最常見的用途。
然后,我們來看當C/T位被設置為1時,定時器作為計數器使用的情況。在這種情況下,定時器會計數外部的脈沖信號。例如,我們可以將一個按鈕連接到定時器的計數輸入端口(對于定時器0,這個端口是P3.4)。當我們按下按鈕時,就會在這個端口產生一個脈沖信號。定時器會在每次檢測到脈沖信號時增加1,直到計數值溢出(達到0),這時就會觸發一個中斷。這就是我們通常說的計數器溢出,也是計數器的一種常見用途。
當C/T位被設置為1時,定時器會作為計數器使用,計數外部的脈沖信號。在這種情況下,TH0和TL0的值通常被設置為0,因為我們希望從0開始計數外部的脈沖信號。
觸發定時器
當定時器計數溢出時,就會觸發一個中斷,我們可以在中斷服務程序中編寫需要定時執行的代碼。
中斷是什么
中斷,顧名思義,就是打斷當前正在執行的任務,去做一些更重要的事情。你可以把它想象成你正在看電視,突然有人按門鈴,你就需要"中斷"你正在做的事情,去開門。
在計算機中,中斷的概念也是類似的。當計算機正在執行一個程序時,如果發生了一個中斷,計算機就會暫時停止執行當前的程序,去處理這個中斷。處理完中斷后,計算機會回到被中斷的地方,繼續執行原來的程序。
中斷可以由很多事情觸發,比如輸入/輸出設備(如鍵盤、鼠標)的操作,定時器的超時,甚至是軟件的請求。處理中斷的程序叫做中斷服務程序或者中斷處理程序。
中斷是計算機實現并發操作的一種重要機制。通過中斷,計算機可以在處理一個任務的同時,響應其他的事件。這使得計算機可以更有效地利用資源,提高工作效率。
我們的定時器也是調用中斷的
在C語言中,中斷運行的代碼使用函數表示,中斷發生后,會去運行中斷中的代碼
中斷函數
中斷函數相對我們的C語言函數,在參數列表后面加interrupt x
即可,x為定時器編號,他從1開始
void Timer0_ISR(void) interrupt 1 {TH0 = 0xFC; TL0 = 0x18; //。。。。
}
在中斷函數中需要重新裝載THx
與TLx
,其中x為定時器編號
在51單片機的定時器中,"重新裝載"是一個非常重要的概念。當我們說"重新裝載"時,我們是指將定時器的計數值重新設置為它的初值。
那么,為什么我們需要重新裝載呢?這主要是因為定時器的工作方式。當定時器開始運行時,它會從初值開始計數,每個機器周期增加1。當計數值達到最大值(對于16位定時器,這個值是0xFFFF)時,定時器就會溢出,并觸發一個中斷。
然而,如果我們在中斷服務程序中不重新裝載定時器的初值,那么定時器在下一個機器周期就會從0開始計數。這意味著定時器會立即再次溢出,并立即再次觸發中斷。這樣,我們的中斷服務程序就會被連續調用兩次,而且這個過程會一直重復,導致我們的程序無法正常運行。
通過重新裝載定時器的初值,我們可以控制定時器中斷的觸發頻率。例如,如果我們希望定時器每1ms觸發一次中斷,我們就可以將定時器的初值設置為一個特定的值,使得定時器在1ms后溢出。然后,在每次中斷服務程序被調用時,我們就重新裝載這個初值,這樣定時器就會在下一個1ms后再次溢出,再次觸發中斷。
定時器點亮led
當我們設置TH0
和TL0
為0xFC
與0x18
時,他們移除需要加下面數量:
當計數這么多時,就溢出觸發中斷
在51單片機中,定時器0的工作方式默認為模式1,也就是16位定時/計數模式。當TH0和TL0分別設置為0xFC和0x18時,定時器0的初始值為0xFC18。由于定時器0是16位的,所以它的最大值為0xFFFF。當定時器0從0xFC18開始計數,每次增加1,直到達到0xFFFF,然后溢出并回到0x0000,這個過程就會產生一個中斷。
在一個12MHz的系統時鐘下,每個機器周期為1/12μs。在定時器模式下,定時器每過12個機器周期就會增加1。因此,從0xFC18增加到0xFFFF所需的時間可以通過以下公式計算:
T = ( 0 x F F F F ? 0 x F C 18 + 1 ) × 12 × 1 12 M H z T = (0xFFFF - 0xFC18 + 1) \times 12 \times \frac{1}{12MHz} T=(0xFFFF?0xFC18+1)×12×12MHz1?
為什么需要+1?
這是因為計數是從0開始的。當我們從0xFC18計數到0xFFFF時,我們實際上是包括了0xFFFF這個數值的。所以,我們需要計算的是從0xFC18到0xFFFF的所有數值,包括這兩個數值本身,總共有(0xFFFF - 0xFC18 + 1)個數值。
計算結果,我們可以得到定時器溢出并產生中斷的時間。這就是當TH0和TL0設置為0xFC和0x18時,12MHz系統時鐘下,溢出調用中斷的時間。
#include <reg51.h>sbit led = P1^0;void Timer0_Init(void) {TMOD = 0x01; // ?????0???1TH0 = 0xFC; // ??????TL0 = 0x18; // ??????ET0 = 1; // ?????0??EA = 1; // ?????TR0 = 1; // ?????0
}void Timer0_ISR(void) interrupt 1 {static unsigned short x = 0;TH0 = 0xFC; TL0 = 0x18; if( x == 0){led = !led;}x++;x%= 500;
}void delay(unsigned int ms) {unsigned int i, j;for(i = ms; i > 0; i--)for(j = 120; j > 0; j--);
}void main()
{Timer0_Init();while(1){}
}
總結
定時器是51單片機中非常強大的一個功能,它為我們提供了一種精確控制時間的方法。通過合理地設置定時器模式和定時初值,我們可以實現各種復雜的時間控制需求。希望通過本文的介紹,你對51單片機的定時器有了更深入的理解。在未來的學習和開發中,你可以嘗試利用定時器來實現更多有趣和實用的功能。祝你學習愉快!