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