中斷使得高低速設備可以協調工作(低速設備完成工作后通過中斷的方式通知高速設備一次處理一批數據),中斷還可以根據不同的優先級實現嵌套執行。
定時器本質上是個 16 位的自增計數器,當發生溢出時,如果開啟了溢出中斷,單片機會自動向 CPU 報告這個溢出中斷,處理相應的中斷任務。
寄存器
TCON 定時器控制寄存器
TCON 的低 4 位用作外部中斷,高 4 位用作定時控制。地址是 88H。
TCON 定時器控制寄存器各個位的意義如下:
所在位 bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
名稱 | TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
- IT0:外部中斷0觸發方式。1為低電平觸發,0為下降沿觸發。
- IE0:外部中斷0請求標志位。IE0=1 時表示有中斷請求,0則沒有。
- IT1:外部中斷1觸發方式。1為低電平,0為下降沿信號。
- IE1:外部中斷1請求標志位。IE0=1 時表示有中斷請求,0則沒有。
- TR0:定時器/計數器0啟動停止控制位。1為啟動,0為停止。
- TF0:定時器/計數器0溢出標志位。1表示發生溢出,如果開啟了中斷,則會觸發中斷。
- TR1:定時器/計數器1啟動停止控制位。1為啟動,0為停止。
- TF1:定時器/計數器1溢出標志位。1表示發生溢出,如果開啟了中斷,則會觸發中斷。
IE 中斷允許控制寄存器
CPU 對中斷源的開啟或屏蔽的控制,是通過 IE 寄存器來設置的,IE 既可按字節地址尋址(其字節地址為 A8H),又可按位尋址。某個中斷對應的位設為 1 則表示允許中斷,否則禁止。
IE 寄存器各個位的意義
IE 中斷允許控制寄存器各個位的意義如下:
所在位 bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
名稱 | EA | - | ET2 | ES | ET1 | EX1 | ET0 | EX0 |
- EX0:外部中斷 0 中斷允許位
- ET0:定時器/計數器 0 中斷允許位
- EX1:外部中斷 1 中斷允許位
- ET1:定時器/計數器 1 中斷允許位
- ES:串口中斷允許位
- ET2:定時器/計數器 2 中斷允許位(進行52系列)
- EA:中斷總開關
定時器工作在中斷方式時,當定時器的值計滿溢出時,會觸發定時器溢出中斷。
C 語言示例
只要想使用中斷,就必須開啟 EA 總中斷。例如,如果想使用定時器/計數器0,需要添加下面一段 Keil C51 代碼來開啟 EA 和 ET0:
EA = 1; // 開啟總中斷
ET0 = 1; // 開啟定時器/計數器0 中斷
或者使用字節操作:
IE |= 0x82; // 設置 IE 寄存器為 10000010,即開啟總中斷和定時器/計數器0中斷
匯編語言示例
如果使用匯編語言,開啟外部中斷 0 的匯編代碼,字節操作為:
MOV IE,#81H
;MOV 0A8H,#81H; 這里也可以直接使用 IE 寄存器的地址 A8H
或者使用匯編語言的位操作:
SETB EA
SETB EX0
TMOD 定時器工作模式寄存器
TMOD 用於控制定時器的工作模式,低4位用於 T0,高4位用於 T1。各個位的意義如下:
所在位 bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
名稱 | GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 |
- M0 與 M1:共 4 中組合,對應定時器的 4 中工作模式
- M1 = 0,M0 = 0:模式0,13 位,最大計數范圍 8192。TL 的低 5 位和 TH 的高 8 位組成 13 位計數器,用於兼容 48 系列一般不用。
- M1 = 0,M0 = 1:模式1,16 位,最大計數范圍 65536
- M1 = 1,M0 = 0:模式2,8 位,最大計數范圍 256。高 8 位放預置數,只有低 8 位參與計數。計數溢出后可以自動重新裝填預置數,定時精度高。可以用於波特率發生器等精確計時場合。
- M1 = 1,M0 = 1:模式3,8 位,最大計數范圍 256。此時 T0 被拆成 2 個獨立的定時/計數器。其中 TL0 可以用作 8 位的定時/計數器,TH0 只能用於定時器。TH0 的控制及溢出標志借用 T1 的。一般僅當 T1 工作在模式 2 時,才會讓 T0 工作在模式 3。
- C/T:設置為 0 則作為定時器使用,設置為 1 則成為計數器
- GATE:計數脈沖與定時/計數器之間的開關。
- GATE = 0 時,開關僅由 TR0 控制,TR0 = 1 時計數脈沖可以通過,否則無法通過
- GATE = 1 時,開關由 TR0 和 INT0 同時控制,僅在 TR0 = 1 且 INT0 高電平時,計數脈沖才可以通過
IP 中斷優先級控制寄存器
所在位 bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
名稱 | - | - | PT2 | PS | PT1 | PX1 | PT0 | PX0 |
SCON 串行口控制寄存器
所在位 bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
名稱 | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
- SM0 和 SM1:串行口方式選擇
SM0 | SM1 | 方式 | 說明 | 波特率 |
---|---|---|---|---|
0 | 0 | 0 | 8位數據發送 | fosc/12 |
0 | 1 | 1 | 10位數據發送,包括起始位,停止位 | 可變 |
1 | 0 | 2 | 11位數據發送,包括起始位,停止位 ,校驗位 | fosc/64 |
1 | 1 | 3 | 同方式2 |
- SM2:多機通信使能位。在方式2或方式3中,若SM2=1,則只有當接收到的第9位數據(RB8)為1時,才能將接收到的數據送入SBUF,並使接收中斷標志RI置位向CPU申請中斷,否則數據丟失;若SM2=0,則不論接收到的第9位數據為1還是為0,都將會把前8位數據裝入SBUF中,並使接收中斷標志RI置位向CPU申請中斷。在方式1,如SM2=1,則只有收到有效的停止位時才會使RI置位。在方式0時,SM2必須為0。
- REN:串口數據接收允許位,1允許,0禁止
- TB8:在方式2和方式3中,這位發送的是第9位。在多機通信中,常以該位的狀態來表示主機發送的是地址還是數據。通常規定:TB8為“0”表示主機發送的是數據,為“1”表示發送的是地址。
- RB8:在方式2和方式3中,這位發送的是第9位。它和SM2、TB8一起用於通信控制。
- TI:發送中斷標志位 ,用完時要用軟件清0
- RI:接受中斷標志位,用完時要用軟件清0
中斷
中斷源
51單片機有5個中斷源,5個中斷源分別是:
- 外部中斷0,從 P3.2 端口復用
- 外部中斷1,從 P3.3 端口復用
- 定時/計數器0溢出中斷
- 定時/計數器1溢出中斷
- 串口發送或接收中斷
中斷可以根據優先級實現嵌套,51 系列可以實現 2 級嵌套(對應優先級寄存器 IP),52 系列可以實現 4 級嵌套(對應優先級寄存器 IP 和 IPH)。
中斷對應信息
中斷名稱 | 中斷標志位 | 中斷號 | 默認優先級 | 中斷入口地址 |
---|---|---|---|---|
外部中斷0 | IE0 | 0 | 高 | 0003H |
定時/計數器0溢出中斷 | TF0 | 2 | ↓ | 000BH |
外部中斷1 | IE1 | 1 | ↓ | 0013H |
定時/計數器1溢出中斷 | TF1 | 3 | ↓ | 001BH |
串口發送或接收中斷 | RI/TI | 4 | 低 | 0023H |
中斷處理流程
- 停止主程序運行
- 保護斷點,把程序計數器 PC 的值壓入堆棧
- 尋找中斷入口,每個中斷都有不同的程序入口
- 執行中斷處理程序
- 中斷返回,繼續執行主程序
中斷的使用
任何中斷的使用都要滿足 3 個條件:
- 開啟總開關:EA
EX = 1
- 開啟指定中斷的開關,例如要使用外部中斷0,則必須設置:
EX0=1
- 發生中斷事件
中斷系統有一個總的開關 EA(IE 寄存器中),如果想使用中斷,必須打開總開關。
每個中斷都有一個單獨的開關,這些單獨的開關跟總開關 EA 一樣,都在 IE 寄存器中。
定時器中斷使用
初始值的計算
假設我要每計數 24 次觸發一次溢出中斷,定時器工作在模式 1,則計數初始值為 65536 - 24 = 65512。
Keil C51 代碼
外部中斷示例代碼
下面代碼使用了外部中斷0,上電后 P1 端口 0 號引腳的 LED 會一直閃爍,首次觸發外部中斷時,P1 端口所有 LED 點亮,再次觸發外部中斷時,0號引腳的 LED 再次開始閃爍,以此循環:
#include <reg52.h>
int flag = 1;
void delay() {
unsigned int a = 50000;
while(a--);
}
void main() {
EA = 1;// 開啟中斷總開關
IT0 = 1;// 設置外部中斷0觸發方式,下降沿觸發
EX0 = 1;// 開啟外部中斷0
while(1) {
while(flag & 0x01 == 1) {
P1 = 0xfe;
delay();
P1 = 0xff;
delay();
}
P1 = 0x00;
}
}
void externelInterrupt() interrupt 0 {
flag++;
}
電路圖:
串口示例代碼
/*9600@11.0592M*/
#include <reg52.h>
void InitUART(void) {
TMOD = 0x20;
SCON = 0x50;
TH1 = 0xFD;
TL1 = TH1;
PCON = 0x00;
EA = 1;
ES = 1;
TR1 = 1;
}
void SendOneByte(unsigned char c) {
SBUF = c;
while(!TI);
TI = 0;
}
void main(void) {
InitUART();
}
void UARTInterrupt(void) interrupt 4 {
if(RI) {
RI = 0;
} else
TI = 0;
}