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