在第一篇到第九篇博文中,我們認識到了一些基于IO口輸入與輸出的基礎電子器件使用:
《8051單片機實戰分析(以STC89C52RC為例) | 01 - 點亮一個LED》
《8051單片機實戰分析(以STC89C52RC為例) | 02 - LED延時約5s閃爍》
《8051單片機實戰分析(以STC89C52RC為例) | 03 - LED流水燈》
《8051單片機實戰分析(以STC89C52RC為例) | 04 - 蜂鳴器驅動》
《8051單片機實戰分析(以STC89C52RC為例) | 05 - 靜態數碼管驅動》
《8051單片機實戰分析(以STC89C52RC為例) | 06 - 動態數碼管驅動》
《8051單片機實戰分析(以STC89C52RC為例) | 07 - 獨立按鍵驅動》
《8051單片機實戰分析(以STC89C52RC為例) | 08 - 矩陣按鍵驅動》
《8051單片機實戰分析(以STC89C52RC為例) | 09 - LED點陣顯示數字》
但現在我們要開始回到8051單片機內部,通過實戰來認識它們的作業原理,你會發現通過它們可以去開發一些更有意思的東西!
這篇博文帶領大家認識一下STC89C52RC單片機定時器中斷的使用,如果你不了解什么是中斷,建議你先看這篇:
《STC89C52RC單片機額外篇 | 01 - 認識中斷、中斷源以及中斷優先級》
1 中斷系統結構
以下這張圖是從中斷引腳到中斷入口所經過的通道:
從圖中不難看出T0與T1經過了TCON、IE、IP這些暫存器,因此我們在寫程式時得把這些暫存器功能配置好,CPU才會按照我們的想法只執行!下面分別對這些暫存器進行介紹(稍微了解一下即可,忘記的時候再查),
1.1 TCON暫存器
TCON(Timer Control Register),中文叫定時器/計數器控制暫存器,TCON暫存器是用于中斷觸發方式設定以及中斷標志,
各暫存器位的作用如下:
| TCON暫存器位 | 作用 |
|---|---|
TF0(TF1) | 計數溢位標志位,當計數器計數溢位時,該位置1 |
TR0(TR1) | 定時器運行控制位,當TR0(TR1)=0停止定時器/計數器作業;當TR0(TR1)=1啟動定時器/計數器作業 |
IE0(IE1) | 外中斷請求標志位,當CPU采樣到P3.2(P3.3)出現有效中斷請求時,此位由硬體置1,在中斷回應完成后轉向中斷服務時,再由硬體自動清0 |
IT0(IT1) | 外中斷請求信號方式控制位,當IT0(IT1) =1 脈沖方式(后沿負跳有效);當IT0(IT1)=0電平方式(低電平有效)此位由軟體置1或清0 |
TF0(TF1) | 計數溢位標志位,當計數器產生計數溢位時,此位由硬體置1,當轉向中斷服務時,再有硬體自動清0,計數溢位的標志位的使用有兩種情況:采用中斷方式時,作中斷請求標志位來使用;采用查詢方式時,作查詢狀態位來使用 |
1.2 IE暫存器
IE(Interrupt Enable),中文叫中斷允許暫存器,它的作用是控制所有中斷源的開放或禁止,以及每個中斷源是否被允許,

各暫存器位的作用如下:
| IE暫存器位 | 作用 |
|---|---|
EA | EA = 0時,所有中斷禁止(即不產生中斷);EA = 1時,各中斷的產生由個別的允許位決定 |
ES | 串行口RX/TX中斷允許 |
ET1 | 定時器T1中斷允許 |
EX1 | 外中斷INT1中斷允許 |
ET0 | 定時器T0中斷允許 |
EX0 | 外部中斷INT0中斷允許 |
1.3 IP暫存器
IP(Interrupt Priority),中文叫中斷優先級暫存器,它是用來設定各個中斷源屬于兩級中斷中的哪一級,

