3 单片机应用实践篇之定时/计数器实现按键可调电子时钟的代码分享

上节分享了单片机定时/计数器实现数码管动态刷新的程序代码,本节利用定时/计数器以及外部中断实现一简单的电子时钟功能。对应的视频课程为:单片机应用实践篇之定时/计数器实现按键可调电子时钟,视频链接:https://www.ixigua.com/6840071457402257928。此处把视频课程中的程序代码重新整理了下,按照模块化进行了简单的处理,分为main.c,display.c,display.h,time.c,time.h,exit.c,exit.h。此处的按键可调电子时钟功能比较简单,主要是为了给初学单片机的同学介绍一个综合的应用实例,同时,也考虑下状态机的编程思路,及程序的框架处理。程序当然有许多需要改进和优化的地方,此处只是采用了4位数码管进行程序的功能演示,大家如果做实物的话,可以考虑采用时钟专用数码管,另外程序为了演示效果,当前界面是显示的分钟:秒,小时并没有显示,大家可以根据自己的需求,进行相应的界面设计及修改。如果在此代码的基础上,适当的增加些小算法代码就可以实现一个万年历的功能,如果有网友有想要实现万年历的功能代码的话,可以留言,我后面可以进行分享以前调试的一个代码。话不多说,代码撸起来。

//main.c
/***********************************************************
程序功能:按键可调电子时钟的设计
          四位数码管显示  分钟:秒(可根据需要调整显示界面)
          KEY1               外部中断0     状态切换
					KEY2               外部中断1     参数调整
					产生时间基准        定时器0 
          数码管动态刷新      定时器1					
*************************************************************/
#include <reg51.h>
#include "display.h"
#include "time.h"
#include "exit.h"

void showNormal();
void showSec();
void showMin();
void showHour();

//状态变量
unsigned char state = 0;

void main()
{
	EX0_Init();
	EX1_Init();
	Timer1Init();
	Timer0Init();
	EA = 1;
	while(1)
	{
		switch(state)
		{
			case 0:TR0 = 1;showNormal();break;//正常时钟状态
			case 1:showSec();break;//调秒
			case 2:showMin();break;//调分
			case 3:showHour();break;//调时
			default:break;
		}
	}
}
//正常时钟显示界面
void showNormal()
{
	LEDBuf[0] = min/10;
	LEDBuf[1] = min%10;
	LEDBuf[2] = sec/10;
	LEDBuf[3] = sec%10;
}
//调秒显示界面
void showSec()
{
	LEDBuf[0] = 23;//关
	LEDBuf[1] = 23;//关
	LEDBuf[2] = sec/10;
	LEDBuf[3] = sec%10;
}
//调分显示界面
void showMin()
{
	LEDBuf[0] = min/10;
	LEDBuf[1] = min%10;
	LEDBuf[2] = 23;//关
	LEDBuf[3] = 23;//关
}
//调时显示界面
void showHour()
{
	LEDBuf[0] = hour/10;
	LEDBuf[1] = hour%10;
	LEDBuf[2] = 23;//关
	LEDBuf[3] = 23;//关
}
//time.c
#include "time.h"
#include "display.h"

//全局变量定义
unsigned char timerCnt;
unsigned char sec;
unsigned char min;
unsigned char hour;

void Timer0Init(void)		//50毫秒@12.000MHz
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0xB0;		//设置定时初值
	TH0 = 0x3C;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	ET0 = 1;
}

void Timer1Init(void)		//1毫秒@12.000MHz
{
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;		//设置定时器模式
	TL1 = 0x18;		//设置定时初值
	TH1 = 0xFC;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	ET1 = 1;
	TR1 = 1;		//定时器1开始计时
}

/*************************************
1、中断服务函数一定是一个没有返回值的函数
2、中断服务函数一定是没有参数的函数
3、中断服务函数函数名后跟关键字 interrupt 
4、interrupt n 0 - 4   5个中断源     8*n + 0003H  
    0003H INT0 000BH T0  0013H INT1 001BH T1 0023H ES
5、中断服务函数不能被主程序或其他程序所调用
6、 n 后面 跟 using m (0 - 3 )工作寄存器组
***************************************/
void timer0_ISR(void) interrupt 1
{
	timerCnt++;
	if(timerCnt >= 20)//20次50ms中断,即1S时间到
	{
		timerCnt = 0;
		sec++;
		if(sec >= 60)
		{
			sec = 0;
			min++;
			if(min >= 60)
			{
				min = 0;
				hour++;
				if(hour >= 24)
				{
					hour = 0;
				}
			}
		}
	}
	TL0 = 0xB0;		//设置定时初值
	TH0 = 0x3C;		//设置定时初值
}

