51單片機中斷
- 51單片機中斷原理
- 中斷的概念:
- 中斷作用
- 中斷源及相關暫存器
- 中斷源及優先級
- 定時器/計數器控制暫存器 TCON
- 中斷允許暫存器 IE
- 中斷優先暫存器 IP
- 作業方式暫存器TMOD
- 定時器初值暫存器THx 和 TLx
- 計數器初值的計算:
- 外部中斷
- 操作步驟(INT0 INT1)
- 如何配置外部中斷
- 程式示例
- 定時器/計數器中斷
- 需要了解的知識
- 作業原理
- 定時器結構
- 操作步驟(T0 T1)
- 如何配置定時器
- 程式示例
- 最終專案展示
51單片機中斷原理
中斷的概念:
CPU在處理某一事件A時,發生了另一事件B請求CPU迅速去處理(中斷發生),那么CPU就會暫停當前的作業(A事件),去執行B事件(中斷回應和中斷服務),然后B事件做完之后,再回到原來的事件(A事件)中繼續作業,(中斷的回傳),

中斷作用
隨著計算機技術的應用,人們發現中斷技術不僅解決了快速主機與I/O設備的資料傳送問題,而且還有具有如下的優點:
1. 分時操作:CPU可以分時為多個I/O設備服務,提高了計算機的利用率,
2. 實時操作:CPU能夠及時處理應用系統的隨機事件,系統的實時性大大增強,
3. 可靠性高:CPU具有處理設備故障及掉電等突發性事件能力,從而使系統可靠性更高,
中斷源及相關暫存器
中斷源及優先級
| 中斷源符號 | 名稱 | 中斷標志 | 中斷引起原因 | 中斷號 | 優先級 |
|---|---|---|---|---|---|
| /INT0 | 外部中斷0 | IE0 | 低電平或下降沿信號 | 0 | 最高 |
| T0 | 定時器中斷0 | TF0 | 定時/計數器0 計數回0溢位 | 1 | ↓ |
| /INT1 | 外部中斷1 | IE1 | 定電平或下降沿信號 | 2 | ↓ |
| T1 | 定時器中斷1 | TF1 | 定時/計數器1 計數回0溢位 | 3 | ↓ |
| TX/RX | 串行口中斷 | TI/RI | 串行通信完成一幀資料發送或接收 | 4 | 最低 |
定時器/計數器控制暫存器 TCON
| 位 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|---|---|---|---|---|---|---|---|---|
| 功能 | TF1 | TR1 | TF0 | TR0 | IE1 | IT1 | IE0 | IT0 |
TF0、 TF1: 是定時器中斷標志(定時器0溢位標志位、定時器1溢位標志位)
TR0 、TR1: 打開相應的定時器(定時器0運行控制位,=1時啟動定時器0、定時器1運行控制位,=1時啟動定時器1)
由軟體清0關閉定時器0/1,當GATE=1,且INIT為高電平時,TR1置1啟動定時器1;當GATE=0時,TR1置1啟動定時器0/1,
IT0、IT1: 是外部中斷的觸發方式, =0時 低電平觸發,=1時負跳變觸發,
IE0、IE1: 是外部中斷的標志位
中斷允許暫存器 IE
| 位 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|---|---|---|---|---|---|---|---|---|
| 功能 | EA | —— | —— | ES | ET1 | EX1 | ET0 | EX0 |
EA: 總中斷允許, EA=0;CPU屏蔽所有中斷的請求 EA=1;開放所有中斷,
ES:串行口中斷允許位,ES=0; 禁止串行中斷,ES=1; 允許串口中斷,
ET0、ET1: 定時器/計數器0 和 定時器/計數器 1 中斷允許位 =0時 禁止相應的定時器中斷, =1 允許相應的定時器中斷,
EX0、EX1: 外部中斷0 和 外部中斷 1 中斷允許位,=0時 禁止相應的外部中斷, =1時 允許相應的外部中斷,
——:無效位
中斷優先暫存器 IP
| 位 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|---|---|---|---|---|---|---|---|---|
| 功能 | —— | —— | —— | PS | PT1 | PX1 | PT0 | PX0 |
PS: 串行口中斷優先級 =0時,串行口中斷定義為高優先級 =1時,低優先級
PT0、PT1: 定時器0 和 定時器 1 的中斷優先級 =0時 高 = 1時為低
PX0、PX1: 外部中斷0 和 外部中斷1 的中斷優先級 =0時 高 =1時 低
默認是為低優先級,
作業方式暫存器TMOD
| 位 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|---|---|---|---|---|---|---|---|---|
| 功能 | GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 |

