一、设计题目:数字秒表设计
二、课程设计内容及要求
????????基本要求:
????????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.1s
LCD_WDATA(temp[0]); //显示定时器T0产生的 0.01s
}
第三步:主函数部分
int main(){
uchar i;
TMOD = 0X01; //选择T0工作方式1
TH0 = 0XDC; //10ms触发一次中断
TL0 = 0X00; //10ms触发一次中断
EA = 1; //开启中断总开关
ET0 = 1; //开启定时中断T0
LCD_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.01s
if(time[0]==10){
time[1]++; //保存0.1s
time[0] = 0;
}
if(time[1]==10){
time[2]++; //保存1s
time[1] = 0;
}
if(time[2]==10){
time[3]++; //保存10s
time[2] = 0;
}
if(time[3]==6){
time[4]++; //保存1min
time[3] = 0;
}
if(time[4]==10){
time[5]++; //保存10min
time[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 int
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};
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.01s
if(time[0]==10){
time[1]++; //0.1s
time[0] = 0;
}
if(time[1]==10){
time[2]++; //1s
time[1] = 0;
}
if(time[2]==10){
time[3]++; //10s
time[2] = 0;
}
if(time[3]==6){
time[4]++; //1min
time[3] = 0;
}
if(time[4]==10){
time[5]++; //10min
time[4] = 0;
}
if(time[5]==6){
time[5] = 0;
}
}
|