void timer1_ISR(void) interrupt 3
{
	TR1 = 0;
	Display();
	TL1 = 0x18;		//设置定时初值
	TH1 = 0xFC;		//设置定时初值
	TR1 = 1;	
}
//time.h
#ifndef __TIME_H__
#define __TIME_H__

#include <reg51.h>

extern unsigned char sec;
extern unsigned char min;
extern unsigned char hour;

void Timer0Init(void);
void Timer1Init(void);

#endif
//exti.c
#include "exit.h"
#include "time.h"

//外部变量声明
extern unsigned char state;

void EX0_Init()
{
	IT0 = 1;
	EX0 = 1;
}
void EX1_Init()
{
	IT1 = 1;
	EX1 = 1;
}

void EX0_ISR() interrupt 0
{
	TR0 = 0;
	state++;
	if(state >3 )
		state = 0;
}

void EX1_ISR() interrupt 2
{
	switch(state)
	{
		case 0:break;
		case 1:sec++;if(sec >= 60) sec = 0;break;
		case 2:min++;if(min >= 60) min = 0;break;
		case 3:hour++;if(hour >= 24) hour = 0;break;
		default:break;
	}
}
//exti.h
#ifndef __EXIT_H__
#define __EXIT_H__

#include <reg51.h>

void EX0_Init();
void EX1_Init();

#endif
//display.c
#include "display.h"

unsigned char code leddata[]={ //数码管的段码表
                0x3F,  //"0"
                0x06,  //"1"
                0x5B,  //"2"
                0x4F,  //"3"
                0x66,  //"4"
                0x6D,  //"5"
                0x7D,  //"6"
                0x07,  //"7"
                0x7F,  //"8"
                0x6F,  //"9"
                0x77,  //"A"
                0x7C,  //"B"
                0x39,  //"C"
                0x5E,  //"D"
                0x79,  //"E"
                0x71,  //"F"
                0x76,  //"H"
                0x38,  //"L"
                0x37,  //"n"
                0x3E,  //"u"
                0x73,  //"P"
                0x5C,  //"o"
                0x40,  //"-"
                0x00,  //熄灭             
                         };
unsigned char LEDBuf[] = {22,22,22,22};//数据显示缓冲区
unsigned char code PLACE_CODE[] = {0xfe,0xfd,0xfb,0xf7};//位选信号

void Display()
{
	static unsigned char i = 0;
	//1、送段码
	//2、送位选
	//3、延时 
	//4、消隐	
	switch(i)
	{
		case 0:
			GPIO_DIG = 0x00;
			GPIO_DIG  = leddata[LEDBuf[0]];
		  GPIO_PLACE = PLACE_CODE[0];
			i++;
			break;
		case 1:
			GPIO_DIG = 0x00;
			GPIO_DIG  = leddata[LEDBuf[1]];
		  GPIO_PLACE = PLACE_CODE[1];
			i++;
			break;
		case 2:
			GPIO_DIG = 0x00;
			GPIO_DIG  = leddata[LEDBuf[2]];
		  GPIO_PLACE = PLACE_CODE[2];
			i++;
			break;
		case 3:
			GPIO_DIG = 0x00;
			GPIO_DIG  = leddata[LEDBuf[3]];
		  GPIO_PLACE = PLACE_CODE[3];
			i = 0;
			break;
		default:break;
	}

}
//display.h
#ifndef __DISPLAY_H__
#define __DISPLAY_H__

#include <reg51.h>

//IO的声明
#define GPIO_DIG     P0  //段码 IO
#define GPIO_PLACE   P2  //位选 IO

#define N            4   //数码管个数

unsigned char code leddata[];
extern unsigned char LEDBuf[];

void Display();
#endif

执行代码后,通过Proteus仿真的显示界面如下:

正常时钟显示界面(分钟:秒)

调秒显示界面

调分显示界面

调时显示界面

程序代码仅供初学的同学参考学习,代码可以根据自己的风格进行相应的优化处理,仅供参考。

举报
评论 0