GATE:門控制
=0:僅有運行控制位TRx來控制定時/計數器的開啟,
=1:由TRx和外部中斷脈沖計數,(用于計算外部中斷 負跳變 的次數)
C/T:計數器模式和定時器模式選擇
=0:選擇定時器模式
=1:選擇計數器模式
M1、M0:選擇定時/計數器的作業方式
| M1 | M0 | 作業方式 |
|---|---|---|
| 0 | 0 | 方式0:為13位定時/計數器 |
| 0 | 1 | 方式1:為16位定時/計數器 |
| 1 | 0 | 方式2:為8位初值自動重裝定時/計數器 |
| 1 | 1 | 方式3:僅適用于T0,分成兩個8位計數器,T1停止計數, |
方式0
方式0為13位計數,由TL0的低5位(高3位未用)和TH0的8位組成,TL0的低5位溢位時向TH0進位,TH0溢位時,置位TCON中的TF0標志,向CPU發出中斷請求,

方式1
方式1的計數位數是16位,由TL0作為低8位,TH0作為高8位,組成了16位加1計數器 ,

方式2
方式2為自動重裝初值的8位計數方式,

方式3
方式3只適用于定時/計數器T0,定時器T1處于方式3時相當于TR1=0,停止計數,

定時器初值暫存器THx 和 TLx
首先先了解一下CPU時序有關知識:
振蕩周期: 為單片機提供定時信號的振蕩源的周期(晶振周期或外加振蕩周期)
狀態周期:2個振蕩周期為1個狀態周期,用S表示,振蕩周期又稱S周期或時鐘周期,
機器周期:1個機器周期含6個狀態周期,12個振蕩周期,
指令周期:完成1條指令所占用的全部時間,它以機器周期為單位,
例如:外接晶振為12MHz時,51單片機相關周期的具體值為:
振蕩周期=1/12us;
狀態周期=1/6us;
機器周期=1us;
指令周期=1~4us;
計數器初值的計算:
機器周期就是CPU完成一個基本操作所需要得時間,
機器周期 = 1 /單片機的時鐘頻率
51單片機內部時鐘頻率是外部時鐘的12分頻,也就是當外部晶振的頻率輸入到單片機里面
的時候要進行12分頻,
比如:你用的是12MHZ的晶振,當你使用12MHZ的外部晶振的時候,
機器周期 = 1 / 1M = 1us,(選擇定時器作業方式1 16位)
我們2的16次方等于65536,也就是最大值為65536(溢位)
如果定時1ms
初值就為:1ms / 1us = 1000,也就是要計數1000個數, 初值 = 65535-1000+1 = 64536,65536才會溢位, 所以初值即FC18H(十進制為64536)
如果定時50ms
50ms/1us=50000;
初值 = 65535-50000+1=15536;
定時為50ms 初值為15536 即3CB0(十六進制)
對于每個不同的方式計算初值數有公式去計算的,但是我不會哈哈,我是用一個軟體來計算的,軟體名字 mcuelf這樣也比較快

