單片機學習(六)定時器的使用
參考資料:
一、定時器簡介
- 定時器介紹:
51單片機的定時器屬於單片機的內部資源,其電路的連接和運轉均在單片機內部完成 - 定時器作用:
- (1) 用於計時系統,可實現軟件計時,或者使程序每隔一固定時間完成一項操作
- (2)替代長時間的Delay, 提高CPU的運行效率和處理速度
- …
二、STC89C52定時器資源
- 定時器個數: 3個 (
T0
、T1
、T2
),T0和T1與傳統的51單片機兼容,
T2是此型號單片機增加的資源 - 注意:定時器的資源和單片機的型號是關聯在一起的,不同的型號可能會有不同的定時器個數和操作方式,但一般來說,T0和T1的操作方式是所有51單片機所共有的
三、定時器工作原理
定時器在單片機內部就像一個小鬧鐘一樣,根據時鐘的輸出信號,每隔「一秒”(一個單位時間),計數單元的數值就增加一,當計數單元數值增加到「設定的鬧鐘提醒時間”時,計數單元就會向中斷系統發出中斷申請,產生”響鈴提醒”,使程序跳轉到中斷服務函數中執行。
在實現細節上:時鐘部分每隔單位時間就會傳遞一個脈衝信號到計數部分,而計數部分會對脈衝個數進行計數,當脈衝個數達到TH0,TL0
能存儲的最大數量後(即發生溢出,這裡一共有16位,即只能表示0~65535這些數字,繼續接收信號即溢出),TF0
將會被置一,此時就會發出中斷申請,然後程序跳轉到中斷服務程序中執行。
1. 時鐘信號的提供
可以看到,在時鐘部分有兩個輸入來源:
- 系統時鐘
T0
引腳
當我們使用T0
引腳時往往是將這個模塊作為計數器記錄脈衝的個數的,而不是用作定時器,故這裡我們一般只考慮系統時鐘的信號輸入。
SYSclk
:系統時鐘,即晶振周期,本開發板上的晶振為12MHz
當 C/T=0
時選擇的是系統時鐘作為輸入,而 C/T=1
時選擇的是T0
引腳作為輸入,故我們需要選擇第一種。
2. 中斷系統簡介
中斷系統是為使CPU具有對外界緊急事件的實時處理能力而設置的。
中斷的概念
當中央處理機CPU正在處理某件事的時候外界發生了緊急事件請求,要求CPU暫停當前的工作,轉而去處理這個緊急事件,處理完以後,再回到原來被中斷的地方,繼續原來的工作,這樣的過程稱為中斷。
中斷的優先級
實現這種功能的部件稱為中斷系統,請示CPU中斷的請求源稱為中斷源。微型機的中斷系統一般允許多個中斷源,當幾個中斷源同時向CPU請求中斷,要求為它服務的時候,這就存在CPU優先響應哪一個中斷源請求的問題。通常根據中斷源的輕重緩急排隊,優先處理最緊急事件的中斷請求源,即規定每一個中斷源有一個優先級別。CPU總是先響應優先級別最高的中斷請求。
中斷的嵌套
當CPU正在處理一個中斷源請求的時候( 執行相應的中斷服務程序),發生了另外一個優先級比它還高的中斷源請求。如果CPU能夠
- 暫停對原來中斷源的服務程序
- 轉而去處理優先級更高的中斷請求源,處理完該請求
- 再回到原低級中斷服務程序
這樣的過程稱為中斷嵌套,這樣的中斷系統稱為多級中斷系統
沒有中斷嵌套功能的中斷系統稱為單級中斷系統。
中斷流程圖
STC89C52的中斷資源
-
中斷源個數: 8個
- 外部中斷0
- 定時器0中斷
- 外部中斷1
- 定時器1中斷
- 串口中斷
- 定時器2中斷
- 外部中斷2
- 外部中斷3
-
中斷優先級個數: 4個
-
中斷號
3. 相關的寄存器
TCON寄存器
TF和TR
當計數器溢出時TF0
會被置一,TR0
用於控制T0
計時器是否計數。
TMOD寄存器
TMOD寄存器主要是用於配置我們的定時器的功能。
GATE
C/T
M1、M0
總圖
4. 定時器程序基本配置圖
四、編碼實現
初始化代碼
1. 配置TMOD寄存器
首先我們希望使用定時器0,我們需要配置:
GATE = 0
C/T = 0
M1,M0 = 01
所以我們可以使用與或賦值法進行賦值:
TMOD &= 0xF0; // 1111 0000
TMOD |= 0x01; // 0000 0001
tips:TMOD寄存器是不可位尋址的,故我們只能通過整體賦值的方式進行配置
2. 配置TCON寄存器
我們需要啟用T0
計數器,並給它的溢出標誌位TF0
置零:
TF0 = 0;
TR0 = 1;
3. 配置計數寄存器的初始值
計數器的計數範圍:0~65535
大約每隔1us計數加一,故其總共定時時間為65535us
而我們希望計時1ms即發生中斷,則我們可以將計數寄存器初始值為64536,因為64536離計數器溢出差值1000,所以當計時時間為1ms時即可觸發中斷。
而計數寄存器由兩個寄存器拼接而成,則我們可以分高低位賦值:
TH0 = 64536 / 0x0100; // 高八位
TL0 = 64536 % 0x0100; // 低八位
4. 配置中斷通路
即按照上一部分的第4點的圖進行配置即可:
ET0 = 1;
EA = 1;
PT0 = 0;
5. 總的配置代碼
void Timer0Init(void)
{
// 1.配置TMOD寄存器
TMOD &= 0xF0; // 1111 0000
TMOD |= 0x01; // 0000 0001
// 2.配置TCON寄存器
TF0 = 0;
TR0 = 1;
// 3.配置計數寄存器的初始值
TH0 = 64536 / 0x0100;
TL0 = 64536 % 0x0100;
// 4.配置中斷通路
ET0 = 1;
EA = 1;
PT0 = 0;
}
中斷程序代碼
語法:相當於在函數後面加上 interrupt
關鍵字:
void 中斷程序名() interrupt 中斷號 {
...
}
其中中斷號需要和中斷源相對應。
使用STC-ISP自帶的定時器計算器
打開STC-ISP燒錄工具,並點擊【定時器計算器】的tab:
配置如下:
然後我們即可獲得相應的初始化代碼了!
注意:在我們當前版本的單片機中是沒有AUXR這個寄存器的,我們不需要配置此項(刪除即可),然後我們還需要加入中斷的配置,即【配置中斷通路】部分的三行代碼。
使用自動生成的代碼:
void Timer0Init(void) //1毫秒@12.000MHz
{
TMOD &= 0xF0; //設置定時器模式
TMOD |= 0x01; //設置定時器模式
TL0 = 0x18; //設置定時初值
TH0 = 0xFC; //設置定時初值
TF0 = 0; //清除TF0標誌
TR0 = 1; //定時器0開始計時
// 配置中斷通路
ET0 = 1;
EA = 1;
PT0 = 0;
}
unsigned int T0Count = 0;
void timer0_routine() interrupt 1 {
T0Count++;
TL0 = 0x18; //設置定時初值
TH0 = 0xFC; //設置定時初值
if (T0Count == 1000) {
T0Count = 0;
P2_0 =! P2_0;
}
}
五、將計時器封裝成模塊
新建Timer0.h
和Timer0.c
文件:
// Timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0Init(void);
#endif // __TIMER0_H__
#include <Atmel/REGX52.H>
#include "Timer0.h"
void Timer0Init(void) //1毫秒@12.000MHz
{
TMOD &= 0xF0; //設置定時器模式
TMOD |= 0x01; //設置定時器模式
TL0 = 0x18; //設置定時初值
TH0 = 0xFC; //設置定時初值
TF0 = 0; //清除TF0標誌
TR0 = 1; //定時器0開始計時
// 配置中斷通路
ET0 = 1;
EA = 1;
PT0 = 0;
}
這樣我們只需要在主函數中使用Timer0Init()
,我們即可在固定的時間內調用中斷程序了。