51单片机应用篇-- --倒计时数字钟,矩阵按键可调

news/2024/11/23 10:01:20/

开篇先说一句废话····
本旺名字叫萨摩耶,,Please 叫我旺财,,,哈哈,招财进宝嘛!

项目要求

这篇也和之前那篇文章单片机应用篇-- --数码管60秒计时,独立按键可调 差不多,而且原因也差不多哦,也是平台有人私信让我帮忙瞧瞧,直接上图。
在这里插入图片描述

项目分析

这次的项目和上次差不多,而且上次写的的时候也是按照矩阵按键检测写的,与上次的区别就是上次是只有秒,而这次时、分、秒都有了,上次只有四个按键,而这次有16个按键,需要多个按键处理函数。

  • 首先,就是对定时器、数码管显示的初始化
void Init(){//参数初始化hour=0;minute=0;second=0;t0=0;flag=0;flag1=0;flag2=0;//定时器0初始化TMOD=0x01;TH0=(65536-9174)/256;   //10msTL0=(65536-9174)%256;EA=1;ET0=1;TR0=0;
}
  • 接下来是矩阵按键检测,这里面也要设置一个标志位,因为Func功能没有允许的情况下就让0-9这几个按键起作用(不能人家正倒计时走呢,你瞎按改变时分秒的值吧)
void KeyDown(){char a=0;GPIO_KEY=0x0f;if(GPIO_KEY!=0x0f){Delay(10);if(GPIO_KEY!=0x0f){GPIO_KEY=0x0f;switch(GPIO_KEY){case 0x07 : KeyVal=0;flag3=1;break;case 0x0b : KeyVal=1;flag3=1;break;case 0x0d : KeyVal=2;flag3=1;break;case 0x0e : KeyVal=3;flag3=1;break;}GPIO_KEY=0xf0;switch(GPIO_KEY){case 0x70 : KeyVal=KeyVal;break;case 0xb0 : KeyVal=KeyVal+4;break;case 0xd0 : KeyVal=KeyVal+8;break;case 0xe0 : KeyVal=KeyVal+12;break;}}while((a<150) && (GPIO_KEY!=0xf0)){Delay(1);a++;}}
}
  • 检测完就要处理了,这个处理的目的就是要设置按键与功能的对应了。
void Keypross(){if(flag3==1){flag3=0;switch(KeyVal){case 0:Numpross(7);break;case 1:Numpross(8);break;case 2:Numpross(9);break;case 3:Uppross();break;case 4:Numpross(4);break;case 5:Numpross(5);break;case 6:Numpross(6);break;case 7:Downpross();break;case 8:Numpross(1);break;case 9:Numpross(2);break;case 10:Numpross(3);break;case 11:Funcpross();break;case 12:Numpross(0);break;case 13:break;case 14:Backpross();break;case 15:Enterpross();break;}}
}
  • 按键Func功能,当按下这个按键之后,要进入设置功能,就要让定时器暂停,这里就要用到那个标志位,允许改变时分秒
void Funcpross(){TR0=0;	//停止计时flag=1; //允许设置时间
}
  • 按键Up功能,首先判断Func按键是否按下过,按下过的话,让改变数字的那位向后加一位
void Uppross(){if(flag==1){flag1++;if(flag1>5){flag1=0;}}
}
  • 按键Down功能,和Up一样,若按下过,让改变数字的那位向前减一位
void Downpross(){if(flag==1){flag1--;if(flag1<0)flag1=5;}
}
  • 按键数字功能,这里就要判断Func是否按下和Up和Down是否调过位(默认为第一位,也就是小时的十位)
void Numpross(u8 dat){u8 tp;if(flag==1){switch(flag1){case 0:tp=hour%10;hour=tp+dat*10;break;case 1:tp=hour/10;hour=tp*10+dat;break;case 2:tp=minute%10;minute=tp+dat*10;break;case 3:tp=minute/10;minute=tp*10+dat;break;case 4:tp=second%10;second=tp+dat*10;break;case 5:tp=second/10;second=tp*10+dat;break;}}
}
  • 按键Enter功能,当它按下表示设置完成,Func那个标志位就要清零了,判断一下小时是否在24以内,分和秒是否在60以内(若超过就要强制归为23,59,59),还有定时器开始计时,开始倒计时。
void Enterpross(){flag=0;		//清零,不允许设置时间flag1=0;if(hour>23)hour=23;if(minute>59)minute=59;if(second>59)second=59;TR0=1;		//开始计时
}
  • 按键Back功能,这个暂时不设置,题目没细说,可自行设计。
