51單片機中斷系統(tǒng)程序實例 (STC89C52RC)
51單片機有了中斷,在程序設計中就可以做到,在做某件事的過程中,停下來先去響應中斷,做別的事情,做好別的事情再繼續(xù)原來的事情。中斷優(yōu)先級是可以給要做的事情排序。
單片機的學習不難,只要掌握學習方法,學起來并不難。什么是好的學習方法呢,一定要掌握二個要點:
1. 要知道寄存器的英文全拼,比如IE = interrupt中斷
不知道全拼,要去猜,去查。這樣就可以理解為什么是這個名稱,理解了以后就不用記憶了。
2. 每個知識點要有形像的出處
比如看到TF0,腦子里馬上要形像地定位到TCON寄存器的某位
看到ET0, 馬上要形像地定位到IE寄存器的第2位
51hei獨家揭秘: 形像是記憶的最大技巧。當人眼看到某個圖時,是把視覺信號轉化成電信號,再轉化成人能理解的形像。當我們回憶形像時,就是在重新檢索原先那個視覺信號,并放大。在學習過程中,不斷練習檢索、放大信號,我們的學習能力就會越來越強。
寫程序代碼時,也要把盡量把每行代碼形像化。
51單片機內中斷源
8051有五個中斷源,有兩個優(yōu)先級。與中斷系統(tǒng)有關的特殊功能寄存器有IE(中斷允許寄存器)、IP(中斷優(yōu)先級控制寄存器)、中斷源控制寄存器(如TCON、SCON的有關位)。51單片機的中斷系統(tǒng)結構如下圖(注意,IF0應為TF0):
8052有6個中斷源,它比8051多一個定時器/計數(shù)器T2中斷源。
8051五個中斷源分別是:
(1)51單片機外部中斷源
8051有兩個外部中斷源,分別是INT0和INT1,分別從P3.2和P3.3兩個引腳引入中斷請求信號,兩個中斷源的中斷觸發(fā)允許由TCON的低4位控制,TCON的高4位控制運行和溢出標志。
INT0 也就是Interrupt 0。在這里應該看一下你的51單片機開發(fā)板的電路原理圖。離開形像的記憶是沒有意義的。讀到上面這句,你應該回憶起原理圖上的連接。任何記憶都轉化為形 像,這是學習的根本原理,我們通過學習單片機要學會這種學習方法,會讓你一輩子受益無窮。
TCON的結構如下圖:
(a)定時器T0的運行控制位TR0
TR0由軟件置位或者清0。當門控位GATE=0時,TO計數(shù)器僅由TR0控制,TR0=1啟動計數(shù),TR0=0時停止。當門控位GATE=1時,T0計數(shù)器由INT0和TR0共同控制,當INT0=1且TR0=1時啟動T0計數(shù)器。
(b)定時器T0溢出標志位TF0
當T0溢出時TF0=1,并向CPU申請中斷,CPU響應中斷后由硬件將TF0清0,也可以由軟件查詢方式將TF0清0。
c)定時器T1的運行控制位TR1
功能同TR0。
(d)定時器T1溢出標志為TF1
功能同TF1。
(e)外部中斷源1(INT1、P3.3)中斷請求標志IE1
IE1=1時外部中斷源1正在向CPU請求中斷,當CPU響應該中斷時由硬件將IE1清0(下降沿觸發(fā)方式)。
(f)外部中斷源1觸發(fā)方式選擇位IT1
IT1=0時外部中斷源1選擇電平觸發(fā)方式,當輸入低電平時置位IE1;IT1=1時外部中斷源1選擇下降沿觸發(fā)方式,當中斷源由高電平變低電平時置位 IE1,向CPU請求中斷。
(g)外部中斷源0(INT0、P3.2)中斷請求標志IE0
功能類同IE1。
(h)外部中斷源0觸發(fā)方式選擇位IT0
功能類同IT1。
CPU在每個機器周期采樣INT0和INT1引腳的輸入電平。
i、電平觸發(fā)方式
當CPU采樣到低電平時,置位IE0和IE1,采樣到高電平時,將IE0和IE1清零。在電平觸發(fā)方式下,外部中斷源必須一直保持低電平(至少保持1個以 上的機器周期)直到CPU響應中斷請求,否則中斷請求將丟失,同時在中斷處理程序結束之前必須,外部中斷源必須變?yōu)楦唠娖剑駝t將產(chǎn)生另一次中斷。
ii、下降沿觸發(fā)方式
CPU 每個機器周期采樣中斷輸入引腳,如果相續(xù)的兩次采樣,第一次是高電平,第二次是低電平,則置位相應的IE,響應中斷后,硬件自動將IE清0。采樣下降沿觸 發(fā)方式,中斷源的高、低電平都必須保持12個振蕩周期(即1個機器周期)以上,這樣CPU才能有效檢測到下降沿,并引發(fā)CPU中斷。
(2)51單片機內部中斷源
8051有3個內部中斷源,分別是定時器T0、T1和串行口中斷。8052增加了一個T2定時器中斷。
2、51單片機中斷使能控制
中斷的允許和禁止由中斷使能控制寄存器IE控制,其字節(jié)地址為0A8H,可以位尋址,其結構如下圖所示:
EX0:外部中斷0中斷允許位;
ET0:定時器/計數(shù)器T0中斷允許位;
EX1:外部中斷1中斷允許位;
ET1:定時器/計數(shù)器T1中斷允許位;
ES:串行口中斷允許位;
ET2:定時器/計數(shù)器T2中斷允許位;(只要8052具有)
EA:CPU中斷總允許位,EA=1時所有的中斷開放,EA=0時禁止所有的中斷。
3、51單片機中斷優(yōu)先級
51有兩個優(yōu)先級:高、低。通過IP(中斷優(yōu)先級寄存器)來設置優(yōu)先級,其字節(jié)地址為0B8H,可位尋址,其結構如下圖:
IP中各位值為0時表示低優(yōu)先級中斷,為1時表示高優(yōu)先級中斷。CPU復位后IP=0。
高優(yōu)先級中斷可以中斷低優(yōu)先級中斷,同優(yōu)先級中斷不能相互中斷。當CPU同時接到同優(yōu)先級的幾個中斷請求時,CPU按照如下硬件順序進行中斷響應:
4、51單片機中斷請求的撤除
CPU響應中斷請求,執(zhí)行中斷服務程序,但在中斷返回指令(RETI)之前必須撤除中斷信號,否則將可能再次引起中斷而發(fā)生錯誤。
中斷請求撤銷的方法有三種:
a、單片機內部硬件自動復位:對于定時器/計數(shù)器T0、T1及采用邊沿觸發(fā)方式的外部中斷請求,CPU在響應中斷后,由內部硬件自動撤銷中斷請求;
b、應用軟件清除響應標志:對串口發(fā)送/接收中斷請求及定時器T2的溢出和捕獲中斷請求,CPU響應中斷后,內部無硬件自動復位RI、TI、TF2及EXF2,必須在中斷服務程序中清除這些標志,才能撤除中斷;
c、既無軟件清除也無硬件撤除:對于采用電平方式的外部中斷請求,CPU對引腳上的中斷請求信號既無控制能力,也無應答信號,為保障CPU響應中斷請求中斷后,執(zhí)行返回指令前撤除中斷請求,必須考慮另外的措施。
5、51單片機中斷響應過程
51 單片機在每個機器周期的S5P2狀態(tài)順序檢查每個中斷源的中斷請求標志,若有中斷源發(fā)送中斷請求,CPU在下個機器周期的S5P2狀態(tài)按優(yōu)先級順序查詢各 中斷標志,并且取高優(yōu)先級的中斷進行響應。響應中斷后置位相應的中斷優(yōu)先級狀態(tài)觸發(fā)器,標明當前中斷服務的優(yōu)先級別,執(zhí)行硬件調用程序,將程序計數(shù)器PC 的內容壓入堆棧進行保護。對于中斷源的中斷入口地址裝入程序計數(shù)器PC,使程序轉入該中斷入口處執(zhí)行中斷服務程序,直到遇到RETI指令。執(zhí)行RETI指 令,撤銷中斷優(yōu)先級觸發(fā)器,彈出斷點地址至程序計數(shù)器PC,繼續(xù)源程序的執(zhí)行過程。
在接收中斷申請時,如遇到下列情況之一,硬件調用子程序將被封鎖:
a、正在執(zhí)行同級或高一級的中斷服務程序;
b、當前指令周期不是該指令的最后一個周期(或一條指令未執(zhí)行完);
c、當前正在執(zhí)行的指令是RETI或對IE、IP的讀寫操作。
6、中斷入口地址
各中斷源的中斷入口地址為:
STC86C52RC 51單片機中斷示例程序
#include <reg52.h>
typedef unsigned char uint8;
typedef unsigned int uint16;
typedef unsigned long uint32;
sbit enableG1 = P1^3; sbit enableG2 = P1^4;
sbit selectC = P1^2; sbit selectB = P1^1; sbit selectA = P1^0;
code uint16 num16[16] = { 0xC0, 0xF9, 0xA4, 0xB0,
0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83,
0xC6, 0xA1, 0x86, 0x8E };//共陽數(shù)碼管真極表
uint8 num6[6] = {0};//儲存秒,0-5對應于個位...10萬位上各位上的值
void enable138(void); //啟用138譯碼器切換IO口
void refresh_led(void);
void thtl_init(void);
void timer1_init(void);
void et1_init(void);
void main(void)
{
enable138();
timer1_init();
et1_init();
while(1);
}
void interrupt_timer1(void) interrupt 3
{
static uint16 counter = 0;
static uint32 sec = 0;
counter++;
thtl_init();
if(counter == 1000)
{
counter = 0;
sec++;
num6[0] = sec % 10;
num6[1] = sec/10%10;
num6[2] = sec/100%10;
num6[3] = sec/1000%10;
num6[4] = sec/10000%10;
num6[5] = sec/100000%10;
}
refresh_led();//更新num6數(shù)組后再刷新數(shù)碼管
}
void enable138(void) { enableG1 = 1; enableG2 = 0; }
//刷新數(shù)碼管,只顯示有效值
void refresh_led(void)
{
static uint8 i = 0;
switch(i)
{
case 0: selectC = 0; selectB = 0; selectA = 0; P0 = num16[ num6[0] ]; break;
case 1: selectC = 0; selectB = 0; selectA = 1; P0 = num6[5] == 0 && num6[4] == 0 && num6[3] == 0 && num6[2] == 0 && num6[1] == 0 ? 0xFF : num16[ num6[1] ]; break;
case 2: selectC = 0; selectB = 1; selectA = 0; P0 = num6[5] == 0 && num6[4] == 0 && num6[3] == 0 && num6[2] == 0 ? 0xFF : num16[ num6[2] ]; break;
case 3: selectC = 0; selectB = 1; selectA = 1; P0 = num6[5] == 0 && num6[4] == 0 && num6[3] == 0 ? 0xFF : num16[ num6[3] ]; break;
case 4: selectC = 1; selectB = 0; selectA = 0; P0 = num6[5] == 0 && num6[4] == 0 ? 0xFF : num16[ num6[4] ]; break;
case 5: selectC = 1; selectB = 0; selectA = 1; P0 = num6[5] == 0 ? 0xFF : num16[ num6[5] ]; break;
default: break;
}
i = ++i % 6;
}
//設置計數(shù)器初數(shù)值,重用的內容都應該寫成獨立函數(shù)出來方便維護
void thtl_init(void)
{
TH1 = (65536 - 922) / 256;
TL1 = (65536 - 922) % 256;
}
void timer1_init(void)
{
TMOD |= 0X10;
TMOD &= 0xDF;
thtl_init();
TR1 = 1;
}
void et1_init(void) { ET1 = 1; EA = 1; }
|