各暫存器位的作用如下:
| IP暫存器位 | 作用 |
|---|---|
PS | 串行口RX/TX中斷優先 |
PT1 | 定時器T1中斷優先 |
PX1 | 外中斷INT1中斷優先 |
PT0 | 定時器T0中斷優先 |
PX0 | 外部中斷INT0中斷優先 |
2 定時器/計數器結構
以下這張圖是定時器/計數器結構框圖:
從圖中不難如果我們要使用定時器,必須要初始化TH1(TH0)、TL1(TL0)、TCON與TMOD暫存器,對于TCON暫存器,在前面的中斷系統結構中,我們已經學習到了,下面分別對TH1(TH0)、TL1(TL0)與TMOD暫存器的使用進行介紹,
2.1 TH1(TH0)與TL1(TL0)暫存器
TH1(TH0)與TL1(TL0),它們都是定時值存盤暫存器,用于存盤定時器的計數值,其中TH1/TL1 用于 T1,TH0/TL0 用于 T0,以下是它們各個暫存器的作用:
| 定時值存盤暫存器 | 作用 |
|---|---|
TH1 | 定時器T1定時值的高位元組 |
TL1 | 定時器T1定時值的低位元組 |
TH0 | 定時器T0定時值的高位元組 |
TL0 | 定時器T0定時值的低位元組 |
2.2 TMOD暫存器
TMOD(TIMER MODE),中文叫定時器模式暫存器,它用于給定時器設定作業方式,
我們看到TMOD暫存器前面四位與后面四位一模一樣,其實前四位是用于設定定時器T1的模式,后四位是用于設定定時器T0的模式,
各暫存器位的作用如下:
| TMOD暫存器位 | 作用 |
|---|---|
GATE | 該位被置 1 時為門控位,當INTx腳為高電平并且TRx控制位被置1時使能定時器,定時器開始計時;當該位被清0時,只要TRx位被置1,定時器 就使能開始計時,而不受到單片機引腳INTx外部信號的干擾,常用來測量外部信號脈沖寬度 |
C/T | 定時器/計數器選擇位,該位被清零時用作定時器功能(內部系統時鐘),被置 1用作計數器功能 |
上面這部分暫存器一般使用較少,通常不設定,使用復位默認值!下面是TMOD暫存器 M1/M0作業模式:
| M1 | M0 | 作業模式 | 描述 |
|---|---|---|---|
| 0 | 0 | 0 | 13 位定時器/計數器,TH1(TH0)的 8 位和TL1(TL0)的 5 位組成一個 13 位定時器 |
| 0 | 1 | 1 | TH1(TH0)和 TL1(TL0) 組成一個 16 位的定時器/計數器 |
| 1 | 0 | 2 | 8 位自動重裝模式,定時器/計數器溢位后TH1(TH0)重裝到TL1(TL0)中 |
| 1 | 1 | 3 | 禁用定時器/計數器 T1,定時器/計數器T0變成 2 個 8 位定時器/計數器 |
下面列出各個作業模式下的內部作業圖:
① 作業模式0:
② 作業模式1:
③ 作業模式2:
④ 作業模式3:
事實上,對于作業模式的設定,通常我們用得最多的是作業模式1和作業模式2, 為了更好地說明作業模式1和作業模式2的使用,我們先記住一句話:1個機器周期含6個狀態周期,12個振蕩周期,如果使用12M頻率的晶振,那么它的振蕩周期就是1/12(us),即機器周期為1us!那么定時器/計數器每計數1次,則花費1us,
對于作業模式1,我們可以理解以下這個例子:
通過計算N=500,即要定時1ms,需要機器周期運行500次,那么為什么X=65536-500?結合前面的內部作業圖,我們知道溢位(0xFFFF+1=65536)才能TF0位設定為1,由于定時器計數是往上自加的,所以從65026開始自加到65536剛好計數500次,也就是1ms,所以在此處TH0=0xFE,TL0=0x0C,也就是X的前八位與后八位,
對于作業模式2也是,由于只使用TL0進行計數,所以最大計數為256次,自動重裝模式的意思是每次溢位(0xFF+1=256)后,TH0里面存的計數值會直接復制到TL0,因此TH0只存計數值,而不會計數,
3 原理圖
① LED燈:
② 數碼管原理圖:

③ 數碼管的位選使用138譯碼器進行決議,關于這塊我們可以參考這篇文章:《數字器件認識 | 74HC138三八譯碼器的應用》,