外部中斷
操作步驟(INT0 INT1)
1 設定 外部中斷中斷源號及觸發方式,設定IT0或IT1(TCON暫存器)
2 打開相應的外部中斷允許,設定EX0或EX1(IE暫存器)
3 打開總中斷,設定EA(IE暫存器)
如何配置外部中斷
這里就用外部中斷0開示例吧
//配置外部中斷0
void initInterrupt0()
{
IT0 = 1; //觸發方式為負跳變觸發
EX0 = 1; //打開外部中斷0允許
EA = 1; //總中斷打開
}
中斷服務函式(發生中斷你想做什么?)
無回傳值 函式名(隨意起) interrupt 中斷號
void interrupt0ServiceFun() interrupt 0
{
//撰寫你要做的事情
}
外部中斷1也是差不多的,這里就不寫了,
程式示例
實作按鍵按下開燈/關燈
#include "reg52.h"
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long int u32;
sbit KEY = P3^2; //定義中斷按鍵引腳
sbit LED = P2^0; //定義LED1引腳
//配置中斷0
void initInterrupt0()
{
EX0 = 1;
IT0 = 1;
EA = 1;
}
//延時函式
void delay(u8 i)
{
while(i--);
}
void main()
{
initInterrupt0(); //呼叫中斷
while(1);
}
//發生中斷執行函式
void interruptHandler() interrupt 0
{
delay(12000); //延遲 因為當進入外部中斷函式的時候按鍵時已經按下了這里是消抖作用
if(KEY == 0) //再次確認是否真的按鍵被按下
{
LED = ~LED; //開燈/關燈
}
}
定時器/計數器中斷
需要了解的知識
51單片機有兩組定時器/計數器,因為既可以定時,又可以計數,故稱之為定時器/計數器,
定時器/計數器和單片機的CPU是相互獨立的,定時器/計數器作業的程序是自動完成的,不需要CPU的參與,
51單片機中的定時器/計數器是根據機器內部的時鐘或者是外部的脈沖信號對暫存器中的資料加1,
有了定時器/計數器之后,可以增加單片機的效率,一些簡單的重復加1的作業可以交給定時器/計數器處理,CPU轉而處理一些復雜的事情,同時可以實作精確定時作用,
作業原理
實質上是加1計數器,隨著輸入脈沖,計數器自動加1,
溢位的時候會回0.,且計數器的溢位使相應的中斷標志位 置1.
向CPU發出中斷請求,如果定時/計數器作業于定時模式,則表示定時時間已到;如果作業于計數模式,則表示計數值已滿,
可見,由溢位時計數器的值減去計數初值才是加1計數器的計數值,
定時器結構
定時/計數器的實質是加1計數器(16位),由高8位和低8位兩個暫存器THx和TLx組成,TMOD是定時/計數器的作業方式暫存器,確定作業方式和功能;TCON是控制暫存器,控制T0、T1的啟動和停止及設定溢位標志,

操作步驟(T0 T1)
1.選擇作業方式,設定M1、M0 (TMOD暫存器)
2.選擇控制方式,設定GATE(TMOD暫存器)
3.選擇定時器還是計數器模式,設定C/T(TMOD暫存器)
4.給定時/計數器賦初值,設定THx 和 TLx(定時器初值暫存器)
5.開啟總中斷,設定EA(IE暫存器)
6.打開相應定時器中斷允許, 設定ET0或ET1(IE暫存器)
7.啟動定時器,設定TR1或TR0(TCON暫存器)
如何配置定時器
這里就選擇定時器0吧 選擇方式1(16位)進行定時示例吧
1.選擇作業方式1(16位)M1=0;M0=1;
2.控制方式 :僅有運行控制位TRx來控制定時/計數器的開啟,GATE=0;
3.選擇定時器模式 C/T=0;
TMOD=0x01;
4.賦初值 這里選擇定時為50ms 我的板子晶振是11.0592 具體怎么算我不會,推薦跟我一樣不會的用軟體mcuelf計算出以下結果

