C51微控制器利用LCD1602設計時鐘
-設計思路 -知識積累 -編寫程式碼 -總結
1. 設計思路 第一行顯示年月日,第二行顯示時間 上電之後計時開始,K2控制分秒設定,按第一次計時停止秒針閃爍,按第二次分針閃爍,按第三次計時繼續,K3,K4分別控制加減(只有在按鍵K2按下後生效),在計時過程中可以實現整分報時,K1轉換成60秒倒計時
2. 知識積累
- LCD1602
(1)LCD1602介面訊號說明
VSS:電源地訊號引腳; VDD:電源訊號引腳; VEE:液晶對比度調節引腳,接0~5V以調節液晶的顯示對比度; RS:暫存器選擇引腳: RS = 1時為資料暫存器; RS = 0時為指令暫存器; RW:讀寫選擇引腳: RW = 1時,選擇讀操作; RW = 0時,選擇寫操作; E
(2)LCD1602的基本操作分為四種:
1)讀狀態:輸入RS=0,RW=1,E=高脈衝。輸出:D0—D7為狀態字。 2)讀資料:輸入RS=1,RW=1,E=高脈衝。輸出:D0—D7為資料。 3)寫命令:輸入RS=0,RW=0,E=高脈衝。輸出:無。 4)寫資料:輸入RS=1,RW=0,E=高脈衝。輸出:無。
(3)初始化設定
- 定時器
3.編寫程式碼
#include<reg52.h> #define uchar unsigned char #define uint unsigned int sbit LCD_RS=P2^6; sbit LCD_RW=P2^5; sbit LCD_EN=P2^7; sbit Key0=P3^1; //轉換模式 sbit Key1=P3^0; //設定分秒 sbit Key2=P3^2; //分秒加 sbit Key3=P3^3; //分秒減 sbit beep=P1^5; char code line3[]=" 2018-11-6"; char code line4[]=" 00:00"; uchar flag1=1,flag2=0; char sec=0,min=0,count=0,i; uchar sec_shi,sec_ge,min_shi,min_ge; void delay(int n) { int i,j; for(i=0;i<n;i++) for(j=0;j<110;j++); } void W_LCD_Com(uchar com) { LCD_EN=0; LCD_RS=0; LCD_RW=0; P0=com; delay(1); LCD_EN=1; delay(1); LCD_EN=0; } void W_LCD_Dat(uchar Dat) { LCD_EN=0; LCD_RS=1; LCD_RW=0; P0=Dat; delay(1); LCD_EN=1; delay(1); LCD_EN=0; } void LCD_Init() { W_LCD_Com(0x38); delay(5); W_LCD_Com(0x38); delay(5); W_LCD_Com(0x38); W_LCD_Com(0x38); W_LCD_Com(0x08); W_LCD_Com(0x01); W_LCD_Com(0x06); W_LCD_Com(0x0c); W_LCD_Com(0x80); for(i=0;i<12;i++) { W_LCD_Dat(line3[i]); delay(5); } W_LCD_Com(0x80+0x40); for(i=0;i<10;i++) { W_LCD_Dat(line4[i]); delay(5); } } void Time_Init() { TMOD=0x01; TH0=(65536-50000)/256; TL0=(65536-50000)%256; EA=1; ET0=1; TR0=1; } void display() //顯示 { sec_shi=sec/10; sec_ge=sec%10; W_LCD_Com(0x80+0x48); //+48,是因為只能顯示ASCII碼 W_LCD_Dat(sec_shi+48); W_LCD_Dat(sec_ge+48); delay(1); min_shi=min/10; min_ge=min%10; W_LCD_Com(0x80+0x45); W_LCD_Dat(min_shi+48); delay(1); W_LCD_Dat(min_ge+48); delay(1); } void read_key() //按鍵控制 { if(Key1==0) { delay(5); if(Key1==0) { flag2++; while(!Key1); if(flag2==1) { TR0=0; // 計時關閉 W_LCD_Com(0x80+0x49); W_LCD_Com(0x0f); } if(flag2==2) { W_LCD_Com(0x80+0x46); } if(flag2==3) { flag2=0; W_LCD_Com(0x0c); TR0=1; } } } if(flag2!=0) { if(Key2==0) { delay(5); if(Key2==0) { while(!Key2); if(flag2==1) { sec++; sec%=60; display(); W_LCD_Com(0x80+0x49); } if(flag2==2) { min++; min%=60; display(); W_LCD_Com(0x80+0x46); } } } if(Key3==0) { delay(5); if(Key3==0) { while(!Key3); if(flag2==1) { sec--; if(sec==-1) sec=59; display(); W_LCD_Com(0x80+0x49); } if(flag2==2) { min--; if(min==-1) min=59; display(); W_LCD_Com(0x80+0x46); } } } } } void main() { LCD_Init(); Time_Init(); while(1) { read_key(); if(count==20) { count=0; sec++; display(); if(sec==59) { sec=0; beep=~beep; delay(200); min++; if(min==60) { min=0; } } } if(Key0==0) { delay(5); if(Key0==0) { while(!Key0) sec=0;min=1; display(); delay(1000); for(sec=59;sec>-1;sec--) { min=0; display(); delay(1000); } if(sec==0) { beep=~beep; } } } } } void Time0()interrupt 1 { TH0=(65536-50000)/256; TL0=(65536-50000)%256; count++; }
4.總結 (1)當要用一個按鍵控制不用功能,並且一個按鍵必須作用於另一按鍵裡時,需熟練使用標誌位 這時應該構出整體框架,再細寫不同功能,例如
void read_key() { if(Key1==0) { delay(5); if(Key1==0) { flag2++; while(!Key1); if(flag2==1) { } if(flag2==2) { } if(flag2==3) { } } } if(flag2!=0) { if(flag2==1) { } if(flag2==2) { } } }
詳細使用見程式碼。 這樣的好處是思路清晰,不容易搞混{} (2)常見錯誤 1)LCD顯示有問題 若LCD只顯示一排方塊,檢查引腳是否正確 若LCD一直頻閃,可能是初始化寫在while(1)裡,所以一進入迴圈就清屏 2)數字亂跳,檢查按鍵是否消抖 (3)遺留問題 1)蜂鳴器只有“噠”的一聲 2)計時時秒針會出現60 (4)PCB佈線制板 1)分模組化,最好先畫電源,因為所有VCC和GND都來自電源,可確保其不遺漏 2)線不宜過細,焊盤和焊孔不宜過小