C51单片机实现数字秒表计时(分段计时,LCD1602显示,总计时),误差精度在1s之内
一、設(shè)計(jì)題目:數(shù)字秒表設(shè)計(jì)
二、課程設(shè)計(jì)內(nèi)容及要求
????????基本要求:
????????1.按鍵2個(gè),一個(gè)用于計(jì)時(shí)開(kāi)始/停止,一個(gè)用于數(shù)字清零
????????2.數(shù)碼管顯示,數(shù)碼管初始顯示00-00-00(分-秒-毫秒)
????????3.基本功能,按計(jì)時(shí)開(kāi)始/停止鍵,數(shù)碼管從當(dāng)前計(jì)時(shí)數(shù)字累加,精度為10ms,計(jì)時(shí)10分鐘誤差補(bǔ)超過(guò)2秒,再按計(jì)時(shí)開(kāi)始/停止鍵,數(shù)碼管停止計(jì)時(shí),按數(shù)字清零鍵,數(shù)碼管恢復(fù)初始顯示,重? 新計(jì)時(shí)
????????擴(kuò)展功能:
????????1.顯示更換為1602液晶屏顯示
????????2.實(shí)現(xiàn)分段計(jì)時(shí)和總計(jì)時(shí)兩個(gè)模式
此次設(shè)計(jì)實(shí)現(xiàn)了基本功能和拓展功能。
三、明確用戶需求,根據(jù)設(shè)計(jì)需求繪制原理圖(K4鍵可刪掉):
原理圖
四、代碼解析
第一步:根據(jù)原理圖定義引腳信息,及相關(guān)控制位、標(biāo)志位、以及需要用到的延時(shí)函數(shù)
//定義引腳
sbit LCD_RS = P1^0;
sbit LCD_RW = P1^1;
sbit LCD_E = P2^5;sbit K1 = P3^6; //定時(shí)開(kāi)始、暫停鍵
sbit K2 = P3^7; //定時(shí)清零鍵
sbit K3 = P3^4; //顯示分段計(jì)時(shí)鍵
uint keyCount = 0; //按鍵次數(shù)標(biāo)志
uint flag1 = 0; //計(jì)時(shí)時(shí)的時(shí)間是否顯示
uint time[] = {0x00,0x00,0x00,0x00,0x00,0x00}; //用來(lái)存放計(jì)時(shí)所產(chǎn)生的時(shí)間
uint temp[] = {0x00,0x00,0x00,0x00,0x00,0x00}; //用于計(jì)算、并time數(shù)組時(shí)間的時(shí)、分、秒
uchar msg[] = {" round"}; //提示界面
uchar wname[] = {"welcome"}; //友好界面uchar all_time[4][6]; //存放分段計(jì)時(shí)所產(chǎn)生的時(shí)間uint flag = -1; //作為向all_time數(shù)組保存時(shí)間的數(shù)組下標(biāo)
//延時(shí)函數(shù)delay_nops()
void delay_nops(){_nop_();_nop_();_nop_();_nop_();
}
//延時(shí)函數(shù)Delay(uint num)
void Delay(uint num)
{while( --num );
}
//延時(shí)函數(shù)delay1(int ms)
void delay1(int ms)
{unsigned char n;while(ms--){for(n = 0; n<250; n++){_nop_();_nop_();_nop_();_nop_();}}
}
第二步:LCD1602顯示部分
//在對(duì)LCD1602進(jìn)行寫操作(包括寫命令和寫數(shù)據(jù))的時(shí)候(讀操作不需要),需要檢查“忙”標(biāo)志位,不“忙”即可寫入
int LCD_BUSY(){bit isBusy;LCD_RS = 0; //設(shè)置RS為命令寄存器LCD_RW = 1; //設(shè)置RW為讀操作,讀busy位LCD_E = 1; //設(shè)置E(使能端)為1,進(jìn)行讀操作delay_nops(); //延時(shí)isBusy = (bit)(P0&0X80); //根據(jù)原理圖引腳信息及LCD1602文檔,busy位對(duì)應(yīng)P1^7口,從P1^7讀出端口的狀態(tài)LCD_E = 0; //恢復(fù)使能標(biāo)志return isBusy; //返回busy位狀態(tài)信息
}
//LCD1602 寫命令操作
void LCD_WCMD(uchar cmd){while(LCD_BUSY()); //對(duì)LCD1602進(jìn)行寫操作前,要檢查“忙”標(biāo)志位,不“忙”即可繼續(xù)寫操作LCD_RS = 0; //設(shè)置RS為命令寄存器LCD_RW = 0; //設(shè)置RW為寫操作LCD_E = 0; //設(shè)置使能E為低電平,后續(xù)給予高電平,即形成一個(gè)正脈沖,完成寫操作_nop_(); //延時(shí)一個(gè)空操作時(shí)間,保證引腳初始狀態(tài)正確賦值_nop_(); //延時(shí)一個(gè)空操作時(shí)間,保證引腳初始狀態(tài)正確賦值P0 = cmd; //向P0口寫入命令,16進(jìn)制delay_nops(); //延時(shí)若干個(gè)空操作LCD_E = 1; //給予E高電平,形成正脈沖,完成寫操作delay_nops(); //延時(shí)若干個(gè)空操作,保證命令正確寫入 LCD_E = 0; //恢復(fù)使能E為低電平Delay(10); //延時(shí)
}
//LCD1602 寫數(shù)據(jù)操作,與LCD1602寫命令操作類似,但又有不同之處,不同之處在下面代碼中標(biāo)出
void LCD_WDATA(uchar dat){while(LCD_BUSY()); LCD_RS = 1; //將RS設(shè)置為數(shù)據(jù)寄存器LCD_RW = 0;LCD_E = 0;P0 = dat; //向P0口寫入數(shù)據(jù)delay_nops();LCD_E = 1;delay_nops();LCD_E = 0;Delay(5);
}
//設(shè)置LCD1602 的顯示位置,以標(biāo)準(zhǔn) 0x80+RAM 地址方式寫入命令,具體LCD1602RAM地址映射圖如下
void LCD_POS(uchar pos){LCD_WCMD(pos | 0x80); //根據(jù)下面RAM地址映射圖,舉例,例如:LCD_WCMD(0x00 | 0x80);即顯示在LCD1602的第一行,從第一個(gè)單元開(kāi)始顯示、LCD_WCMD(0x40 | 0x80);即顯示在LCD1602的第二行,從第一個(gè)單元開(kāi)始顯示
}
????????LCD1602 RAM地址映射圖:
?
//配置完LCD1602引腳和端口信息,以及LCD1602相關(guān)函數(shù)后,根據(jù)LCD1602使用手冊(cè)進(jìn)行LCD1602的初始化
void LCD_INIT(){LCD_RW = 0; //設(shè)置LCD1602中RS為命令寄存器delay1(15); //延時(shí)LCD_WCMD(0x01); //寫入命令,清屏,清除LCD的顯示內(nèi)容LCD_WCMD(0x38); //16*2顯示,5*7點(diǎn)陣,8位數(shù)據(jù)delay1(5); //延時(shí),保證正確寫入,下方延時(shí)函數(shù)功能皆是如此LCD_WCMD(0x38); //重復(fù)寫入,保證正確寫入delay1(5);LCD_WCMD(0x38); delay1(5);LCD_WCMD(0x38);delay1(5);LCD_WCMD(0x0c); //開(kāi)顯示,不顯示光標(biāo) delay1(5);LCD_WCMD(0x01); //清除LCD的顯示內(nèi)容delay1(5);
}
//LCD1602 顯示部分
void display(){uchar i;for(i = 0;i<6;i++){temp[i] = time[i] + 0x30; //這里是把time數(shù)組(定時(shí)器T0計(jì)時(shí)產(chǎn)生的時(shí)間),進(jìn)行賦值給temp數(shù)組,便于后期進(jìn)行時(shí)、分、秒拆分顯示}LCD_POS(0x45); //設(shè)置LCD1602上顯示為第二行第6個(gè)單元開(kāi)始,具體請(qǐng)看上面提到的RAM地址映射圖LCD_WDATA(temp[5]); //顯示定時(shí)器T0產(chǎn)生的 分(十位)LCD_WDATA(temp[4]); //顯示定時(shí)器T0產(chǎn)生的 分(個(gè)位)LCD_WDATA(0x3a); //顯示“:”LCD_WDATA(temp[3]); //顯示定時(shí)器T0產(chǎn)生的 秒(十位)LCD_WDATA(temp[2]); //顯示定時(shí)器T0產(chǎn)生的 秒(個(gè)位)LCD_WDATA(0x3a); //顯示“:”LCD_WDATA(temp[1]); //顯示定時(shí)器T0產(chǎn)生的 0.1sLCD_WDATA(temp[0]); //顯示定時(shí)器T0產(chǎn)生的 0.01s}
第三步:主函數(shù)部分
int main(){uchar i;TMOD = 0X01; //選擇T0工作方式1TH0 = 0XDC; //10ms觸發(fā)一次中斷TL0 = 0X00; //10ms觸發(fā)一次中斷EA = 1; //開(kāi)啟中斷總開(kāi)關(guān)ET0 = 1; //開(kāi)啟定時(shí)中斷T0LCD_INIT(); //LCD1602初始化LCD_POS(0X00); //設(shè)置LCD1602 顯示位置為第一行第一個(gè)單元for(i = 0;i<7;i++){LCD_WDATA(wname[i]); //友好界面}delay1(100);while(1){if(K1 == 0){ //判斷按鍵K1是否按下,按下則進(jìn)行計(jì)時(shí),或者暫停(根據(jù)keyCount判斷)keyCount++; delay1(100);switch(keyCount){case 1:TR0 = 1; //打開(kāi)定時(shí)器T0,開(kāi)始LCD_POS(0x00);for(i = 0;i<10;i++){LCD_WDATA(msg[i]);}LCD_WDATA(0X31); //顯示這是第幾輪計(jì)時(shí)break;case 2:TR0 = 0; //關(guān)閉定時(shí)器T0,暫停flag++;saveTime(); //在暫停的時(shí)候進(jìn)行時(shí)間保存,即達(dá)到分段計(jì)時(shí)的效果break;case 3:TR0 = 1; //打開(kāi)定時(shí)器T0,開(kāi)始LCD_POS(0x00);for(i = 0;i<10;i++){LCD_WDATA(msg[i]);}LCD_WDATA(0X32);break;case 4:TR0 = 0; //關(guān)閉定時(shí)器T0,暫停flag++; saveTime();break;case 5:TR0 = 1; //打開(kāi)定時(shí)器T0,開(kāi)始LCD_POS(0x00);for(i = 0;i<10;i++){LCD_WDATA(msg[i]);}LCD_WDATA(0X33);break;case 6:TR0 = 0; //關(guān)閉定時(shí)器T0,暫停flag++;saveTime();break;case 7:TR0 = 1;LCD_POS(0x00);for(i = 0;i<10;i++){LCD_WDATA(msg[i]);}LCD_WDATA(0X34);break;case 8:TR0 = 0;flag++;saveTime();break;default:TR0 = 0;flag = -1; break;}delay1(10);}if(K2==0){ //定時(shí)時(shí)間清零鍵TR0=0;keyCount = 0;flag=3;delay1(10);flag=3;delay1(10);flag=3;delay1(10);for(i=0;i<6;i++){ //對(duì)在中斷處理函數(shù)中保存在time中的時(shí)間進(jìn)行清零time[i] = 0;}}if(K3==0){ //顯示分段計(jì)時(shí),所存儲(chǔ)的時(shí)間段TR0 = 0;flag1 = 1;display1(flag); //顯示分段時(shí)間段flag1 = 0; //顯示完自動(dòng)隱式退出delay1(10); } if(flag1==0){ // 當(dāng)flag1==0,即自動(dòng)退出“顯示分段計(jì)時(shí)”后,顯示計(jì)時(shí)頁(yè)面display(); //顯示計(jì)時(shí)頁(yè)面} }
}
????????定時(shí)中斷處理函數(shù):
//定時(shí)器T0的中斷處理函數(shù),time數(shù)組用來(lái)保存定時(shí)器T0計(jì)時(shí)所產(chǎn)生的時(shí)間void timer0() interrupt 1{TH0 = 0XDC; //重新裝入初值TL0 = 0X00;time[0]++; //保存0.01sif(time[0]==10){ time[1]++; //保存0.1stime[0] = 0;}if(time[1]==10){ time[2]++; //保存1stime[1] = 0;}if(time[2]==10){ time[3]++; //保存10stime[2] = 0;}if(time[3]==6){ time[4]++; //保存1mintime[3] = 0;}if(time[4]==10){ time[5]++; //保存10mintime[4] = 0;}if(time[5]==6){time[5] = 0;}
}
? ? ? LCD1602 顯示時(shí)間函數(shù)(循環(huán)顯示):
//LCD1602 顯示函數(shù) 時(shí)分秒函數(shù)
void display1(uint m){uchar i;uint t = 0x31;for(i = 0;i<=m;i++){LCD_WCMD(0X01);LCD_POS(0X00);LCD_WDATA(0X3C);LCD_WDATA(t++);LCD_WDATA(0X3E);LCD_POS(0x45);LCD_WDATA(all_time[i][5] + 0x30);LCD_WDATA(all_time[i][4] + 0x30);LCD_WDATA(0x3a);LCD_WDATA(all_time[i][3] + 0x30);LCD_WDATA(all_time[i][2] + 0x30);LCD_WDATA(0x3a);LCD_WDATA(all_time[i][1] + 0x30);LCD_WDATA(all_time[i][0] + 0x30);delay1(500);}}
? ? ? 在main函數(shù)中調(diào)用的保存分段時(shí)間函數(shù):
//保存時(shí)間段
void saveTime(){uchar j = 0;if(flag != -1){for(j = 0;j<6;j++){all_time[flag][j] = time[j]; //保存每次暫停所產(chǎn)生的時(shí)間段,即分段計(jì)時(shí)功能}}
}
? ? ? ? 在main函數(shù)中調(diào)用的顯示分段時(shí)間的函數(shù):
//顯示分段計(jì)時(shí)存儲(chǔ)的時(shí)間信息,類似前面的display()函數(shù)
void display1(uint m){uchar i;uint t = 0x31;for(i = 0;i<=m;i++){LCD_WCMD(0X01); LCD_POS(0X00);LCD_WDATA(0X3C);LCD_WDATA(t++);LCD_WDATA(0X3E);LCD_POS(0x45);LCD_WDATA(all_time[i][5] + 0x30); //這里加上0x30是根據(jù)單片機(jī)系統(tǒng)自帶字符庫(kù)編碼方式LCD_WDATA(all_time[i][4] + 0x30);LCD_WDATA(0x3a);LCD_WDATA(all_time[i][3] + 0x30);LCD_WDATA(all_time[i][2] + 0x30);LCD_WDATA(0x3a);LCD_WDATA(all_time[i][1] + 0x30);LCD_WDATA(all_time[i][0] + 0x30);delay1(500);}}
第四步:實(shí)物連接
?
完整代碼:
#include<reg51.h>
#include<intrins.h>#define uchar unsigned char
#define uint unsigned intsbit LCD_RS = P1^0;
sbit LCD_RW = P1^1;
sbit LCD_E = P2^5;sbit K1 = P3^6;
sbit K2 = P3^7;
sbit K3 = P3^4;
uint keyCount = 0;
uint flag1 = 0;
uint time[] = {0x00,0x00,0x00,0x00,0x00,0x00};
uint temp[] = {0x00,0x00,0x00,0x00,0x00,0x00};
uchar msg[] = {" "};
uchar wname[] = {"welcome"};uchar all_time[4][6];uint flag = -1;
void delay_nops(){_nop_();_nop_();_nop_();_nop_();
}
void Delay(uint num)
{while( --num );
}
void delay1(int ms)
{unsigned char n;while(ms--){for(n = 0; n<250; n++){_nop_();_nop_();_nop_();_nop_();}}
}
int LCD_BUSY(){bit isBusy;LCD_RS = 0;LCD_RW = 1;LCD_E = 1;delay_nops();isBusy = (bit)(P0&0X80);LCD_E = 0;return isBusy;
}
void LCD_WCMD(uchar cmd){while(LCD_BUSY());LCD_RS = 0;LCD_RW = 0;LCD_E = 0;_nop_();_nop_();P0 = cmd;delay_nops();LCD_E = 1;delay_nops();LCD_E = 0;Delay(10);
}
void LCD_WDATA(uchar dat){while(LCD_BUSY());LCD_RS = 1;LCD_RW = 0;LCD_E = 0;P0 = dat;delay_nops();LCD_E = 1;delay_nops();LCD_E = 0;Delay(5);
}void LCD_INIT(){LCD_RW = 0;delay1(15); LCD_WCMD(0x01);LCD_WCMD(0x38);delay1(5);LCD_WCMD(0x38);delay1(5);LCD_WCMD(0x38);delay1(5);LCD_WCMD(0x38);delay1(5);LCD_WCMD(0x0c);delay1(5);LCD_WCMD(0x01);delay1(5);
}
void LCD_POS(uchar pos){LCD_WCMD(pos | 0x80);
}
void display(){uchar i;for(i = 0;i<6;i++){temp[i] = time[i] + 0x30;}LCD_POS(0x45);LCD_WDATA(temp[5]);LCD_WDATA(temp[4]);LCD_WDATA(0x3a);LCD_WDATA(temp[3]);LCD_WDATA(temp[2]);LCD_WDATA(0x3a);LCD_WDATA(temp[1]);LCD_WDATA(temp[0]);}
void saveTime(){uchar j = 0;if(flag != -1){for(j = 0;j<6;j++){all_time[flag][j] = time[j];}}
}
void display1(uint m){uchar i;uint t = 0x31;for(i = 0;i<=m;i++){LCD_WCMD(0X01);LCD_POS(0X00);LCD_WDATA(0X3C);LCD_WDATA(t++);LCD_WDATA(0X3E);LCD_POS(0x45);LCD_WDATA(all_time[i][5] + 0x30);LCD_WDATA(all_time[i][4] + 0x30);LCD_WDATA(0x3a);LCD_WDATA(all_time[i][3] + 0x30);LCD_WDATA(all_time[i][2] + 0x30);LCD_WDATA(0x3a);LCD_WDATA(all_time[i][1] + 0x30);LCD_WDATA(all_time[i][0] + 0x30);delay1(500);}}
int main(){uchar i;TMOD = 0X01;TH0 = 0XDC;TL0 = 0X00;EA = 1;ET0 = 1;LCD_INIT();LCD_POS(0X00);P3 = 0XC0 | P3;LCD_POS(0X00);for(i = 0;i<7;i++){LCD_WDATA(wname[i]);}delay1(100);while(1){if(K1 == 0){keyCount++;delay1(100);switch(keyCount){case 1:TR0 = 1;LCD_POS(0x00);for(i = 0;i<10;i++){LCD_WDATA(msg[i]);}LCD_WDATA(0X31);break;case 2:TR0 = 0;flag++;saveTime();break;case 3:TR0 = 1;LCD_POS(0x00);for(i = 0;i<10;i++){LCD_WDATA(msg[i]);}LCD_WDATA(0X32);break;case 4:TR0 = 0;flag++; saveTime();break;case 5:TR0 = 1;LCD_POS(0x00);for(i = 0;i<10;i++){LCD_WDATA(msg[i]);}LCD_WDATA(0X33);break;case 6:TR0 = 0;flag++;saveTime();break;case 7:TR0 = 1;LCD_POS(0x00);for(i = 0;i<10;i++){LCD_WDATA(msg[i]);}LCD_WDATA(0X34);break;case 8:TR0 = 0;flag++;saveTime();break;default:TR0 = 0;flag = -1;break;}delay1(10);}if(K2==0){TR0=0;keyCount = 0;flag=3;delay1(10);flag=3;delay1(10);flag=3;delay1(10);for(i=0;i<6;i++){time[i] = 0;}}if(K3==0){TR0 = 0;flag1 = 1;display1(flag);flag1 = 0;delay1(10); } if(flag1==0){display();} }
}
void timer0() interrupt 1{TH0 = 0XDC;TL0 = 0X00;time[0]++; //0.01sif(time[0]==10){ time[1]++; //0.1stime[0] = 0;}if(time[1]==10){ time[2]++; //1stime[1] = 0;}if(time[2]==10){ time[3]++; //10stime[2] = 0;}if(time[3]==6){ time[4]++; //1mintime[3] = 0;}if(time[4]==10){ time[5]++; //10mintime[4] = 0;}if(time[5]==6){time[5] = 0;}
}
總結(jié)
以上是生活随笔為你收集整理的C51单片机实现数字秒表计时(分段计时,LCD1602显示,总计时),误差精度在1s之内的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: CSS flex布局是怎样的
- 下一篇: IT 实力较量:决战超级数据中心之巅