void Backpross(){
//暂无功能
}
  • 按键模块完了,就要进入本体,倒计时了呀,让定时器计时,每一秒让秒自减一,这里就要判断了,减完一之后如果分是正数,分自减一并且秒归为59,若是负数(无符号0减完就是256)它比59大,判断小时的正负,若为正小时自减一并且分归为59,若小时也是负数(比23大),表示时分秒都为0,将时分秒都归零,定时器停止,倒计时结束。最后将时分秒分别求模和求余放到数组中,方便数码管显示。
void Datapross(){if(flag2==1){flag2=0;second--;if(second>59){if(minute>59){if(hour>23){second=0;minute=0;hour=0;TR0=0;}else{hour--;minute=59;}}else{minute--;second=59;}	}	}Display[0]=smgduan[hour/10];Display[1]=smgduan[hour%10];Display[2]=0x40;Display[3]=smgduan[minute/10];Display[4]=smgduan[minute%10];Display[5]=0x40;Display[6]=smgduan[second/10];Display[7]=smgduan[second%10];
}

完整代码

//main.c
#include "smg.h"void main(){Init();while(1){KeyDown();Keypross();Datapross();Showsmg();}
}//数码管显示函数
void Showsmg(){      u8 i;for(i=0;i<8;i++){switch(7-i)	  //位选{case 0: LSA=0;LSB=0;LSC=0;break;case 1: LSA=1;LSB=0;LSC=0;break;case 2: LSA=0;LSB=1;LSC=0;break;case 3: LSA=1;LSB=1;LSC=0;break;case 4: LSA=0;LSB=0;LSC=1;break;case 5: LSA=1;LSB=0;LSC=1;break;case 6: LSA=0;LSB=1;LSC=1;break;case 7: LSA=1;LSB=1;LSC=1;break;}P0=Display[i];Delay(1);P0=0x00;}
}
//延时函数
void Delay(u16 i)   //11.0592 1ms
{while(i--){unsigned char i, j;_nop_();i = 2;j = 199;do{while (--j);} while (--i);}
} 
//初始化函数
void Init(){//参数初始化hour=0;minute=0;second=0;t0=0;flag=0;flag1=0;flag2=0;//定时器0初始化TMOD=0x01;TH0=(65536-9174)/256;   //10msTL0=(65536-9174)%256;EA=1;ET0=1;TR0=0;
}
//按键检测
void KeyDown(){char a=0;GPIO_KEY=0x0f;if(GPIO_KEY!=0x0f){Delay(10);if(GPIO_KEY!=0x0f){GPIO_KEY=0x0f;switch(GPIO_KEY){case 0x07 : KeyVal=0;flag3=1;break;case 0x0b : KeyVal=1;flag3=1;break;case 0x0d : KeyVal=2;flag3=1;break;case 0x0e : KeyVal=3;flag3=1;break;}GPIO_KEY=0xf0;switch(GPIO_KEY){case 0x70 : KeyVal=KeyVal;break;case 0xb0 : KeyVal=KeyVal+4;break;case 0xd0 : KeyVal=KeyVal+8;break;case 0xe0 : KeyVal=KeyVal+12;break;}}while((a<150) && (GPIO_KEY!=0xf0)){Delay(1);a++;}}
}
//按键处理
void Keypross(){if(flag3==1){flag3=0;switch(KeyVal){case 0:Numpross(7);break;case 1:Numpross(8);break;case 2:Numpross(9);break;case 3:Uppross();break;case 4:Numpross(4);break;case 5:Numpross(5);break;case 6:Numpross(6);break;case 7:Downpross();break;case 8:Numpross(1);break;case 9:Numpross(2);break;case 10:Numpross(3);break;case 11:Funcpross();break;case 12:Numpross(0);break;case 13:break;case 14:Backpross();break;case 15:Enterpross();break;}}
}
//数据处理
void Datapross(){if(flag2==1){flag2=0;second--;if(second>59){if(minute>59){if(hour>23){second=0;minute=0;hour=0;TR0=0;}else{hour--;minute=59;}}else{minute--;second=59;}	}	}Display[0]=smgduan[hour/10];Display[1]=smgduan[hour%10];Display[2]=0x40;Display[3]=smgduan[minute/10];Display[4]=smgduan[minute%10];Display[5]=0x40;Display[6]=smgduan[second/10];Display[7]=smgduan[second%10];
}
//按键数字处理
void Numpross(u8 dat){u8 tp;if(flag==1){switch(flag1){case 0:tp=hour%10;hour=tp+dat*10;break;case 1:tp=hour/10;hour=tp*10+dat;break;case 2:tp=minute%10;minute=tp+dat*10;break;case 3:tp=minute/10;minute=tp*10+dat;break;case 4:tp=second%10;second=tp+dat*10;break;case 5:tp=second/10;second=tp*10+dat;break;}}
}
//Up功能处理
void Uppross(){if(flag==1){flag1++;if(flag1>5){flag1=0;}}
}
//Down功能处理
void Downpross(){if(flag==1){flag1--;if(flag1<0)flag1=5;}
}
//Func功能处理
void Funcpross(){TR0=0;	//停止计时flag=1; //允许设置时间
}
//Enter功能处理
void Enterpross(){flag=0;		//清零,不允许设置时间flag1=0;if(hour>23)hour=23;if(minute>59)minute=59;if(second>59)second=59;TR0=1;		//开始计时
}
//Back功能处理
void Backpross(){
//暂无功能
}void Timer0() interrupt 1{TH0=(65536-9174)/256;   //重置TL0=(65536-9174)%256;t0++;if(t0==100){t0=0;flag2=1;}
}
//smg.h
#include "reg52.h"
#include "intrins.h"typedef unsigned int u16;
typedef unsigned char u8;
#define GPIO_KEY P1
u8 smgduan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};u8 t0,KeyVal,Display[8],flag,flag1,flag2,flag3;
u16 hour,minute,second;				   
//38译码器					 
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;//数码管显示函数
void Showsmg();
//延时函数
void Delay(u16 i);
//初始化函数
void Init();
//按键检测
void KeyDown();
//按键处理
void Keypross();
//数据处理
void Datapross();
//按键数字处理
void Numpross(u8 dat);
//Up功能处理
void Uppross();
//Down功能处理
void Downpross();
//Func功能处理
void Funcpross();
//Enter功能处理
void Enterpross();
//Back功能处理
void Backpross();

