單片機原理及應用實驗報告
引言
單片機是一種控制芯片,一個微型的計算機,而加上晶振,存儲器,地址鎖存器,邏輯門,七段譯碼器(顯示器),按鈕(類似鍵盤),擴展芯片,接口等那是單片機系統。80C51單片機有40個引腳大致可分為4類:電源、時鐘、控制和I/O引腳。1. 電源:⑴ VCC-芯片電源,接+5V;⑵ VSS-接地端;⒉ 時鐘:XTAL1、XTAL2-晶體振蕩電路反相輸入端和輸出端。⒊ 控制線:控制線共有4根,⑴ ALE/PROG:地址鎖存允許/片內EPROM編程脈沖 ① ALE功能:用來鎖存P0口送出的低8位地址 ② PROG功能:片內有EPROM的芯片,在EPROM編程期間,此引腳輸入編程脈沖。⑵ PSEN:外ROM讀選通信號。⑶ RST/VPD:復位/備用電源。① RST(Reset)功能:復位信號輸入端。② VPD功能:在Vcc掉電情況下,接備用電源。 ⑷ EA/Vpp:內外ROM選擇/片內EPROM編程電源。① EA功能:內外ROM選擇端。② Vpp功能:片內有EPROM的芯片,在EPROM編程期間,施加編程電源Vpp。⒋ I/O線80C51共有4個8位并行I/O端口:P0、P1、P2、P3口,共32個引腳。P3口還具有第二功能,用于特殊信號輸入輸出和控制信號(屬控制總線)。80C51的存儲器組織結構可以分為三個不同的存儲空間,分別是:⑴ 64KB程序存儲器(ROM),包括片內ROM和片外ROM;⑵ 64KB外部數據存儲器(外RAM);⑶ 256B內部數據存儲器(內RAM)(包括特殊功能寄存器)。
目錄
一、 KEIL 和PROTEUS 聯調實驗 1
二、 中斷與定時實驗 5
三、 輸入輸出實驗 13
四、 ADC/DAC 21
五、通信實驗 35
實驗總結與感想 54
一、 KEIL 和PROTEUS 聯調實驗
1.1 實驗要求
1. 采用延時方法,實現2kHz的方波輸出,觀察LED的現象。
2. 采用軟件延時方法實現頻率2Hz方波輸出,實現LED的閃爍顯示。
3. 采用LED的閃爍顯示的方法,實現檢測內存單元20H中高電平“1”的個數,閃爍1次表示1,閃爍2次表示2….,結束關顯示2秒。循環進行。
4. 采用LED1的閃爍顯示的表示“1”,LED2的閃爍表示“0”的方法,實現檢測內存單元20H中的數據大小,從高位開始,結束關顯示2秒。循環進行。
5. P2口驅動8個LED,設計流水燈程序,實現每0.5s移位顯示。
1.2程序實現
1)
圖1.1
在圖1.1中,經過一定的時鐘周期(晶振為12MHZ),根據一個機器周期為1us來計算延遲時間,達到延時時間信號取反,在Proteus中仿真得到如下波形:
2)
圖1.2
在圖1.2中,經過一定的時鐘周期(晶振為12MHZ),根據一個機器周期為1us來計算延遲時間,達到延時時間信號取反,在Proteus中仿真得到如下波形:
3)
每個單元存儲的數據為8位,將數據0FEH存入A后進行移位操作,通過一定的時間間隔,實現8個LED依次循環點亮,從而實現流水燈的設計;仿真結果如下:
1.3實驗總結
51單片機是基礎的開發工具,這節課我們主要實現了兩個軟件的聯調以及匯編語言的編寫,匯編是基礎的機器語言一開始的編程不是很順利一遍一遍的試,尤其的延遲程序雖然有單字節指令、多字節指令,根據機器周期一個一個進行計算確定延遲時間,總是有那么一點誤差。軟件的聯調也在實驗過程中實現了,查找自己代碼上的錯誤以及指導計算機是如何運行我們所編寫的程序,讓我的編程能力得到了一定提升,也更加了解熟悉計算機,要繼續努力不斷深入下去,加油。
二、 中斷與定時實驗
2.1 實驗要求
1. 第1次按鍵LED亮,第2次按鍵燈滅,用中斷方式實現;
2. P2.0輸出2kHz的方波,用中斷方式實現;
3. P2.0輸出1kHz占空比25%的方波,用中斷方式實現;
4. 按鍵中斷交替實現,P2.0輸出2kHz與500Hz的方波,用中斷方式實現;
5. 在1-2s時間內按鍵1次P2.0發出1kHz方波,在1-2s時間內按鍵連2次,P2.0發出2kHz的方波。在1-2s內連擊按鍵3次,P2.0發出500Hz 的占空比25%的方波。
2.2 程序實現
1)
圖2.1
在圖2.1中,設定一個外部中斷0,實時監聽P2.0口,一旦檢測到按鍵按下,參數A取反,對應輸出相應的高低電平。一開始燈不亮,當按鍵按下時燈亮,再按一下燈又熄滅。。。。。。如此反復。在Proteus上仿真得到如下情況:
圖2.2
2)
圖2.3
在圖2.3中,根據一定的時鐘周期(晶振為12MHZ),利用定時器1來進行定時,定時器1每間隔0.25ms對P2_0取反一次,如此循環得到頻率為2kHz的方波,在Proteus上仿真得到如下情況:
圖2.4
3)
圖2.5
在圖2.5中,要獲得一個1kHz、占空比為25%的方波,只要在0.25ms時電平置低,之后再經過0.75ms,電平再拉高,由此實現1kHz、占空比25%方波的輸出,在Proteus上仿真得到如下情況:
4)
圖2.7
在圖2.7中,設定一個外部中斷0,實時監聽INT0口,一旦檢測到按鍵按下,參數A取反。對于參數A的不同取值,通過定時器1分別輸出2kHz與500Hz。在Proteus上仿真得到如下情況:
圖2.8
5)
圖2.9
在圖2.9中,通過中斷函數ext0(),在一定時間內按鍵按下幾次,參數A取相應的值。再通過定時器1,通過判斷參數A的取值,P2.0口輸出相應的方波。當按鍵次數A=1時實現P2.0口輸出1kHz方波,當按鍵次數A=2時實現P2.0口輸出2kHz方波,當按鍵次數A=3時實現P2.0口輸出500Hz占空比25%的方波。編譯無誤后進行Proteus仿真:
圖2.10 P2.0口輸出1kHz方波
圖2.11 P2.0口輸出2kHz方波
圖2.12 P2.0口輸出500Hz占空比25%的方波
在圖2.10、2.11、2.12中,通過按鍵次數的增加,實現實驗所需要求的功能,順利完成實驗。
2.3實驗總結
在做實驗之前,我復習了中斷的概念,包括外部中斷、定時器中斷,了解到51單片機內部資源豐富,能實現現階段很多想要實現的功能。感覺學了這次實驗的內容,我可以動手做很多跟時間有關的小作品,在實驗課上,我對代碼進行優化,因為一開始對定時器所給的初值與我們所需要方波的延遲周期有一定的誤差,需要不斷調試來產生的方波更加優美。
三、 輸入輸出實驗
3.1實驗要求
1) 設計電路圖
由P2口控制顯示器位碼,P1口控制顯示器段碼,設計6位的LED顯示器,采用動態顯示。也可以采用靜態顯示。
用P1或P3口設計一鍵盤,可以是16個按鍵的行列按鍵,也可以是8個獨立按鍵。
2) 程序設計
1. 實現顯示123456
2. 時間顯示,顯示格式 XXXX.XX (s)
3. 實現秒表功能,按一鍵啟動秒表,再按一次該按鍵停止計時,顯示格式 MM.SS.XX.
4. 實現倒計時功能,按鍵設定計時的時間,啟動按鍵計時,中間可以按鍵暫停計時,時間到后閃爍顯示屏,顯示格式 MM.SS.XX.
5. 實現時鐘功能,可按鍵修改初始時間,顯示格式 HH.MM.SS
要求采用按功能鍵實現功能2~5的切換。
3.2程序實現
1) 實現顯示123456。
圖3.1
在圖3.1中,采用動態掃描的方式,通過函數DigDisplay()實現數碼管位選與段選數據對應輸出,經過掃描和人眼視覺殘留實現6個數碼管顯示123456。在Proteus中仿真得到如下顯示:
2) 按功能鍵實現功能2~5的切換,綜合程序。
#include<reg51.h>
#define GPIO_DIG P1 //段選
#define GPIO_PLACE P2 //位選
#define uchar unsigned char
#define uint unsigned int
sbit key=P3^0;
sbit t3=P3^5;
sbit t2=P3^6;
sbit t1=P3^7;
uint disnum1=0,disnum2=0,disnum3=0,K,T=0,power=0;
unsigned char code DIG_PLACE[6] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf}; //位碼
unsigned char code table[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//段碼0~9
void display(uint liu,uint wu,uint si,uint san,uint er,uint yi);
void timing();
void delay(uint a)
{
unsigned int i,j;
for(j=a;j>0;j--)
for(i=0;i<500;i++);
}
void main()
{
TMOD = 0X01;
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
EA = 1;
ET0 = 1;
TR0 = 1;
IT0=1;
EX0=1;
IT1=1;
EX1=1;
while(1)
{
timing();
display(disnum3/10,disnum3%10,disnum2/10,disnum2%10,disnum1/10,disnum1%10);
}
}
void time1() interrupt 1
{
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
if(power==0) //功能2
{
disnum1++;
if(disnum1==100)
{
disnum1=0;
disnum2++;
if(disnum2==60)
{
disnum2=0;
disnum3++;
if(disnum3==60)
{
disnum3=0;
}
}
}
}
if(power==1) //功能3
{
if(K!=0)
{
disnum1++;
if(disnum1==100)
{
disnum1=0;
disnum2++;
if(disnum2==60)
{
disnum2=0;
disnum3++;
if(disnum3==60)
{
disnum3=0;
}
}
}
}
}
if(power==2) //功能4
{
if(K!=0)
{
if(disnum1==0&&(disnum3!=0||disnum2!=0))
{
disnum1=100;
if(disnum2==0&&disnum3!=0)
{
disnum2=60;
disnum3--;
}
disnum2--;
}
else
if(disnum1!=0||disnum3!=0||disnum2!=0)
disnum1--;
}
}
if(power==3) //功能5
{
T++;
if(T==100)
{
T=0;
if(K!=0)
{
disnum1++;
if(disnum1==60)
{
disnum1=0;
disnum2++;
if(disnum2==60)
{
disnum2=0;
disnum3++;
if(disnum3==60)
{
disnum3=0;
}
}
}
}
}
}
}
void int0(void) interrupt 0 //啟動/暫停鍵
{
K = ~K;
}
void int1(void) interrupt 2 //功能選擇鍵
{
power++;
if(power==4)
{
power=0;
}
disnum1=0;disnum2=0;disnum3=0;K=0;T=0;
}
void display(uint liu,uint wu,uint si,uint san,uint er,uint yi)
{
GPIO_PLACE=~DIG_PLACE[0];
GPIO_DIG=table[liu];
delay(1);
GPIO_PLACE=~DIG_PLACE[1];
GPIO_DIG=table[wu]+0X80;
delay(1);
GPIO_PLACE=~DIG_PLACE[2];
GPIO_DIG=table[si];
delay(1);
GPIO_PLACE=~DIG_PLACE[3];
GPIO_DIG=table[san]+0X80;
delay(1);
GPIO_PLACE=~DIG_PLACE[4];
GPIO_DIG=table[er];
delay(1);
GPIO_PLACE=~DIG_PLACE[5];
GPIO_DIG=table[yi];
delay(1);
}
void timing()
{
if(t3==0)
{
delay(40);
if(t3==0)
{
disnum3++;
if(disnum3==60)
disnum3=0;
}
}
if(t2==0)
{
delay(40);
if(t2==0)
{
disnum2++;
if(disnum2==60)
disnum2=0;
}
}
if(t1==0)
{
delay(40);
if(t1==0)
{
disnum1++;
if(disnum1==100)
disnum3=0;
}
}
}
根據Proteus中所設計的的電路圖,功能實現為:
將8個按鍵從上到下依次命名為key1~key8.key4為功能切換鍵,每按下一次切換一個功能;key3為啟動/暫停鍵,用于秒表的啟動/暫停控制;key6、key7、key8為時間調整鍵,每按一次加一。
功能2:通過定時器計時,每隔10ms,disnum1加一;當disnum1加到100時,disnum1置零,disnum2加一;當disnum2加到60時,disnum2置零,disnum3加一。運用動態掃描的方式在數碼管上依次顯示disnum3、disnum2、disnum1。
功能3:與功能2類似,增加了K取值零與非零的判斷。通過判斷K的取值實現秒表的啟動/暫停功能。
功能4:首先設定MM.SS.XX的時間,通過啟動/暫停鍵控制啟動。設定剛好與秒表功能相反。通過定時器計時,每隔10ms,disnum1減一;當disnum1減到0時,disnum1置100,disnum2減一;當disnum2減到0時,disnum2置60,disnum3減一。運用動態掃描的方式在數碼管上依次顯示disnum3、disnum2、disnum1。
功能5:與秒表的功能實現方式相同,只是在時間上做了調整。
3.3實驗總結
此次實驗在上次中斷實驗基礎上進行,實現時鐘、秒表、倒計時等功能的實現,在實驗的過程中遇到了一個麻煩就是實現倒計時時,每位之間借位時,出現了顯示錯誤,通過在課堂上的測試與調整,成功克服了這一問題。此次實驗學到了很多新知識,過程挺開心的,同時實驗也取得了成功。
四、 ADC/DAC
4.1實驗要求
1. 編制ADC轉換程序,將轉換的數字直接顯示在顯示器上;
2. 編制DAC轉換三角波程序,輸出頻率在10-200Hz;
3. 可按鍵設定ADC轉換程序,量程最大值限制在50-99之間,前面兩位為提示符;
4. 編制電位器調節量程程序,實現2路調節電位器最小到-最大,顯示25-99,前面兩位為提示符;
5. 編制程序用電位器控制三角波的輸出幅度及頻率。一路控制幅度在25-99,另一路頻率在25-99Hz。
4.2程序實現
1)
#include "reg52.h"
#include<intrins.h>
typedef unsigned int uint;
typedef unsigned char uchar;
sbit LSA=P2^0;
sbit LSB=P2^1;
sbit LSC=P2^2;
sbit CLK=P1^4;
sbit ST=P1^2;
sbit EOC=P1^1;
sbit OE=P1^3;
sbit ALE=P1^0;
sbit ADDA=P1^5;
uchar code table[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//顯示0~F的值
uchar ADdisplayflag=0;
uint adcount=0;
uint dpcount=0;
uint dd=0;
uchar zhuanhuanflag=0;
void CLKInit(void)
{
ET0 = 1;
TR0 = 1;
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
}
void ScanInit(void)
{
ET1 = 1;
TR1 = 1;
TH1 = 0x06;
TL1 = 0x06;
}
void delay(uint i)
{
while(i--);
}
void ADdisplay()
{
switch(ADdisplayflag)
{
case 0:
{
P0 = 0x00;
LSA=1;LSB=1;LSC=0;
}break;
case 1:
{
P0 = 0x00;
P0 = table[dd/100];
LSA=0;LSB=1;LSC=0;
}break;
case 2:
{
P0 = 0x00;
P0 = table[dd%100/10];
LSA=1;LSB=0;LSC=0;
}break;
case 3:
{
P0 = 0x00;
P0 = table[dd%10];
LSA=0;LSB=0;LSC=0;
}break;
default : break;
}
ADdisplayflag++;
if(ADdisplayflag == 4)
{
ADdisplayflag = 0;
}
}
void main()
{
ADDA=0;
EA = 1;
TMOD = 0x21;
ScanInit();
CLKInit();
while(1);
}
void CLK50us(void) interrupt 1
{
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
CLK=!CLK;
}
void ScanLed(void) interrupt 3
{
dpcount++;
if(dpcount==4)
{
dpcount=0;
ADdisplay();
}
adcount++;
if(adcount==40)
{
adcount = 0;
ALE=1;
ST=1;
ALE=0;
ST=0;
_nop_();
_nop_();
zhuanhuanflag=1;
}
if(zhuanhuanflag==1)
{
if(EOC==1)
{
OE=1;
dd=P3;
OE=0;
zhuanhuanflag=0;
}
}
}
在程序中,題目要求需要將電壓信號轉換成數字并用數碼管顯示出來,用八位進行采集,采集到的信號轉換成數字后的范圍為0~255,之后需要用數碼管顯示,這里還是采用動態掃描顯示,顯示方式與上一次實驗一致,仿真結果如下:
2)
#include <reg52.h>
void delay(int i)
{
while(i--);
}
void main()
{
P0 = 0;
while(1)
{
while(1)
{
if(P0!=0xFF)
{
P0++;
delay(1);
}
else
break;
}
while(1)
{
if(P0!=0x00)
{
P0--;
delay(1);
}
else
break;
}
}
}
P0從0依次加到255,再從255依次減到0,如此循環反復實現了三角波的輸出,其中三角波的頻率由延時函數delay()來控制,用protuse進行仿真得到如下的仿真波形:
3)
#include "reg52.h" //此文件中定義了單片機的一些特殊功能寄存器
#include<intrins.h>
typedef unsigned int uint; //對數據類型進行聲明定義
typedef unsigned char uchar;
sbit LSA=P2^0;
sbit LSB=P2^1;
sbit LSC=P2^2;
sbit CLK=P1^4;
sbit ST=P1^2;
sbit EOC=P1^1;
sbit OE=P1^3;
sbit ALE=P1^0;
sbit k1 = P2^3;
sbit k2 = P2^4;
sbit k3 = P2^5;
sbit k4 = P2^6;
sbit k5 = P2^7;
uchar code table[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//顯示0~F的值
uint adcount=0,dpcount=0,dd=0,ddcopy=0,dchange=0,dchangecopy=0,shanshuoflag=0;
uchar ADdisplayflag=0,zhuanhuanflag=0,k4flag=0,moduleFlag=0,lc=0,rc=0,ledchoose=0,max=0,min=0,weishu=0;
void CLKInit(void)
{
ET0 = 1;
TR0 = 1;
TH0 = 0xF0;
TL0 = 0xF0;
}
void ScanInit(void)
{
ET1 = 1;
TR1 = 1;
TH1 = 0x06;
TL1 = 0x06;
}
void delay(uint i)
{
while(i--);
}
void ADdisplaya(uchar ledmie)
{
switch(ADdisplayflag)
{
case 0:
{
P0 = 0x00;
if(ledmie==1)
{
P0 = 0x00;
}
else P0 = table[10];
LSA=1;LSB=1;LSC=0;
}break;
case 1:
{
P0 = 0x00;
if(ledmie==2)
{
P0 = 0x00;
}
else P0 = table[13];
LSA=0;LSB=1;LSC=0;
}break;
case 2:
{
P0 = 0x00;
if(ledmie==3)
{
P0 = 0x00;
}
else P0 = table[dchange/10];
LSA=1;LSB=0;LSC=0;
}break;
case 3:
{
P0 = 0x00;
if(ledmie==4)
{
P0 = 0x00;
}
else P0 = table[dchange%10];
LSA=0;LSB=0;LSC=0;
}break;
default : break;
}
ADdisplayflag++;
if(ADdisplayflag == 4)
{
ADdisplayflag = 0;
}
}
void ADdisplayb(uchar ledmie)
{
switch(ADdisplayflag)
{
case 0:
{
P0 = 0x00;
if(ledmie==1)
{
P0 = 0x00;
}
else P0 = table[10];
LSA=1;LSB=1;LSC=0;
}break;
case 1:
{
P0 = 0x00;
if(ledmie==2)
{
P0 = 0x00;
}
else P0 = table[13];
LSA=0;LSB=1;LSC=0;
}break;
case 2:
{
P0 = 0x00;
if(ledmie==3)
{
P0 = 0x00;
}
else P0 = table[dchangecopy/10];
LSA=1;LSB=0;LSC=0;
}break;
case 3:
{
P0 = 0x00;
if(ledmie==4)
{
P0 = 0x00;
}
else P0 = table[dchangecopy%10];
LSA=0;LSB=0;LSC=0;
}break;
default : break;
}
ADdisplayflag++;
if(ADdisplayflag == 4)
{
ADdisplayflag = 0;
}
}
void adc0809()
{
ALE=1;
ST=1;
ALE=0;
ST=0;
_nop_();
_nop_();
while(EOC==0);
OE=1;
dd=P3;
OE=0;
}
void keypress(void)
{
if(k4==0)
{
delay(1000);
if(k4==0)
{
k4flag=~k4flag;
k4flag&=0x01;
if(k4flag==0)
{
moduleFlag = 0;
weishu=3;
}
else if(k4flag==1)
{
moduleFlag = 1;
if(dchange<50||dchange>99)
{
dchangecopy=50;
}
else dchangecopy=dchange;
}
while(!k4);
}
}
if(moduleFlag == 1)
{
if(k1==0)
{
delay(1000);
if(k1==0)
{
if(ledchoose%2)
{
weishu=3;
}
else
{
weishu=4;
}
ledchoose++;
}
while(!k1);
}
if(k2==0)
{
delay(1000);
if(k2==0)
{
if(ledchoose%2)
{
if(dchangecopy>=50&&dchangecopy<99)
{
dchangecopy=dchangecopy+1;
}
}else
{
if(dchangecopy>=50&&dchangecopy<90)
{
dchangecopy=dchangecopy+10;
}
}
}
while(!k2);
}
if(k3==0)
{
delay(1000);
if(k3==0)
{
if(ledchoose%2)
{
if(dchangecopy>50&&dchangecopy<=99)
{
dchangecopy=dchangecopy-1;
}
}else
{
if(dchangecopy>=60&&dchangecopy<=99)
{
dchangecopy=dchangecopy-10;
}
}
}
while(!k3);
}
if(k5==0)
{
delay(1000);
if(k5==0)
{
max=dchangecopy;
moduleFlag=0;
k4flag=0;
}
while(!k5);
}
}
}
void main()
{
EA = 1;
TMOD = 0x22;
ScanInit();
CLKInit();
max=99;
min=0;
weishu=3;
while(1)
{
keypress();
}
}
void CLK500us(void) interrupt 1
{
CLK=~CLK;
}
void ScanLed(void) interrupt 3
{
dpcount++;
adcount++;
shanshuoflag++;
if(moduleFlag==0)
{
if(dpcount==4)
{
dpcount=0;
ADdisplaya(0);
}
}else if(moduleFlag==1)
{
if(dpcount>4||shanshuoflag>3000)
{
dpcount=0;
shanshuoflag=0;
}
if(dpcount==4)
{
dpcount = 0;
if(shanshuoflag<1000)
ADdisplayb(0);
else if(shanshuoflag>=1000&&shanshuoflag<=3000)
{
ADdisplayb(weishu);
if(shanshuoflag==3000)
shanshuoflag=0;
}
}
}
if(adcount==40)
{
adcount = 0;
ALE=1;
ST=1;
ALE=0;
ST=0;
_nop_();
_nop_();
zhuanhuanflag=1;
}
if(zhuanhuanflag==1)
{
if(EOC==1)
{
OE=1;
dd=P3;
OE=0;
dchange=min+((max-min)*(dd+1))/256;
zhuanhuanflag=0;
}
}
}//全局時鐘變量計時
采集到的信號轉換成數字后的范圍為0~255,現如今要將其用(0~50)~(0~99)來表示,故在此需要將所采集的數據進行轉換,這里只需要用到簡單的數學公式就可以完成。轉換好之后需要用數碼管顯示,這里還是采用動態顯示。仿真結果如下:
4) 在程序3)的基礎上,增加模擬電壓輸入端的數據選擇即可,按鍵只運用一個數據選擇切換鍵。仿真結果如下:
4.3實驗總結
對于本次實驗,個人挺難的,課下花費了好長時間才勉強完成。數碼管顯示數據已經在之前實驗中做過了,數據的采集與轉換是本次實驗的難點,經過多次測試才克服這一難題。在本次實驗中收獲頗多。
五、通信實驗
5.1實驗要求
1) 設計電路圖
要求采用2個單片機,左邊單片機控制2路模擬電壓的采集和1個18B20溫度傳感器的采集,右邊的單片機控制4位LED數碼管的顯示,6位按鍵控制,DAC輸出,2個單片機通過串口通信。
2) 實現功能
1、 按鍵F1,實現數字時鐘功能。可以設定起始時間,顯示格式 時:分 (HH.MM)
2、 按鍵F2,實現顯示溫度值,顯示格式 t XX.X
3、 按鍵F3, 實現顯示第1路ADC 顯示格式 F XXX, 實現顯示第2路ADC 顯示格式 L xxx
4、 按鍵F4, 實現時鐘與溫度交替顯示 ,交替間隔10秒。切換時閃爍2秒。
5、 由2路ADC控制DAC輸出三角波
5.2程序實現
1) 數字時鐘功能
#include<reg51.h>
#define GPIO_DIG P1 //段選
#define GPIO_PLACE P2 //位選
#define uchar unsigned char
#define uint unsigned int
sbit key=P3^0;
uint disnum1=0,disnum2=0,disnum3=0,K;
unsigned char code DIG_PLACE[6] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
unsigned char code table[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
void display(uint si,uint san,uint er,uint yi);
void delay(uint a)
{
unsigned int i,j;
for(j=a;j>0;j--)
for(i=0;i<500;i++);
}
void main()
{
TMOD = 0X01;
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
EA = 1;
ET0 = 1;
TR0 = 1;
IT0=1;
EX0=1;
while(1)
{
display(disnum3/10,disnum3%10,disnum2/10,disnum2%10);
}
}
void time1() interrupt 1
{
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
if(K!=0){
disnum1++;
if(disnum1==100)
{
disnum1=0;
disnum2++;
if(disnum2==60)
{
disnum2=0;
disnum3++;
if(disnum3==60)
{
disnum3=0;
}
}
}
}
}
void int0(void) interrupt 0
{
K = ~K;
}
void display(uint si,uint san,uint er,uint yi)
{
GPIO_PLACE=~DIG_PLACE[2];
GPIO_DIG=table[si];
delay(1);
GPIO_PLACE=~DIG_PLACE[3];
GPIO_DIG=table[san]+0X80;
delay(1);
GPIO_PLACE=~DIG_PLACE[4];
GPIO_DIG=table[er];
delay(1);
GPIO_PLACE=~DIG_PLACE[5];
GPIO_DIG=table[yi];
delay(1);
}
通過定時器計時,每隔10ms,disnum1加一;當disnum1加到100時,disnum1置零,disnum2加一;當disnum2加到60時,disnum2置零,disnum3加一。運用動態掃描的方式在數碼管上依次顯示disnum3、disnum2。仿真結果如下:
2) 溫度采集顯示
a) 溫度采集與數據發送程序
#include<reg51.h>
#include<intrins.h>
#include <absacc.h>
#include <math.h>
typedef unsigned int uint;
typedef unsigned char uchar;
sbit CLK=P1^4;
sbit ST=P1^2;
sbit EOC=P1^1;
sbit OE=P1^3;
sbit ALE=P1^0;
sbit DQ=P1^0;
unsigned char TL;
unsigned char TH;
unsigned char TN;
unsigned int TD;
unsigned char time_DS18B20;
uint adcount=0;
uint dpcount=0;
uint dd=0;
uchar zhuanhuanflag=0;
void UsartConfiguration();
unsigned char ReadOneChar();
void WriteOneChar(unsigned char dat);
void ReadyReadTemp(void);
bit Init_DS18B20(void);
void CLKInit(void)
{
ET0 = 1;
TR0 = 1;
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
}
void CLK50us(void) interrupt 1
{
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
CLK=!CLK;
}
void ScanLed(void) interrupt 2
{
adcount++;
if(adcount==40)
{
adcount = 0;
ALE=1;
ST=1;
ALE=0;
ST=0;
_nop_();
_nop_();
zhuanhuanflag=1;
}
if(zhuanhuanflag==1)
{
if(EOC==1)
{
OE=1;
dd=P2;
OE=0;
zhuanhuanflag=0;
}
}
}
void UsartConfiguration()
{
SCON=0X40; //設置為工作方式1
TMOD=0X20; //設置計數器工作方式2
PCON=0X00; //波特率不加倍
TL1=0XE8; //計數器初始值設置,注意波特率是2400的
TH1=0XE8;
ET1=1;
TR1=1; //打開計數器
EA=1; //打開總中斷
ES=1; //打開接收中斷
}
void ReadyReadTemp(void)
{
Init_DS18B20();
WriteOneChar(0xCC);
WriteOneChar(0x44);
for(time_DS18B20=0;time_DS18B20<100;time_DS18B20++);
Init_DS18B20();
WriteOneChar(0xCC);
WriteOneChar(0xBE);
}
bit Init_DS18B20(void)
{
bit flag_DS18B20;
DQ = 1;
for(time_DS18B20=0;time_DS18B20<2;time_DS18B20++);
DQ = 0;
for(time_DS18B20=0;time_DS18B20<200;time_DS18B20++);
DQ = 1;
for(time_DS18B20=0;time_DS18B20<10;time_DS18B20++);
flag_DS18B20=DQ;
for(time_DS18B20=0;time_DS18B20<200;time_DS18B20++);
return (flag_DS18B20);
}
unsigned char ReadOneChar( )
{
unsigned char i=0;
unsigned char dat;
for (i=0;i<8;i++)
{
DQ =1;
_nop_();
DQ = 0;
dat>>=1;
_nop_();
DQ = 1;
for(time_DS18B20=0;time_DS18B20<3;time_DS18B20++);
if(DQ==1)
dat|=0x80;
else
dat|=0x00;
for(time_DS18B20=0;time_DS18B20<8;time_DS18B20++)
;
}
return(dat);
}
void WriteOneChar(unsigned char dat)
{
unsigned char i=0;
for (i=0; i<8; i++)
{
DQ =1;
_nop_();
DQ=0;
DQ=dat&0x01;
for(time_DS18B20=0;time_DS18B20<10;time_DS18B20++);
DQ=1;
for(time_DS18B20=0;time_DS18B20<1;time_DS18B20++);
dat>>=1;
}
for(time_DS18B20=0;time_DS18B20<4;time_DS18B20++);
}
void main(void)
{
UsartConfiguration();
CLKInit();
while(1)
{
ReadyReadTemp();
TL=ReadOneChar();
TH=ReadOneChar();
if(TH>=8)
{
TH=~TH;
TL=~TL;
TL=TL+1;
if(TL==0)
TH+=1;
TN=TH*16+TL/16;
TD=(TL%16)*63;
dd=TN*10+TD;
}
else
{
TN=TH*16+TL/16;
TD=(TL%16)*62;
dd=TN*10+TD;
}
TI=0;
SBUF=dd;
while(!TI);
}
}
b) 數據接收與數碼管顯示程序
#include "reg52.h"
#include<intrins.h>
typedef unsigned int uint;
typedef unsigned char uchar;
sbit LSA=P2^0;
sbit LSB=P2^1;
sbit LSC=P2^2;
uint dd=0;
uchar code table[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//顯示0~F的值
uchar ADdisplayflag=0;
uint CLK=0;
void ADdisplay();
void CLKInit(void);
void UsartConfiguration();
void main()
{
UsartConfiguration();
CLKInit();
while(1)
{
RI=0;
dd=SBUF;
while(!RI)
ADdisplay();
}
}
void CLKInit(void)
{
ET0 = 1;
TR0 = 1;
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
}
void ADdisplay()
{
switch(ADdisplayflag)
{
case 0:
{
P0 = 0x00;
LSA=1;LSB=1;LSC=0;
}break;
case 1:
{
P0 = 0x00;
P0 = table[dd/100];
LSA=0;LSB=1;LSC=0;
}break;
case 2:
{
P0 = 0x00;
P0 = table[dd%100/10]+0x80;
LSA=1;LSB=0;LSC=0;
}break;
case 3:
{
P0 = 0x00;
P0 = table[dd%10];
LSA=0;LSB=0;LSC=0;
}break;
default : break;
}
ADdisplayflag++;
if(ADdisplayflag == 4)
{
ADdisplayflag = 0;
}
}
void CLK50us(void) interrupt 1
{
TH0 = (65536-25)/256;
TL0 = (65536-25)%256;
CLK=!CLK;
}
void UsartConfiguration()
{
SCON=0X50; //設置為工作方式1
TMOD=0X20; //設置計數器工作方式2
PCON=0X00; //波特率不加倍
TL1=0XE8; //計數器初始值設置,注意波特率是2400的
TH1=0XE8;
TR1=1; //打開計數器
EA=1; //打開總中斷
ES=1; //打開接收中斷
}
左邊的單片機通過P1.0口對溫度傳感器DS18B20進行溫度數據采集,然后通過串口通信原理將采集到的數據發送到右端單片機;右端單片機接收到數據后,將數據進行一定的數學處理,然后通過數碼管動態掃描的原理將數據顯示在數碼管上。仿真結果如下:
5.3實驗總結
本次實驗較為困難,在課前已經花了很長時間去理解串口通信,等到了實驗課上的時候,依然比較模糊。之后經過和同學的討論,把串口通信弄明白了。但是又卡在了溫度傳感器DS18B20上,經過上網查找資料,最終才勉強做出溫度的采集顯示。
實驗總結與感想
首先,我對本次實驗課的成功倍感珍惜。在實驗中,我很深刻的了解到了理論和實踐的不同之處,在書本中學到、理解的知識內容并不會很容易的運用在實際當中。有很多沒有遇到過的實際問題會出現,這時就需要我們去深入探究,從而將問題一點點的解決,最后問題才能迎刃而解。在這次實驗中80C51單片機的編程有了更深入的了解,對單片機制作小作品方面的知識也更加的豐富。
同時也花了較多的時間對以前所學的東西進行了溫故,在以前的學習中沒有具體掌握的知識,又進行了學習,鞏固。如硬件設計是很重要的,硬件設計的方式方法很多,并不是一塵不變的,它反映了你的邏輯思維和創新能力,所以我覺得它才是一個設計的靈魂所在。
在實驗過程中,我碰到了很多問題,在同學、老師的幫助下,才能及時順利地解決,讓我感受到集體、友誼的力量。
通過這學期的實驗不但讓我了解了更多電子方面的知識,同時也提高了自己的編程的能力。可以說這學期的實驗不僅體現掌握的知識,而且是學習更多知識的機會,提高自己各方面的知識。可以在今后的學習中,充分發揮自己所學的知識。本次實驗中,我學會什么叫堅持不懈、努力奮斗,在今后的學習、工作中一定要將在這次設計學到的品質堅持下去。同時,對給過我幫助的老師和同學再次表示由衷的感謝!
|