TH0 = 0x4C;
TL0 = 0x00;
5.打開總中斷(總開關)
EA=1;
6.打開T0中斷開關
ET0=1;
7.啟動定時器0
TR0=1;
//配置定時器函式
void time0Config()
{
TMOD=0x01; //設定T0定時器,選擇作業方式1(16位),定時器模式 僅有運行控制位TRx來控制定時/計數器的開啟,
TH0 = 0x4C; //初值設定
TL0 = 0x00; //初值設定
EA=1; //打開總中斷開關
ET0=1; //打開T0中斷開關
TR0=1; //啟動定時器0
}
//發生中斷執行函式(這樣就是50ms)
void time0() interrupt 1 //T0中斷號為1
{
/*要注意這里不會自動重裝,所以要再次設定回初值(除作業方式2)*/
TH0 = 0x4C; //初值重新設定
TL0 = 0x00; //初值重新設定
//撰寫你要做的事
}
程式示例
這里就寫一個用定時器來做一個簡單的時鐘顯示在LCD1602上
LCD1602我昨天發布了一個LCD1602的使用和顯示hello word,這里就是詳細講LCD1602具體的操作了,
引腳定義
//引腳定義
#define LCD P0
sbit E = P2^7; //使能
sbit RS = P2^6; //資料/命令(H/L)
sbit RW = P2^5; //讀寫(H/L)
lcd1602.h
void write_com(unsigned char command); //寫命令函式
void write_data(unsigned char dat); //寫資料函式
void init_lcd(); //初始化LCD1602函式
void delay5ms(); //延時5ms函式
lcd1602.c
#include <reg52.h>
#include "lcd1602.h"
#define LCD P0
sbit E = P2^7;
sbit RS = P2^6;
sbit RW = P2^5;
/******延遲5毫秒函式********/
void delay5ms() //誤差 -0.000000000001us
{
unsigned char a,b;
for(b=15;b>0;b--)
for(a=152;a>0;a--);
}
/******LCD1602寫命令函式********/
void write_com(unsigned char command)
{
RS = 0;
RW = 0; //高讀低寫
LCD = command;
delay5ms(); //這里延時最低要30納秒 我們直接給5ms
E = 1; //使能拉高
delay5ms(); //最低要求延遲150納秒 我們直接給5ms
E = 0;
}
/******LCD1602寫資料函式********/
void write_data(unsigned char dat)
{
RS = 1;
RW = 0;
LCD = dat;
delay5ms(); //這里延時最低要30納秒 我們直接給5ms
E = 1; //使能拉高
delay5ms(); //最低要求延遲150納秒 我們直接給5ms
E = 0;
}
/******初始化LCD1602********/
void init_lcd()
{
write_com(0x06); //寫入資料后游標自動右移 整屏不移動,
write_com(0x0c); //開顯示功能 無游標 不閃爍
write_com(0x38); //資料總線8位 16X2顯示 5*7點陣
write_com(0x01); //清屏 0000 0001
}
main.c
#include <reg52.h>
#include "lcd1602.h"
unsigned char t = 0; //用來計數時間
unsigned char i=0;
unsigned char hours = 23; //小時
unsigned char minutes = 59; //分鐘
unsigned char seconds = 0; //秒
unsigned char date[16] = {"2021-01-27 WED"};
unsigned char time[5] = {"time:"};
//配置定時器函式
void time0Config()
{
TMOD=0x01; //設定T0定時器,選擇作業方式1(16位),定時器模式 僅有運行控制位TRx來控制定時/計數器的開啟,
TH0 = 0x4C; //初值設定
TL0 = 0x00; //初值設定
EA=1; //打開總中斷開關
ET0=1; //打開T0中斷開關
TR0=1; //啟動定時器0
}
void main()
{
init_lcd(); //1.初始化lcd1602
write_com(0x80); //設定顯示日期位置 (第一行第一個開始)
for(i=0;i<16;i++)
{
write_data(date[i]);
}
write_com(0xc0); //設定顯示time:位置(第二行第一個開始)
for(i=0;i<5;i++)
{
write_data(time[i]);
}
time0Config();//呼叫定時器中斷
while(1)
{
write_com(0xc6); //設定顯示小時的十位 位置
write_data(hours/10+'0'); //小時十位
write_com(0xc7); //設定顯示小時的個位 位置
write_data(hours%10+'0'); //小時個位
write_com(0xc8); //設定顯示: 位置
write_data(':'); //顯示 :
write_com(0xc9); //設定顯示分鐘的十位 位置
write_data(minutes/10+'0'); //分鐘十位
write_com(0xca); //設定顯示分鐘的個位 位置
write_data(minutes%10+'0'); //分值個位
write_com(0xcb); //設定顯示: 位置
write_data(':'); //顯示 :
write_com(0xcc); //設定顯示秒的十位 位置
write_data(seconds/10+'0'); //秒十位
write_com(0xcd); //設定顯示秒的 個位 位置
write_data(seconds%10+'0'); //秒個位
}
}
//發生中斷執行函式(一次就是50ms)
void time0() interrupt 1 //T0中斷號為1
{
/*要注意這里不會自動重裝,所以要再次設定回初值(除作業方式2)*/
TH0 = 0x4C; //初值重新設定
TL0 = 0x00; //初值重新設定
t++;
if(t == 20) //證明夠1秒了 20 X 50ms = 1000ms = 1s
{
seconds++; //秒+1
t=0; //夠了1秒設定為0 重新計數
}
if(seconds == 60) //如果秒到60
{
minutes++; //分鐘+1
seconds = 0; //秒回0
}
if(minutes == 60) //如果分鐘到60
{
hours++; //小時+1
minutes = 0; //分鐘回0
}
if(hours == 24 ) //如果小時到24
{
hours = 0; //小時回0
}
}
最終專案展示

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/254075.html
標籤:其他
