一、设计题目:数字秒表设计
二、课程设计内容及要求
基本要求:
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;}
}