一、匯編語言 編寫51單片機定時/計數中斷響應程序
ORG 0000H
LJMP MAIN
ORG 000Bh
LJMP ZD0
ORG 0040H
MAIN: MOV TH0,#0d8h
MOV TL0,#0e8h
MOV A,#0FEh
SETB EA
SETB ET0
SETB TR0
SJMP $
ZD0: MOV P2,A
MOV TH0,#0d8h
MOV TL0,#0f0h
inc r3
CJNE r3,#99D,ddd
mov r3,#00h
inc r4
CJNE r4,#9D,ddd
mov r4,#00h
RL A
DDD:reti
end
二、C語言編寫51單片機定時/計數中斷響應程序
#include"reg51.h"
int ms,i,k,n,m,temp=0x01;
void delay(ms)
{
while(ms--)
for(i=0;i<120;i++);
}
void main()
{
TMOD=0x01;
EA=1;
ET0=1;
TR0=1;
while(1);
}
void timer0() interrupt 1
{
TH0=(65536-10000-6)/256;
TL0=(65536-10000-6)%256;
k++;
if(k==100)
{
k=0;
m++;
}
if(m==8)m=0;
P2=temp<<m;//這個語句是將變量temp左移m位給P2,不改變temp的值。
//如果改成temp=temp<<m,就改變了temp的值
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
定時器中斷c語言解析interrupt x using y
interrupt 表示中斷優先級,using表示所用工作寄存器組。
interrupt x using y
跟在interrupt 后面的x 值是中斷號,就是說這個函數對應第幾個中斷端口,一般在51中,x對應的中斷如下:
0 外部中斷0
1 定時器0
2 外部中斷1
3 定時器1
4 串行中斷
其它的根據相應的單片機有自己的含義,實際上c在編譯的時候就是把你這個函數的入口地址放到這個對應中斷的跳轉地址
using y 這個y是說這個中斷函數使用的那個寄存器組就是51里面一般有4個 r0 -- r7寄存器,如果你的中斷函數和別的程序用的不是同一個寄存器組則進入中斷的時候就不會將寄存器組壓入堆棧,返回時也不會彈出來 節省代碼和時間
外部中斷INT0
void intsvr0(void) interrupt 0 using 1
定時/計數器T0
void timer0(void) interrupt 1 using 1
外部中斷INT1
void intsvr1(void) interrupt 2 using 1
定時/計數器T1
void timer1(void) interrupt 3 using 1
串口中斷
void serial0(void) interrupt4 using 1
一,中斷的概念
中斷:當計算機執行正常程序時,系統中出現某些急需處理的異常情況和特殊請求.
中斷的執行:當CPU正在執行某一程序時,若有中斷響應,則CPU轉而執行中斷服務程序,當中斷服務程序執行完畢后,CPU自動返回原來的程序繼續執行.
中斷服務程序的語句寫法與函數的寫法完全相同,所以,中斷服務程序也是函數,只在函數頭部有不同(后續).
中斷服務程序的執行與函數的執行不同:函數的執行是有固定位置的,是通過函數的調用來完成的;而中斷服務程序的執行是不固定位置的,只要有中斷響應,在一定條件下都會去響應中斷,即執行中斷服務程序.
二,中斷源
中斷源:任何引起計算機中斷的事件,一般一臺機器允許有許多個中斷源.
8051系列單片機至少有5個可能的中斷(8052有6個,其它系列成員最多可達15個).下面以5個中斷源為例.
8051單片機的五個中斷源是:
外部中斷請求0,由INT0(P3.2)輸入;
外部中斷請求1,由INT1(P3.3)輸入;
片內定時器/計數器0溢出中斷請求;
片內定時器/計數器1溢出中斷請求;
片內串行口發送/接收中斷請求.
三,與中斷有關的寄存器
1,定時/計數器控制寄存器TCON
TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0
D7 D6 D5 D4 D3 D2 D1 D0
. IT0,IT1:外部中斷0,1觸發方式選擇位,由軟件設置;
1→下降沿觸發方式,INT0/INT1管腳上高到低的負跳變可引起中斷;
0→電平觸發方式, INT0/INT1管腳上低電平可引起中斷.
. IE0,IE1:外部中斷0,1請求標志位;
當外部中斷0,l依據觸發方式滿足條件,產生中斷請求時由硬件置位 (IE0/IE1=1);當CPU響應中斷時由硬件清除(IE0/IE1= 0).
. TR0,TR1: 啟動定時/計數器0,1.
. TF0,TF1:定時器/計數器0,1(T/C0,T/C1)溢出中斷請求標志;
當T/C0,1計數溢出時由硬件置位(TF0/TF1=l);
當CPU響應中斷由硬件清除(TFO/TF1=0).
三,與中斷有關的寄存器
2,串行口控制寄存器SCON
TI RI
D7 D6 D5 D4 D3 D2 D1 D0
. RI:串行口接收中斷請求標志位;
當串行口接收完一幀數據后請求中斷,由硬件置位(RI=1)
RI必須由軟件清"0".
. TI:串行口發送中斷請求標志位.
當串行口發送完一幀數據后請求中斷,由硬件置位(TI=1)
TI必須由軟件清"0".
三,與中斷有關的寄存器
3,中斷允許寄存器IE
EA ET2 ES ET1 EX1 ET0 EX0
D7 D6 D5 D4 D3 D2 D1 D0
. EX0,EX1:外部中斷0,1的中斷允許位;
l→外部中斷0,1開中斷;0→外部中斷0,1關中斷.
. ET0,ET1:定時器/計數器0,1(T/C0,T/C1)溢出中斷允許位;
1→T/C0,T/Cl開中斷;0→T/C0,T/Cl關中斷.
. ES:串行口中斷允許位;
1→串行口開中斷;0→串行口關中斷.
. ET2:定時器/計數器2(T/C2)溢出中斷允許位;
1→T/C2開中斷;0→T/C2關中斷.
. EA:CPU開/關中斷控制位.
1→CPU開中斷.0→CPU關中斷.
8051復位時,IE被清"0",此時CPU關中斷,各中斷源的中斷也都屏蔽
三,與中斷有關的寄存器
4,中斷優先級寄存器IP
PS PT1 PX1 PT0 PX0
D7 D6 D5 D4 D3 D2 D1 D0
. PX0,PX1:外部中斷0,1中斷優先級控制位;
1→高優先級;0→低優先級.
. PT0,PT1:定時器/計數器0,1中斷優先級控制位;
1→高優先級;0→低優先級.
. PS:串行口中斷優先級控制位;
1→高優先級;0→低優先級.
8051復位時,IP被清"0",5個中斷源都在同一優先級,其內部優先級的順序從高到低為: 外部中斷0(IE0)
定時器/計數器0(TF0)
外部中斷1(IE1)
定時器/計數器1(TF1)
串行口中斷(RI+TI)
四,中斷響應
8051的CPU在每個機器周期采樣各中斷源的中斷請求標志位,如果沒有下述阻止條件,將在下一個機器周期響應被激活了的最高級中斷請求:
1.CPU正在處理同級或更高級的中斷;
2.現行機器周期不是所執行指令的最后一個機器周期;
3.正在執行的是RETI或是訪問IE或IP的指令.
CPU在中斷響應后完成如下的操作:
1.硬件清除相應的中斷請求標志;
2.執行一條硬件子程序,保護斷點,并轉向中斷服務程序人口
3.結束中斷時執行RETI指令,恢復斷點,返回主程序.
8051的CPU在響應中斷請求時,由硬件自動形成轉向與該中斷源對應的服務程序入口地址,這種方法為硬件向量中斷法.
五,中斷服務程序的入口地址
編號 中斷源 人口地址
0 外部中斷0 0003H
1 定時器/計數器0 000BH
2 外部中斷1 0013H
3 定時器/計數器1 001BH
4 串行口中斷 0023H
各中斷服務程序入口地址僅間隔8個字節,編譯器在這些地址放入無條件轉移指令跳轉到服務程序的實際地址.
六,中斷服務程序的語法規則
中斷服務程序的語法規則如下:
函數的返回值 函數名([參數]) interrupt n [using m]
{
函數體;
}
對中斷程序而言,函數的返回值和參數一般為void.
interrupt n 中n的取值為0~31的常數,不允許用表達式,表示中斷向量的編號.
using m 中m的取值為0~3的常數,不允許用表達式,表示內部RAM中的工作寄存器.
七,中斷說明
中斷不允許用于外部函數,它對函數目標代碼影響如下z
·當調用函數時,SFR中的ACC,B,DPH,DPL和PSW(當需要時)入錢;
.如果不使用寄存器組切換,甚至中斷函數所需的所有工作寄存器都入錢;
.函數退出前,所有的寄存器內容出錢;
·函數由8051的指令"RETI"終止.
中斷服務程序使用的任何程序也使用同一寄存器組.
八,中斷例子
#include 〈reg51.h〉
unsigned char status;
bit flag;
void service_int() interrupt 2 using 2
{
flag=1;
status=P1;
}
void main(void)
{
IP=0x04; IE=0x84;
for(;;){
if(flag){
switch(status){
case 0: break;
case 1: break;
case 2: break;
case 3: break;
default: ;}
flag=0;
}
}
}
習題
試設計滿足下列要求的電路圖:
1 單片機采用89C51,時鐘11.0592MHz;
2 有4個指示燈表示狀態;
3 外接12位A/D芯片AD574;
4 有4 * 4的鍵盤;
5 有字符型LCD(畫成插座形式,12Pin插座,管腳接法見書P253);
6 有串行接口與計算機連接;
7 設置8位二進制的地址,地址范圍可表示為0~255;
8 外接EEPROM.
定時器/計數器(T/C)
8051系列單片機至少有兩個16位內部定時器/計數器,8052有三個定時器/計數器,其中有兩個是基本定時器/計數器是定時器/計數器.它們既可以編程為定時器使用,也可以編程為計數器使用.
若是計數內部晶振驅動時鐘,它是定時器;若是計數,8051的輸入管腳的脈沖信號,它是計數器.
當T/C工作在定時器時,對振蕩源12分頻的脈沖計數,即每個機器周期計數值加1,計數率=1/12f osc.例當晶振為12MHz時,計數率=1000kHz,即每1μs計數值加1.
當T/C工作在計數器時,計數脈沖來自外部脈沖輸入管腳T0(P3.4)或T1(P3.5),當T0或T1腳上負跳變時計數值加1.識別管腳上的負跳變需兩個機器周期,即24個振蕩周期.所以T0或T1腳輸入的可計數外部脈沖的最高頻率為1/24fosc,當晶振為12MHZ時,最高計數率為500kHz,高于此頻率將計數出錯.
一,與T/C有關的SFR
1,計數寄存器Th和TL
T/C是16位的,計數寄存器由TH高8位和TL低8位構成.
在特殊功能寄存器(SFR) 中,
對應T/C0為TH0和TL0;
對應T/C1為TH1和TL1.
定時器/計數器的初始值通過TH1/TH0和TL1/TL0設置.
2,定時器/計數器控制寄存器TCON
前面已介紹.
二,與T/C有關的SFR
3,T/C的方式控制寄存器TMOD
. C/T:計數器或定時器選擇位;
1→為計數器;0→為定時器.
. GATE :門控信號;
1 → T/C的啟動受到雙重控制,即要求TR0/TR1和INT0/INT1同時為高;
0 → T/C的啟動僅受TR0或TR1控制.
GATE C/T M1 M0 GATE C/T M1 M0
D7 D6 D5 D4 D3 D2 D1 D0
T/C1
T/C0
三,四種工作方式
M1 M0 方式 功 能
0 0 0 13位定時器/計數器,TL是低5位,TH是高8位
0 1 1 16位定時器/計數器
1 0 2 常數自動重裝的8位定時器/計數器
1 1 3 僅用于T/C0,是兩個8位定時器/計數器
利用定時器編寫時鐘程序.
四,T/C工作方式的說明
1. 方式0:
當TMOD中MlM0=00時,T/C工作在方式0;
方式0為13位的T/C,由TH的高8位,TL的低5位的計數值,滿計數值213,但啟動前可以預置計數初值.
若T/C開中斷(ET=1)且CPU開中斷(EA=1)時,則定時器/計數器溢出時,CPU轉向中斷服務程序時,且TF白動清0.
2. 方式1:
當TMOD中MlM0=01時,T/C工作在方式1;
方式1與方式0基本相同.唯一區別在于計數寄存器的位數是16位的,由TH和TL寄存器各提供8位,滿計數值為216.
四,T/C工作方式的說明
3. 方式2:
當TMOD中MlM0=10時,T/C工作在方式2;
方式2是8位的可自動重載的T/C,滿計數值為28;
在方式0和方式1中,當計數滿后,若要進行下一次定時/計數,須用軟件向TH和TL重裝預置計數初值;
方式2中TH和TL被當作兩個8位計數器,計數過程中,TH寄存8位初值并保持不變,由TL進行8位計數.計數溢出時,除產生溢出中斷請求外,還自動將TH中初值重裝到TL,即重裝載.
4. 方式3:
方式3只適合于T/C0.當T/CO工作在方式3時,TH0和TL0成為兩個獨立的8位計數器.
五,定時器/計數器的初始化
在使用8051的定時器/計數器前,應對它進行編程初始化,主要是對TCON和TMOD編程;計算和裝載T/C的計數初值.一般完成以下幾個步驟:
(1)確定T/C的工作方式——編程TMOD寄存器;
(2)計算T/C中的計數初值,并裝載到TH和TL;
(3)T/C在中斷方式工作時,須開CPU中斷和源中斷——編程IE寄存器;
(4)啟動定時器/計數器——編程TCON中TR1或TR0位.
六,定時器/計數器的初值計算
在定時器方式下,T/C是對機器周期脈沖計數的,若fosc=12MHz,一個機器周期為12/fosc=1μs,則:
方式0 13位定時器最大定時間隔=8.192ms;(2的13次方=8192)
方式1 16位定時器最大定時間隔=65.536ms;(2的16次方=65536)
方式2 8位定時器最大定時間隔=256μs.
若使T/C工作在定時器方式1(16位二進制),要求定時1ms,求計數初值.設計數初值為x,則有:
(65536-x)×1μs=1000μs
或x=65536一1000
因此,TH,TL可置-1000;
即:TH= -1000/256;TL= -1000%256.
對一般fosc有下列公式(設定時時間為timeμs):
(216-x)×12/fosc= time μs
例1,設單片機的fosc=12MHz,要求在P1.0腳上輸出周期為2ms的方波
采用查詢方式.
#include 〈reg51.h〉
sbit P1_0=P1^0;
void main(void〉
{ TMOD=0x01; TR0=1;
for(;;)
{TH0= -1000/256;
TL0= -1000%256;
do {} while(!TF0);
P1_0=!P1_0;
TF0=0;
}
}
采用中斷方式.
#include 〈reg51.h>
sbit P1_0=P1^0;
void timer0(void) interrupt 1using 1 {P1_0=!P1_0; TH0= -1000/256;
TL0= -1000%256;}
void main(void)
{TMOD=0x01; P1_0=0;
TH0= -1000/256;
TL0= -1000%256;
EA=1;ET0=1;TR0=1;
do {} while(1);
}
例2,設單片機的fosc=6MHz,要求在P1.7腳上的指示燈亮一秒滅一秒.
void main(void)
{P1_7=0; P1_0=1;
TMOD=0x61;
TH0= -50000/256;
TL0= -50000%256;
TH1= -5; TL1= -5;
IP=0x08;
EA=l; ET0=1;
ET1=l; TR0=l;
TR1=1;
for (;;){}
}
#include
sbit P1_0=P1^0;
sbit P1_7=P1^7;
void timer0( ) interrupt 1 using 1
{P1_0=!P1_0;
TH0= -50000/256;
TL0= -50000%256;
}
void timer1( ) interrupt3 using 2
{P1_7=!P1_7;}
例3,設單片機的fosc=10MHz,要求在P1.0腳上輸出周期為2.5μs,占空比20%.
#include
#define uchar unsigned char
uchar time;
uchar period=250;
uchar high=50;
void timer0( ) interrupt l using 1
{TH0= -8333/256;
TL0= -8333%256;
if(++time==high)P1=0;
else if(time==period)
{time=0; P1=1;}
}
void main(void)
{
TMOD=0x01;
TH0= -8333/256;
TL0= -8333%256;
EA=l;
ET0=1;
TR0=1;
do {)while(1);
}
#include
#define uchar unsigned char
#define uint unsigned int
uchar time,status,percent,period;
bit one_round;
uint oldcount,target=500;
void pulse(void) interrupt 1using l
{TH0= -833/256;
TL0= -833%256; ET0=l;
if(++time==percent)P1=0;
else if (time ==100)
{time=0;P2=l;}
void tachmeter(void) interrupt 2 using 2
{union {uint word;
struct{uchar hi;uchar lo;}byte; }newcount;
newcount_byte.hi=TH1;
newcount_byte.lo=TLl;
period=newcount.word--oldcounts;
oldcount=newcount.word;
one -round=1;
void main(void)
{IP=0x04;
TMOD=0x01;
TCON=0x54;
TH1=0;TL1=0;
IE=0x86;
for(;;)
{if(one_round)
{if(period
{if(percent0)
--percent;
}
}
}
串行口
8051系列單片機有一個標準的串行通信接口,發送數據時由TXD端口送出,接收數據時由RXD端口輸入.
內置兩個緩沖器SBUF,一個接受緩沖器,另一個是接收緩沖器,可實行全雙工的串行通信.
近距離可直接用TTL電平,若與計算機通信,則需要將電平轉換成RS232電平形式,若需長距離通信可以采用RS485電平形式,通信的數據必須通過軟件的編寫來完成.
一,與串行口有關的SFR
1,串行口控制寄存器SCON
SM0 SM1 SM2 REN TB8 RB8 TI RI
D7 D6 D5 D4 D3 D2 D1 D0
. SM0,SM1:串行口工作方式控制位(見書P158).
. SM2:多機通信控制位(方式2,3);
1→只有接收到第9位(RB8)為1,RI才置位;
0→接收到字符RI就置位.
. REN :串行口接收允許位;
1→允許串行口接收;0→禁止串行口接收.
. TB8:方式2和方式3時,為發送的第9位數據,也可以作奇偶
校驗位.
. RB8:方式2和方式3時,為接收到的第9位數據;方式1時,
為接收到的停止位.
. TI:發送中斷標志;由硬件置位,必須由軟件清0.
. RI:接收中斷標志;由硬件置位,必須由軟件清0.
一,與串行口有關的SFR
2,電源控制寄存器PCON
SMOD
D7 D6 D5 D4 D3 D2 D1 D0
PCON的第7位SMOD是與串行口的波特率設置有關的選擇位.
. SMOD:串行口波特率加倍位.
1→方式1,3波特率=定時器1溢出率/16;方式2波特率為fosc/32;
0→方式1,3波特率=定時器1溢出率/32;方式2波特率為fosc/64.
二,串行口的工作方式
1. 方式0
方式0為移位寄存器輸入/輸出方式,串行數據通過RXD輸入/輸出 ,TXD則用于輸出移位時鐘脈沖.
方式0時,收發的數據為8位,低位在前.波特率固定為fosc/12,其中fosc為單片機外接晶振頻率.
發送是以寫SBUF寄存器的指令開始的,8位輸出結束時TI被置位.
方式0接收是在REN=1和RI=0同時滿足時開始的.接收的數據裝入SBUF中,結束時RI被置位.
移位寄存器方式的也可用于兩個單片機之間的通信.和通常9600波特相比,lMHz通信能力對短距離通信很吸引人.
二,串行口的工作方式
2. 方式1
方式1是10位異步通信方式,1位起始位(0),8位數據位和1位停止位(1).其中的起始 位和停止位在發送時是自動插入的.
任何一條以SBUF為目的寄存器的指令都啟動一次發送,發送的條件是TI=0,發送完置TI為1;
方式l接收的前提條件是SCON中的REN為l,同時下列兩個條件都滿足,本次接收有效,將其裝入SBUF和RB8位.否則放棄接收結果.兩個條件是:(1)RI=0;(2)SM2=0或
接收到的停止位為1;
方式1的波特率是可變的,波特率可由以下計算公式計算得到: 方式1波特率=2SMOD.(定時器1的溢出率)/32
其中的SMOD為PCON的最高位.定時器1的方式0,1,2,都可以使用,其溢出率為定時時間的倒數值.
二,串行口的工作方式
3. 方式2和方式3
這兩種方式都是11位異步接收/發送方式,它們的操作過程完全一樣,所不同的是波特率:
方式2波特率=2SMOD.(fosc/64);
方式3波特率同方式1(定時器l作波特率發生器).
方式2和方式3的發送起始于任何一條"寫SBUF"指令,當第9位數據(TB8)輸出之后,置位TI.
方式2和方式3的接收前提條件也是REN為1.在第9位數據接收到后,如果下列條件同時滿足(1)RI=0;(2)SM2=0或接收到的第9位為1,則將已接收的數據裝入SBUF和RB8,并置位RI,如果條件不滿足,則接收無效.
三,串行口的初始化
在使用串行口之前,應對它進行編程初始化,主要是設置產生波特率的定時器1,串行口控制和中斷控制寄存器.具體步驟如下:
(1) 確定定時器1的工作方式——編程TMOD寄存器;
(2) 計算定時器1的初值——裝載TH1,TL1,具體TH1和
TL1的值可查表得到;
(3) 啟動定時器1——編程TCON中的TR1位,即置TR1為1;
(4) 確定串行口的控制——編程SCON;
(5) 串行口在中斷方式工作時,須開CPU和源中斷——編程IE寄存器.
四,串行口舉例1
#include 〈reg51.h>
#define uchar unsigned char
#define uint unsigned int
uchar idata trdata[10]=
{'M','C','S','-','5','1', 0x0d,
0x0a,0x00};
void main(void)
{ uchar i; uint j;
TMOD=0x20;
TL1=0xfd;TH1=0xfd;
SCON=0xd8;PCON=0x00;
TR1=1;
while(1)
{i=0;
while(trdata[i]!=0x00)
{SBUF=trdata[i];
while(TI==0);
TI=0; i++;
}
for (j=0;j
void main(void〉
{unsigned char a;
TMOD=Ox20;
TL1=0xfd;TH1=0xfd;
SCON=Oxd8;PCON=0x00;
TR1=1;
while (1)
{while (RI==0); RI=0;
a=SBUF; SBUF=a;
while (TI==O); TI=0;
}
}
#include 〈reg51.h〉
#define uchar unsigned char
uchar xdata r_buf[32];
uchar xdata t_buf[32];
uchar r_in,r_out,t_in,t_out;
bit r_full,t_empty,t_done;
code uchar m[]={"this is a test
program\r\n"};
serial( ) interrupt 4 using 1
{ifRI&&~r_full)
{ r_buf[r_in]=SBUF; RI=0;
r_in=++r_in&0xlf;
if(r_in==r_out) r_full=1;}
else if (TI &&~t_empty)
{SBUF=t_buf[t_out]; TI=0;
t_out=++t_out&0x1f;
if{t_out==t_in)t_empty=l;
else if(TI){TI=0;
t_done =1;
}
}
void loadmsg(uchar code *msg)
{ while((*msg!=0)&&((((t_in+1)^ t_out)&0xlf)!=0))
{ t_buf[t_in]= *msg; msg++;
t_in=++t_in&0x1f;
if(t-done){TI=1;
t_empty=t_done =0 ;}
}
}
void process(uchar ch){return;}
void processmsg(void)
{while(((r_out+1)^r_in)!=0)
{process(r_buf[r_out]);
r_out=++r_out&0x1f;}
}
void main()
{TMOD=0x20;TH1=0xfd;TCON=0x40;
SCON=0x50;IE=0x90;
t_empty=t_done =1;r_full=0;
r_out=t_in=t_out=l;r_in=1;
for(;;){loadmsg(&m);
processmsg();}
}