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

2023-05-16

开篇先说一句废话····
本旺名字叫萨摩耶,,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;   //10ms
	TL0=(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;   //10ms
	TL0=(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);
		else
			Showsmg();
		
	}
}

注意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];
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 从输入一个网址到浏览器显示页面经历的全过程

    作为网络专栏的开篇导文 xff0c 本文概况介绍下经典案例 xff1a 从输入一个网址到浏览器显示页面的全过程 步骤概要介绍如下 xff1a 1 输入网址2 DNS解析获取域名对应的IP地址3 建立TCP连接4 web浏览器向web服务器发
  • 端口号是什么以及常见端口号

    端口号 具有网络功能的应用软件的标识号 注意 xff0c 端口号是不固定的 xff0c 即可以由用户手工可以分配 xff08 当然 xff0c 一般在软件编写时就已经定义 xff09 当然 xff0c 有很多应用软件有公认的默认的端口 xf
  • DNS域名解析过程

    目录 1 DNS2 域名系统DNS 的作用3 域名的层级关系4 DNS域名解析过程 递归查询迭代查询 5 高速缓存6 DNS相关面试问题 1 DNS DNS xff08 Domain Name System xff09 是域名系统的英文缩写
  • java常见面试题

    目录 基础语法 1 Java 语言的优点 xff1f 2 Java 如何实现平台无关 xff1f 3 JVM xff0c JDK 和 JRE 的区别 xff1f 4 Java 按值调用还是引用调用 xff1f 5 浅拷贝和深拷贝的区别 xf
  • 分段分页存储

    2020 4 27 在家的网课 xff0c 无聊 xff0c 记录一下分页 xff0c 分段 xff0c 段页式存储笔记 昨天刚学了分页存储 xff0c 听得我一脸懵逼 xff0c 好在课下花了很长时间才弄懂 1 分页存储管理 1 分页存储
  • 解压码

    BN00001 22kke BN00002 88cde BN00003 00ike BN00004 76cdb BN00005 09dbm BN00006 0mndc BN00007 cd78d BN00008 bdmf8 BN00009
  • 保险项目业务流程

    1 整个项目分为四分模块 xff1a 信息采集模块 信息验证 审批 生成合同 xff08 开单 xff09 信息采集模块 xff1a 包括购买保险产品 xff0c 客户个人信息 1 纸质文档给客户填写 xff0c 在回来录入系统 2 客户直
  • IDEA使用maven自定义archetype

    标题自定义archetype 在pom文件中添加archetype plugin span class token generics span class token punctuation lt span plugin span clas
  • 自定义Perperties文件内容读取

    新建properties文件放在resources目录下 properties文件内容 url span class token operator 61 span jdbc span class token operator span my
  • 如何使用Google TV设置Chromecast

    Justin Duino 贾斯汀 杜伊诺 Justin Duino Google changed up its streaming platform with the release of the Chromecast with Googl
  • 使用CSS中的Hover控制显示子元素或者兄弟元素

    lt DOCTYPE html gt lt html lang 61 34 en 34 gt lt head gt lt meta charset 61 34 UTF 8 34 gt lt meta name 61 34 viewport
  • maven项目中的jdbc连接步骤

    在maven项目pom xml中到入驱动包 xff08 以下是驱动包代码 xff09 lt dependencies gt lt https mvnrepository com artifact mysql mysql connector
  • executeUpdate()与executeQuery()的使用

    增 删 改 用executeUpdate xff08 xff09 返回值为int型 xff0c 表示被影响的行数 例子 查用executeQuery 返回的是一个集合 next xff08 xff09 表示 指针先下一行 xff0c 还有f
  • Access denied for user ''@'localhost' (using password: YES)错误解决方法

    远程登录被拒绝 xff0c 要改一个表数据的属性让他可以远程登录 解决方法如下 xff0c 执行命令 xff1a mysql gt use mysql mysql gt select host user from user 查看结果是不是r
  • 基于yolov5和Tesseract-OCR车牌识别项目 Linux系统上搭建运行(大概结构)

    项目大概分为两部分 xff0c 首先使用yolov5进行目标检测并截图 xff1b 然后对图片一系列的处理后使用Tesseract OCR进行字符识别 xff08 本文为简易版框架结构 xff0c 如果看完感兴趣可以在文末跳转看细节操作 x
  • ubuntu20.04使用微软Azure Kinect DK 实现三维重建demo记录

    本文仅为在ubuntu20 04实现Azure Kinect DK 三维重建demo xff0c 此文记录实现过程仅供学习 xff0c 同时为大家避坑 xff0c 文中参考大量文章已列至末尾 1 ros安装 2 安装微软 DK的sdk 3
  • 常见一面问题

    1 智能指针 常用的c 43 43 库 Standard Template Library STL Algorithms 算法 Containers 容器 Functions 函数 Iterators 迭代器 Boost 同样是大量C 43
  • ROS datatype/md5sum错误

    I got this error today Problem ERROR 1576785283 032878520 Client rostopic 21515 1576784759002 wants topic timestamp to h
  • 快速安装Pytorch和Torchvision

    文章目录 1 Linux下激活自己的虚拟环境并查看Python版本2 查看需要安装的Pytorch和Torchvision版本3 直接命令行安装3 1 如果不报错的话3 2 ERROR Could not install packages
  • 【Darknet-53】YOLOv3 backbone Darknet-53 详解

    文章目录 1 模型计算量与参数量2 Darknet 53网络3 感谢链接 1 模型计算量与参数量 模型计算量与参数量的计算方式主要有两种 xff0c 一种是使用thop库 xff0c 一种是使用torchsummaryX 使用pip ins

随机推荐