網站開發(fā)組織架構圖網頁設計模板圖片
本文是《單片機原理及應用》專欄中的最后一篇文章,筆者以編譯器的安裝配置——51單片機簡介——LED和數碼管外設——開關和按鍵控制功能切換——外部中斷系統(tǒng)——定時器與計數器為知識大綱,介紹了C語言編程控制51單片機的入門教程。作為收尾,我們將一同學習三個定時器/計數器的實際應用,不同于之間燈光和按鍵的組合,在這三個應用中都加入了其他外設,更復雜也更有創(chuàng)意,希望大家對單片機的學習也能興致盎然、不斷進取。
定時器控制P1.0引腳產生1ms方波
首先選用系統(tǒng)時鐘為12MHz的單片機, 要在P1.0上產生周期為2ms的方波,定時器應產生1ms的定時中斷,定時時間到則在中斷服務程序中對P1.0求反。在本案例我們使用定時器T0,方式1定時中斷,GATE不起作用。此外對中斷標志位采用查詢法,查詢法不經過中斷程序,是最簡單的I/O方式,數據在CPU和外設之間的傳送完全靠計算機程序控制,外設和CPU之間是串行工作,CPU效率低。由于查詢法效率低于中斷函數,已經被逐漸淘汰,筆者此處僅作為知識補充。
#include <reg51.h>
sbit P1_0=P1^0;
void main(void)
{TMOD=0x01; //設置T0為定時器工作方式1TR0=1; //允許T0中斷while(1) {TH0=0xfc; //賦初值,計數1000即溢出:X=65536-1000TL0=0x18;do{}while(!TF0); //使用查詢法,TF0為0原地循環(huán),為1則T0溢出,往下執(zhí)行P1_0=!P1_0; //P1.0狀態(tài)求反,高低電平轉換TF0=0; //定時器T0溢出標志位TF0清零}
}
仿真如圖所示,為了確認輸出波形,在proteus中添加了虛擬示波器,添加方式是點擊左側菜單欄虛擬儀器模式,在彈出的窗口中選擇OSCILLIOSCOPE即可。
另外請大家注意,左側按鍵屬于復位電路,對方波輸出無影響。啟動仿真后即可看到示波器圖像,示波器右側還有各類參數,大家可以自行調整。
?定時器控制發(fā)出1KHz音頻
第二個應用是用定時器T1的中斷控制P1.7引腳輸出頻率為1kHz方波音頻信號,驅動蜂鳴器發(fā)聲。單片機系統(tǒng)時鐘為12MHz,音頻信號周期1ms,因此T1的定時中斷時間為0.5 ms,進入中斷服務程序后,對P1.7求反。原理與應用一大致相同,區(qū)別在代碼構架中采用了中斷法,且在while循環(huán)中多次更改定時器初值。這幾個應用或多或少都采用了與眾不同的設計思路,更貼合實際使用場景。
#include<reg51.h>
sbit sound=P1^7; //將蜂鳴器連接在P1.7引腳上
#define f1(a) (65536-a)/256 //宏定義輸入計數值即按表達式輸出初值
#define f2(a) (65536-a)%256
unsigned int i=500;
unsigned int j=0;
void main(void)
{EA=1; //開啟總中斷允許.ET1=1; //允許定時器T1中斷 TMOD=0x10; //T設置定時器1工作方式1 TH1=f1(i); //初始化計數值i得到初值,此時i為500TL1=f2(i); TR1=1; //啟動定時器1while(1) { /*i=500; //可以改變頻率達到不同的聲音效果while(j<2000);j=0;i=1000; while(j<2000);j=0;i=3000; while(j<2000);j=0;*/ //返回第一種頻率}}void T1_int(void) interrupt 3 using 0 //定時器T1中斷服務函數
{TH1=f1(i); //重新賦初值.TL1=f2(i); sound=~sound; //對P1.7輸出求反,形成方波j++; //用于記錄中斷次數,也就是記錄時間
}
仿真如下,為了更直觀地看到頻率的變化,在蜂鳴器旁并聯(lián)了一個示波器,雖然會降低幅值,但是不影響頻率。
定時器控制蜂鳴器發(fā)出1KHz音頻
?蜂鳴器聲音較大,大家在觀看視頻演示時需要注意調節(jié)音量。
定時器測量INT1引腳正脈沖寬度
第三個應用是計數器測量INT1引腳正脈沖寬度,利用門控位GATE1可使T1啟動計數受INT1控制:當GATE1=1,TR1=1時,只有INT1引腳輸入高電平時,T1才被允許計數。利用該功能,可測量引腳正脈沖寬度,并在6位數碼管上以機器周期數顯示,能通過旋轉信號源旋鈕調整頻率。
在中斷處理上再次使用了查詢法,另外需要注意INT1作為外部中斷1的引腳,并未像之前我們介紹的那樣設置TCON用于外部中斷允許。作為最后一個應用,可能有些難以理解,大家了解相關原理內容就好。
#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
sbit P3_3=P3^3; //讀取INT1引腳電平
uchar count_high; //定義計數變量,用來讀取TH0
uchar count_low; //定義計數變量,用來讀取TL0
uint num;
uchar shiwan, wan, qian, bai, shi, ge; //定義各數碼管顯示數位
uchar flag; //控制刷新頻率
uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //數組記錄共陰極數碼管段碼void delay(uint z) //自變量延時函數
{uint x,y;for(x=z;x>0;x--)for(y=110;y>0;y--);
}
void display(uint a,uint b,uint c,uint d,uint e,uint f){P2=0xfe; P0=table[f]; delay(2); //數碼管段選,位選,消影P2=0xfd; P0=table[e]; delay(2); //六個數位對應留個雙管P2=0xfb; P0=table[d]; delay(2);P2=0xf7; P0=table[c]; delay(2);P2=0xef; P0=table[b]; delay(2);P2=0xdf; P0=table[a]; delay(2);
}
void read_count() //讀取定時寄存器的內容
{do{count_high=TH1; //讀取高字節(jié)count_low=TL1; //讀取低字節(jié)}while (count_high!=TH1);//讀取完成后繼續(xù)往下執(zhí)行 num=count_high*256+count_low; //將16位數據恢復為十進制數字}
void main( )
{while(1){flag=0;TMOD=0x90; //設置定時器T1工作方式1,門控位為1,當INT1為高電平時定時器才開始工作TH1=0; //計數初值為零TL1=0;while(P3_3==1); //等待INT1變?yōu)榈碗娖絋R1=1; //如果INT1為低電平,啟動定時器器T1,此時未真正開始計數while(P3_3==0); //等待INT1變?yōu)楦唠娖?#xff0c;變高后T1真正開始計數while(P3_3==1); //等待INT1變?yōu)榈碗娖?#xff0c;變低后T1停止計數TR1=0;read_count(); //調用函數,讀取定時寄存器內容的函數shiwan=num/100000; //數據分割,每一位數碼管顯示對應數位wan=num%100000/10000;qian=num%10000/1000;bai=num%1000/100;shi=num%100/10;ge=num%10;while(flag!=100) //運行100次顯示數值后再讀取下一次數值 {flag++;display(ge,shi,bai,qian,wan,shiwan); //調用display函數,傳入相應參數}}
}
仿真如下,為避免接線混亂,采用了之前介紹的總線接法;P0端口使用了上拉電阻,此時端口為準雙向口,不存在高阻抗的懸浮狀態(tài);和示波器一樣,信號發(fā)生器也在虛擬儀器界面,選擇SIGNAL GENERATOR即可。
?
大家可以更改信號發(fā)生器的參數,包括頻率、電壓和波形,觀察數碼管示數的變化,詳細的操作教程可以參考51單片機數字頻率計開發(fā)
定時器測量INT1引腳正脈沖寬度
到此為止,這個專欄就算圓滿落幕了,這部分單片機課程筆者也是學習了一月有余,對于非電子專業(yè)的同學來說這些知識足夠了。以后如果有進一步的機會,或許會重啟這個專欄。總之感謝陪伴我一同學習的每一個人,祝愿大家在新的一年里都能百尺竿頭更進一步,謝謝大家,我們下個專欄再見。