④ MCU原理圖:
4 代碼
① 中斷服務函式:
我們知道我們撰寫的C程式,函式的執行是從main主函式開始執行,現在有了中斷,自然就產生一個中斷服務函式:
從圖中我們可以知道單片機在發生中斷的時候,程式的執行程序會從主程式A跳到中斷服務程式B,在執行完中斷服務程式B后,會回傳到之前主程式A被中斷打斷處繼續執行程式,
那么我們如何指定中斷服務程式?具體參考以下模板(對于函式名你可以隨便寫,當然最好貼近有意義的命名),
外部中斷0的中斷服務函式:
void Int0() interrupt 0
{
... // 中斷服務程式中要執行內容
}
定時器0的中斷服務函式:
void Timer0() interrupt 1
{
... // 中斷服務程式中要執行內容
}
外部中斷1的中斷服務函式:
void Int1() interrupt 2
{
... // 中斷服務程式中要執行內容
}
定時器1的中斷服務函式:
void Timer1() interrupt 3
{
... // 中斷服務程式中要執行內容
}
串行口的中斷服務函式:
void Serial() interrupt 4
{
... // 中斷服務程式中要執行內容
}
② 使用單片機內部定時器T0可以實作準確延時,讓LED回圈點亮1秒,熄滅1秒:
#include "reg52.h" //此檔案中定義了單片機的一些特殊功能暫存器
typedef unsigned int u16; //對資料型別進行宣告定義
typedef unsigned char u8;
sbit led=P2^0; //定義P20口是led
/*******************************************************************************
* 函 數 名 : Timer0Init
* 函式功能 : 定時器0初始化
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void Timer0Init()
{
TMOD|=0x01; //選擇為定時器0模式,作業模式1,僅用TR0打開啟動,
TH0=0xFC; //給定時器賦初值,定時1ms
TL0=0x18;
ET0=1; //打開定時器0中斷允許
EA=1; //打開總中斷
TR0=1; //打開定時器
}
/*******************************************************************************
* 函 數 名 : main
* 函式功能 : 主函式
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void main()
{
Timer0Init(); //定時器0初始化
while(1);
}
/*******************************************************************************
* 函 數 名 : void Timer0() interrupt 1
* 函式功能 : 定時器0中斷函式
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void Timer0() interrupt 1
{
static u16 i;
TH0=0xFC; //給定時器賦初值,定時1ms
TL0=0x18;
i++;
if(i==1000)
{
i=0;
led=~led;
}
}
③ 使用單片機內部定時器T1可以實作準確延時,讓數碼管最后一位間隔一秒回圈顯示0-F:
#include "reg52.h" //此檔案中定義了單片機的一些特殊功能暫存器
typedef unsigned int u16; //對資料型別進行宣告定義
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 code LedChar[]={
0x3F, //"0"
0x06, //"1"
0x5B, //"2"
0x4F, //"3"
0x66, //"4"
0x6D, //"5"
0x7D, //"6"
0x07, //"7"
0x7F, //"8"
0x6F, //"9"
0x77, //"A"
0x7C, //"B"
0x39, //"C"
0x5E, //"D"
0x79, //"E"
0x71, //"F"
0xff, //全亮
0x00 //熄滅
};
u8 n=0;
/*******************************************************************************
* 函 數 名 : Timer1Init
* 函式功能 : 定時器1初始化
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void Timer1Init()
{
TMOD|=0X10; //選擇為定時器1模式,作業模式1,僅用TR1打開啟動,
TH1=0xFC; //給定時器賦初值,定時1ms
TL1=0x18;
ET1=1; //打開定時器1中斷允許
EA=1; //打開總中斷
TR1=1; //打開定時器
}
/*******************************************************************************
* 函 數 名 : main
* 函式功能 : 主函式
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void main()
{
LSA=0;
LSB=0;
LSC=0;
Timer1Init(); //定時器1初始化
while(1);
}
/*******************************************************************************
* 函 數 名 : void Timer1() interrupt 3
* 函式功能 : 定時器0中斷函式
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void Timer1() interrupt 3
{
static u16 i;
TH1=0xFC; //給定時器賦初值,定時1ms
TL1=0x18;
i++;
if(i==1000)
{
i=0;
P0=LedChar[n++];
if(n==16)n=0;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286473.html
標籤:其他
上一篇:基于韌體的漏洞挖掘方法梳理
下一篇:在物聯網中解讀投資回報(一)
