|
單片機原理與應用
課程名稱: 單片機原理與應用
設計題目: LCD電子鐘
院 系: 電子信息工程學院
班 級: 自動化0706
設 計 者: 全宏宇
指導教師:
一,設計目標
LCD顯示電子鐘的基本功能
1,實現(xiàn)時鐘功能;
2,實現(xiàn)鬧鈴功能;
3,實現(xiàn)秒表功能;
4,具有一定的計時精度。
LCD顯示電子鐘的基本要求
1,掌握單片機開發(fā)編程設計的基本流程;
2,了解Keil及Proteus軟件的基本使用;
3,了解LCD的基本使用;
4,學習單片機硬件制作。
二,具體實現(xiàn)
1,軟件平臺
1)Keil編程
Keil 的開發(fā)工具的使用的基本過程:
○1創(chuàng)建 C 或匯編語言的源程序;
○2編譯或匯編源文件;
○3糾正源文件中的錯誤;
○4從編譯器和匯編器連接目標文件;
○5測試連接的應用程序。
2)Proteus仿真
Proteus軟件不僅具有其它EDA工具軟件的仿真功能,還能仿真單片機及外圍器件。它是目前最好的仿真單片機及外圍器件的工具。在編譯方面,它也支持IAR,Keil,PLAB等多種編譯器。
2,硬件開發(fā)平臺
JD51開發(fā)板的基本資料
○1 LED 電路,8 只獨立LED 發(fā)光管,可做指示或各種閃爍效果用。
○2 數(shù)碼管電路,4只共陽一體8 段數(shù)碼顯示管,可實現(xiàn)各種數(shù)據(jù)顯示,如計數(shù)、時鐘等。
○3蜂鳴器電路,可用于設計各種提示音、演奏音樂等。
○4鍵盤電路,學習按鍵控制相關(guān)編程。
○5 LCD 顯示電路,編程控制LCD 顯示。
○6串口電路,學習編程實現(xiàn)JD51 和PC 或其他符合該通信協(xié)議的電路之間的通信。
○7紅外電路,通過選配的紅外遙控器,學習紅外解碼并可實現(xiàn)紅外遙控JD51。
○8 溫度模塊電路,采用一線式溫度傳感器實現(xiàn)溫度的采集并可顯示在數(shù)碼管或者LCD 上,通過溫度數(shù)據(jù)處理便可實現(xiàn)溫度控制器功能。
○9除了以上提到的可編程電路本學習板還有一些常用的不可編程電路,包括電源電路、復位電路、晶振電路等。
本次LCD電子鐘實驗用到其中的蜂鳴器,按鍵,LCD顯示接口。
3,總體設計
1) 基本資源的使用
本次實驗采用了89C52型單片機,1602LCD液晶顯示屏,蜂鳴器。為了實現(xiàn)時鐘,定時,鬧鐘,秒表的功能,用到了單片機的外部中斷,計時器中斷,及
I/O端口.
資源 功能
外部中斷 外部中斷INT1 模式選擇(時鐘,調(diào)時,秒表,鬧鐘)
外部中斷INT0 秒表的暫停,清零
定時器 定時器T0 秒表,時鐘定時基準
定時器T1 音樂鬧鈴的音符產(chǎn)生
I/O端口 P1,P2 LCD接口,蜂鳴器
2)軟件仿真
(1)程序設計的一些關(guān)鍵問題(具體參照后面的程序清單)
○1建立Keil工程時,注意對程序編譯環(huán)境進行設置;
設置內(nèi)容包括器件,頻率,產(chǎn)生hex文件等。
○2LCD管腳及控制字;
LCD管腳的定義
控制字
1602液晶模塊的讀寫操作,屏幕和光標的操作都是通過指令編程來實現(xiàn)的。
指令1:清顯示,指令碼01H,光標復位到地址00H位置
指令2:光標復位,光標返回到地址00H
指令3:光標和顯示位置設置I/D,光標移動方向,高電平右移,低電平左移,S:屏幕上所有文字是否左移或右移,高電平表示有效,低電平表示無效。
指令4:顯示開關(guān)控制。D:控制整體的顯示開與關(guān),高電平表示開顯示,低電平表示關(guān)顯示。C:控制光標的開與關(guān),高電平表示有光標,低電平表示無光標 B:控制光標是否閃爍,高電平閃爍,低電平不閃爍。
指令5:光標或顯示移位 S/C :高電平時顯示移動的文字,低電平時移動光標
指令6:功能設置命令 DL:高電平時為4位總線,低電平時為8位總線 N:低電平時為單行顯示,高電平時為雙行顯示,F(xiàn):低電平時顯示5X7的點陣字符,高電平時顯示5X10的顯示字符。
指令7:字符發(fā)生器RAM地址設置。
指令8:DDRAM地址設置。
指令9:讀忙信號和光標地址 BF:忙標志位,高電平表示忙,此時模塊不能接收命令或數(shù)據(jù),如果為低電平表示不忙。
○3LCD實現(xiàn)滾動顯示;
方法一:采用LCD1602控制命令,顯示后畫面移動一個字符;
方法二:循環(huán)使用顯示不同位置,形成滾動效果。
○5秒表
主要是產(chǎn)生計時基準0.01秒,有了基準之后需要解決的問題是如何實現(xiàn)有效的暫停,清零(達到精準動作),采用中斷是很不錯的結(jié)果。
○6鬧鈴
主要是如何實現(xiàn)鬧鈴關(guān)閉的邏輯,if(f&&g)語句可以實現(xiàn)這一動作。
○7提高計時精度
本程序除了調(diào)時模式外,其他模式的時候時鐘都在跑動,提高了因模式轉(zhuǎn)換而導致的時間精度降低。
(2)Proteus仿真
在Proteus中畫出如下電路圖,添加hex文件,進行虛擬仿真。
2) 開發(fā)板運行調(diào)試
在JD51板上燒制程序,根據(jù)實際的效果,對中斷的反應時間,顯示效果做出了調(diào)整,整體效果不錯,只是音樂的效果不佳,主要是由于如果將蜂鳴器換成揚聲器效果會好些,還可以在程序中增加節(jié)拍來增加音樂效果。
4,硬件制作
根據(jù)仿真電路圖購買元件進行焊接,下載程序效果后,液晶顯示器顯示亂碼,檢查電路后已無連接錯誤,但功能依然沒有實現(xiàn),相關(guān)檢查在進行中。
三,設計體會
本次課程設計系統(tǒng)地學習了單片編程開發(fā)及實際電路的制作。學習了Keil的C語言編程的基本知識,Proteus仿真軟件的模擬仿真,JD51開發(fā)實驗板的使用,本次課設,時間了較多的其他案例,比較全面的掌握了單片機的運營,包括編程,電路,LCD器件等,在知識上有了很多的長進。
本次課程設計還有很多其他收獲,鍛煉了自學的能力,特別是動手實踐方面的東西,讓我深深感受到理論與實際的差距,實際當中會有一些小的問題,這些問題是意想不到的,需要去思考,去解決,這是很鍛煉人的。
四,程序
#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define ulong unsigned long
#define uint unsigned int
uchar idata welcome[16]={" clock "}; //定義第一行顯示數(shù)組
uchar idata lwelcome[16]={" clock "}; //定義第一行顯示數(shù)組
uchar idata llwelcome[16]={" clock "}; //定義第一行顯示數(shù)組
uchar idata lllwelcome[16]={" clock "}; //定義第一行顯示數(shù)組
uchar idata sslcdtimer[16]={" 00:00:00 "};
uchar idata tsslcdtimer[16]={" 00:00:00 "};
uchar idata lcdtimer[16]={" 00:00:00 "}; //定義第二行顯示數(shù)組
uchar idata mlcdtimer[16]={" 00: : "}; //定義第二行顯示數(shù)組
uchar idata hlcdtimer[16]={" :00: "}; //定義第二行顯示數(shù)組
uchar idata setlcdtimer[16]={" 00:01:00 "};
uchar timecount;
uchar temps;
bit timeflag;
bit sstimeflag;
sbit ring=P2^0;//蜂鳴鬧鈴
sbit e=P2^7;//LCD顯示器E
sbit rw=P2^6;//LCD顯示器RW
sbit rs=P2^5;//LCD顯示器RS
sbit P10=P1^0; //測試用閃燈
sbit Key=P3^2;//秒表暫停,清零
sbit Key0=P3^3;//調(diào)時,設定鬧鐘時間,秒表選擇
sbit Key1=P3^4;//調(diào)時,設定鬧鐘時間改變
uchar th1_f; //在中斷中裝載的T0的值高8位
uchar tl1_f; //在中斷中裝載的T0的值低8位
uint m=0;
uint n=0;
ulong i;
uint f=0,g=1;
uint j,k;
uchar code freq[3][14]=
{
{0xF2,0x42,
0xF3,0xC1,
0xF5,0x17,
0xF5,0xB6,
0xF6,0xD0,
0xF7,0xD1,
0xF8,0xB6},
{0xF9,0x21,
0xF9,0xE0,
0xFA,0x8B,
0xFA,0xD7,
0xFB,0x68,
0xFB,0xE8,
0xFC,0x5B },
{0xFC,0x8D,
0xFC,0xEE,
0xFD,0x44,
0xFD,0x6D,
0xFD,0xB4,
0xFD,0xF4,
0xFE,0x2D},
}; //音樂頻率表,低音,中音,高音,系統(tǒng)振蕩頻率11.0592MHz
uint code song1[]={26,25,23,25,31,26,25,26,23,25,26,25,23,22,21,16,25,23,22,22,23,23,23,26,23,22,21,25,23,22,21,16,21,15,26,25,23,25,31,26,25,26,23,25,26,25,23,22,21,16,25,23,22,22,23,23,23,26,23,22,21,25,23,22,21,16,21,15};
//編制音樂
//**************************************************
//軟件延時函數(shù)(系統(tǒng)晶振為11.0592MHz),延時時間約為40μs
//入口條件:無符號整型變量del1,最終的延時時間為(del1*40)μs
//**************************************************
void delay(uint del1)
{
uchar del2;
for(;del1>0;del1--)
for(del2=10;del2>0;del2--);
}
//**************************************************
//液晶顯示器判忙函數(shù)
//**************************************************
void busy()
{
uchar temp;
temp=0x00;
rs=0;
rw=1;
while((temp&0x80)==0x80)
{
P0=0xff;
e=1;
temp=P0;
e=0;
}
}
//**************************************************
//向液晶顯示器寫命令函數(shù)
//**************************************************
void WR_Com(uchar temp)
{
busy();
rs=0;
rw=0;
P0=temp;
e=1;
e=0;
}
//**************************************************
//向液晶顯示器寫數(shù)據(jù)函數(shù)
//**************************************************
void WR_Data(uchar num)
{
busy();
rs=1;
rw=0;
P0=num;
e=1;
e=0;
}
//**************************************************
//向液晶顯示器寫入顯示數(shù)據(jù)函數(shù)
//入口條件:液晶顯示器行首地址(指示第一行還是第二行)和待顯示數(shù)組的首地址
//**************************************************
//void disp_lcdchar(uchar addr,uchar temp1)
//{
// WR_Com(addr);
// delay(100);
// WR_Data(temp1);
//}
void disp_lcd(uchar addr,uchar *temp1)
{
uchar i;
WR_Com(addr);
delay(100);
for(i=0;i<16;i++)
{
WR_Data(temp1[i]);
delay(100);
}
}
//**************************************************
//液晶顯示器初始化函數(shù)
//**************************************************
void lcd_ini()
{
char i;
for(i=3;i>0;i--)
{
P0=0x30;
rs=0;
rw=0;
e=1;
e=0;
delay(100);
}
P0=0x38;
rs=0;
rw=0;
e=1;
e=0;
delay(100);
}
//**************************************************
//液晶顯示器復位函數(shù)
//**************************************************
void lcd_Reset()
{
WR_Com(0x01);
delay(100);
WR_Com(0x06);
delay(100);
WR_Com(0x0c);
delay(100);
}
//**************************************************
//外部中斷函數(shù),秒表暫停,清零
//**************************************************
void modess() interrupt 0 using 0
{
delay(10000);
n++;
if(n==3)
n=0;
}
//**************************************************
//外部中斷函數(shù),模式選擇
//**************************************************
void modepp() interrupt 2 using 1
{
delay(30000);
m++;
if(m==6)
n=0;
if(m==7)
m=0;
}
//**************************************************
//定時/計數(shù)器中斷函數(shù),1S及0.01S
//**************************************************
void timer0() interrupt 1 using 2
{
TH0=0xdc;
TL0=0x23;
sstimeflag=1;
if(--timecount==0)
{
timecount=100;
timeflag=1;
}
}
//**************************************************
//定時/計數(shù)器中斷函數(shù),裝入音樂頻率計數(shù)初值
//**************************************************
void timer1() interrupt 3 using 3
{
TL1=tl1_f;TH0=th1_f; //調(diào)入預定時值
ring=!ring; //取反音樂輸出IO
}
//**************************************************
//主函數(shù)
//**************************************************
void main()
{
P10=1;
lcd_ini();
lcd_Reset();
/* for(i=0;i<10;i++)
{
temps=welcome[i];
disp_lcdchar(0x80+1,temps); //LCD 單字符從左到右顯示
delay(10000);
}*/
disp_lcd(0x80,welcome);
delay(10000);
disp_lcd(0x80,lwelcome);
delay(10000);
disp_lcd(0x80,llwelcome);
delay(10000);
disp_lcd(0x80,lllwelcome);
delay(10000);
disp_lcd(0x80,llwelcome);
delay(10000);
disp_lcd(0x80,lwelcome);
delay(10000);
disp_lcd(0x80,welcome); //LCD滾動顯示
delay(10000);
disp_lcd(0xc0,lcdtimer);
P10=0;
TMOD=0x11;
TH0=0xdc;
TL0=0x23;
TR0=1;
IE=0x86;
timeflag=0; //1秒計時標志
sstimeflag=0; //0.01秒計時標志
timecount=100;
while(1)
{
while(m==6)//秒表
{
IE=0x87;
if(n==0) //秒表跑動
{
if(sstimeflag==1)
{
sstimeflag=0;
sslcdtimer[11]+=1;
if(sslcdtimer[11]>=0x3a)
{
sslcdtimer[11]=0x30;
sslcdtimer[10]+=1;
if(sslcdtimer[10]>=0x3a)
{
sslcdtimer[10]=0x30;
sslcdtimer[8]+=1;
if(sslcdtimer[8]>=0x3a)
{
sslcdtimer[8]=0x30;
sslcdtimer[7]+=1;
if(sslcdtimer[7]>=0x36)
{
sslcdtimer[7]=0x30;
}
}
}
}
disp_lcd(0xc0,sslcdtimer);
}
if(n==1)//秒表暫停
{
disp_lcd(0xc0,sslcdtimer);
}
if(n>=2)//秒表清零
{
disp_lcd(0xc0,tsslcdtimer);
for(i=0;i<16;i++)
sslcdtimer[i]=tsslcdtimer[i];
}
}
}
while(m==0||m>=3) //時鐘程序,只要不是調(diào)時,就一直運行
{
IE=0x86;
if(timeflag==1)
{
timeflag=0;
lcdtimer[11]+=1;
if(lcdtimer[11]>=0x3a)
{
lcdtimer[11]=0x30;
lcdtimer[10]+=1;
if(lcdtimer[10]>=0x36)
{
lcdtimer[10]=0x30;
lcdtimer[8]+=1;
if(lcdtimer[8]>=0x3a)
{
lcdtimer[8]=0x30;
lcdtimer[7]+=1;
if(lcdtimer[7]>=0x36)
{
lcdtimer[7]=0x30;
lcdtimer[5]+=1;
if(lcdtimer[5]>=0x3a)
{
lcdtimer[5]=0x30;
lcdtimer[4]+=1;
if(lcdtimer[4]>=0x33&&lcdtimer[5]>=0x35)
{
lcdtimer[5]=0x30;
lcdtimer[4]=0x30;
}
}
}
}
}
}
}
if(m==0)
{
disp_lcd(0xc0,lcdtimer);
if(!Key1)//關(guān)掉鬧鐘
{
g=0;
}
if(lcdtimer[8]==setlcdtimer[8]&&lcdtimer[7]==setlcdtimer[7]&&lcdtimer[5]==setlcdtimer[5]&&lcdtimer[4]==setlcdtimer[4])
f=1; //開啟鬧鐘 ,播放音樂
if(f&&g)
{
/*ring=1;
delay(10000);
ring=0;
f=0; */
IE=0x8e;
j=song1[i]/10-1;
k=2*(song1[i]%10-1);
th1_f=freq[j][k]; //取出對應的定時值送給T0
tl1_f=freq[j][++k];
TR1=1;
for(n=0;n<50000;n++); //音符間延時
i++;
TR1=0;
f=0;
}
}
}
while(m==1) //調(diào)時分
{
if(!Key1)
{
delay(10000);
timeflag=0;
lcdtimer[8]+=1;
if(lcdtimer[8]>=0x3a)
{
lcdtimer[8]=0x30;
lcdtimer[7]+=1;
if(lcdtimer[7]>=0x36)
{
lcdtimer[7]=0x30;
}
}
}
disp_lcd(0xc0,lcdtimer);
delay(8000);
mlcdtimer[11]=lcdtimer[11];
mlcdtimer[10]=lcdtimer[10];
mlcdtimer[5]=lcdtimer[5];
mlcdtimer[4]=lcdtimer[4];
disp_lcd(0xc0,mlcdtimer);
}
while(m==2) //調(diào)時時
{
if(!Key1)
{
delay(10000);
lcdtimer[5]+=1;
if(lcdtimer[5]>=0x3a)
{
lcdtimer[5]=0x30;
lcdtimer[4]+=1;
}
if(lcdtimer[4]==0x32&&lcdtimer[5]>=0x34)
{
lcdtimer[5]=0x30;
lcdtimer[4]=0x30;
}
}
disp_lcd(0xc0,lcdtimer);
delay(8000);
hlcdtimer[11]=lcdtimer[11];
hlcdtimer[10]=lcdtimer[10];
hlcdtimer[8]=lcdtimer[8];
hlcdtimer[7]=lcdtimer[7];
disp_lcd(0xc0,hlcdtimer);
}
while(m==3)//顯示鬧鈴時間
{
if(!Key1)
{
delay(10000);
}
disp_lcd(0xc0,setlcdtimer);
}
while(m==4) //調(diào)鬧鈴分
{
if(!Key1)
{
delay(10000);
setlcdtimer[8]+=1;
if(setlcdtimer[8]>=0x3a)
{
setlcdtimer[8]=0x30;
setlcdtimer[7]+=1;
if(setlcdtimer[7]>=0x36)
{
setlcdtimer[7]=0x30;
}
}
}
disp_lcd(0xc0,setlcdtimer);
delay(8000);
mlcdtimer[11]=setlcdtimer[11];
mlcdtimer[10]=setlcdtimer[10];
mlcdtimer[5]=setlcdtimer[5];
mlcdtimer[4]=setlcdtimer[4];
disp_lcd(0xc0,mlcdtimer);
}
while(m==5)//調(diào)鬧鈴時
{
if(!Key1)
{
delay(10000);
setlcdtimer[5]+=1;
if(setlcdtimer[5]>=0x3a)
{
setlcdtimer[5]=0x30;
setlcdtimer[4]+=1;
}
if(setlcdtimer[4]==0x32&&setlcdtimer[5]>=0x34)
{
setlcdtimer[5]=0x30;
setlcdtimer[4]=0x30;
}
}
disp_lcd(0xc0,setlcdtimer);
delay(8000);
hlcdtimer[11]=setlcdtimer[11];
hlcdtimer[10]=setlcdtimer[10];
hlcdtimer[8]=setlcdtimer[8];
hlcdtimer[7]=setlcdtimer[7];
disp_lcd(0xc0,hlcdtimer);
}
}
}
|
|