总结

  • 有点遗憾就是,倒计时设置时间的时候让数码管闪烁的功能暂时没有完成,有一个想法就是,让数码管一阵显示正常,一阵显示0x00(不显示)。写出来之后再补充。
  • 从这次可以了看出我之前坚持写博客文章已经逐渐起了作用,阅读量也在增加,而且让我帮忙看看的网友也成功加好友了,让我们一起进步,嘿嘿!

补充

  • 数码管闪烁问题
    设置一个全局变量来控制闪烁频率,这个闪烁其实就是数码管动态扫描的时候循环到标志位的时候让它一阵显示数字一阵不显示,或者是跳过此次循环
void Flash(unsigned char t)				
{	unsigned char i;num%=50;for(i=0;i<8;i++){switch(7-i)	  //位选{case 0: LSA=0;LSB=0;LSC=0;break;case 1: LSA=1;LSB=0;LSC=0;break;case 2: LSA=0;LSB=1;LSC=0;break;case 3: LSA=1;LSB=1;LSC=0;break;case 4: LSA=0;LSB=0;LSC=1;break;case 5: LSA=1;LSB=0;LSC=1;break;case 6: LSA=0;LSB=1;LSC=1;break;case 7: LSA=1;LSB=1;LSC=1;break;}if((i==t)&&(num<25)){P0=0x00;Delay(1);P0=0x00;
//			continue;}else{P0=Display[i];Delay(1);P0=0x00;}}num++;
}

而对应其他地方需要需要修改的是:Up和Down功能需要跳过区分时分秒的那两个杠,扩大标志位的范围原来只有6位,现在就要把两个杠包括进去就是8位。

//Up功能处理
void Uppross(){if(flag==1){flag1++;if(flag1>7){		//扩大范围flag1=0;}if((flag1==5)||(flag1==2)){		//跳过两个 -flag1++;}}
}
//Down功能处理
void Downpross(){if(flag==1){flag1--;if(flag1<0)flag1=7;if((flag1==5)||(flag1==2)){flag1--;}}
}

最后就是要在主函数中调用

void main(){Init();while(1){KeyDown();Keypross();Datapross();if(flag==1)Flash(flag1);elseShowsmg();}
}

注意smg.h文件函数声明的变化

  • Bug问题
    按照上面写的当分(minute)为0且小时(hour)不为0的时候,倒计时的时候会出错,(原因就是没有设置秒的值)所以要在数据处理函数中,在符合该条件时添加一句 second=59; 因为是先判断在处理,所以判断条件改为(==0) 还有一个问题就是,当计时结束后不能重新设置计时,需要在计时结束后退出去(return)
//数据处理
void Datapross(){if(flag2==1){flag2=0;second--;if(second>59){if(minute==0){		//修改条件if(hour==0){	//修改条件second=0;minute=0;hour=0;TR0=0;return;		//退出到主函数}else{hour--;minute=59;second=59;	//设置秒的值}}else{minute--;second=59;}	}	}Display[0]=smgduan[hour/10];Display[1]=smgduan[hour%10];Display[2]=0x40;Display[3]=smgduan[minute/10];Display[4]=smgduan[minute%10];Display[5]=0x40;Display[6]=smgduan[second/10];Display[7]=smgduan[second%10];
}

http://www.ppmy.cn/news/551920.html

相关文章

8051单片机(STC89C52)倒计时器

#include <STC89C5xRC.H>void delay()//用定时器0实现10ms精准定时 {TMOD 0x01;TH0 0xd8;TL0 0xf0;//65536 - 10000 55536TF0 0;//置溢出标志位为0TR0 1;//开启定时器0while(TF0 0);TR0 0;//暂停定时器0 }void disp_digit(int d) {unsigned char code DIG_CODE[1…

STC8H8K系列汇编和C51实战——秒倒计时器(汇编版)

秒倒计时器&#xff08;汇编版&#xff09; 一、题目二、代码总结系列文章——STC8H8K系列汇编51实战 一、题目 在实验箱上利用单片机定时器/计数器&#xff08;工作于中断方式&#xff09;设计一个秒倒计时器&#xff0c;用数码管显示秒数&#xff0c;从100 倒计时至0 s 后从…

51单片机通过计时器实现倒计时

51单片机通过计时器实现倒计时 文章目录 51单片机通过计时器实现倒计时实验环境仿真图代码实现成果图 实验环境 软件: Keil5Proteus7 元件: AT89C51 * 1&#xff0c;7SEG-MPX2-CA * 1 仿真图 代码实现 //名称: 定时器实现数码管倒计时 #include <reg51.h> #define uint …

555定时器+74系列芯片搭建八路抢答器,30s倒计时,附Proteus仿真等

设计要求 1.设计一个基于数字电路的30秒定时抢答器&#xff0c;可同时供8人或8队比赛&#xff1b; 2.给节目主持人设置一个控制开关&#xff0c;用于控制系统复位和抢答开始&#xff1b; 3.抢答器具有数据锁存和显示功能。抢答开始后&#xff0c;如有选手抢答成功&#xff0c…

STC8H8K系列汇编和C51实战——秒倒计时器(可自行设定初值)(51版)

秒倒计时器&#xff08;可自行设定初值&#xff09;&#xff08;51版&#xff09; 一、题目二、代码总结系列文章——STC8H8K系列汇编51实战 一、题目 在实验箱上利用单片机定时器/计数器&#xff08;工作于中断方式&#xff09;设计一个秒倒计时器&#xff0c;用数码管显示秒…

【杰理AC696X】软件定时器介绍

【杰理AC696X】软件定时器介绍 测试SDK版本&#xff1a;《ac696n_soundbox_sdk_v1.6.0》 文章目录 【杰理AC696X】软件定时器介绍前言一、功能概述1.1 软件定时器类型1.2 软件定时器相关知识 二、流程框架2.1 usr_timer 流程框架2.1 sys_timer 流程框架 三、接口详细说明3.1 us…

倒计时器c语言,在线倒计时器

手机评站网今天精心准备的是《在线倒计时器》&#xff0c;下面是详解&#xff01; 如何在PPT中插入3分钟倒计时器,最后有声音。280992... 如何在PPT中插入3分钟倒计时器,最后有声音。280992656秋秋在线等&#xff0c;万分感谢&#xff01;... 如何在PPT中插入3分钟倒计时器,最后…

【国产虚拟仪器】基于FPGA+JESD204B 时钟双通道 6.4GSPS 高速数据采集模块设计(二)研究 JESD204B 链路建立与同步的过程

基于 JESD204B 的采集与数据接收电路设计 本章将围绕基于 JESD204B 高速数据传输接口的双通道高速数据采集实现展 开。首先&#xff0c;简介 JESD204B 协议、接口结构。然后&#xff0c;研究 JESD204B 链路建立与同 步的过程。其次&#xff0c;研究基于 JESD204B …