51单片机的数字钟开发

一:源程序解读

我是在源程序的基础上进行的改动,改动的地方我会进行说明,下面的顺序也是我学习的顺序

1.数码管位选

[1]数码管点亮位置的选择

数码管的段选是使用了3-8线译码器,省下了单片机的5个端口。
在这里插入图片描述
在程序中要先定义引脚

sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

[2] 数码管点亮位置的函数

void DigDisplay()
{
	u8 i=0;	 //无符号整形i有2^8=16*16=256个状态,所以表示范围是0-255。
	for(i=0;i<8;i++)
	{
		switch(i)	 //位选,选择点亮的数码管。我把i改成递减的,更符合看的习惯--最右边是秒。
		{
			case(7):
				LSA=0;LSB=0;LSC=0; break;//显示第0位(右边低位权重)
			case(6):
				LSA=1;LSB=0;LSC=0; break;//显示第1位
			case(5):
				LSA=0;LSB=1;LSC=0; break;//显示第2位
			case(4):
				LSA=1;LSB=1;LSC=0; break;//显示第3位
			case(3):
				LSA=0;LSB=0;LSC=1; break;//显示第4位
			case(2):
				LSA=1;LSB=0;LSC=1; break;//显示第5位
			case(1):
				LSA=0;LSB=1;LSC=1; break;//显示第6位
			case(0):
				LSA=1;LSB=1;LSC=1; break;//显示第7位(左边最高位)	
		}
		P0=TempData[i];//发送段码
		delay(100); //间隔一段时间扫描	
		P0=0x00;//消隐
	//delay(1000);
	}
} 

1.数码管段选

在这里插入图片描述

unsigned char TempData[8]; //存储时间显示值的全局变量 	

TempData[8]分别映射到下面8个变量上

	 TempData[0]=dofly_DuanMa[hour/10]; //时	//数据的转换,因我们采用数码管0~9的显示,将数据分开
	 TempData[1]=dofly_DuanMa[hour%10];
	 TempData[2]=0x40;		                   //加入"-"
	 TempData[3]=dofly_DuanMa[minute/10];//分
	 TempData[4]=dofly_DuanMa[minute%10];
	 TempData[5]=0x40;
	 TempData[6]=dofly_DuanMa[second/10];//秒
	 TempData[7]=dofly_DuanMa[second%10];	
   
	DigDisplay();

这段程序写的很妙,妙就妙在对时间数值做出的处理上。

  • L在verilog HDL 中,受限与一个变量不能同时出现在两个地方(因为电路是具体的存在),是直接将时间映射到数码管上的,
  • 而此处C语言中,就是在直接映射的中间再加了一个数组。(一个变量可以在不同的地方使用)用数学函数的语言来说就是,加了一个复合函数,而这个复合函数的外函数,是怎么求出来的,这一点是很妙的。仔细想想为什么模10,取余呢?
unsigned char code dofly_DuanMa[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};// 显示段码值0~9,也就是选中的数码管显示的内容
unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//位选,一次点亮一个数码管

u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
					0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值

下面是定时器的中断程序,定时器归根结底还是计数器。
计数器:就是对外部脉冲系统时钟12分频的内部脉冲 进行计数。

  • 外部脉冲:如T0 接的P3.4引脚,T1 接的P3.5引脚。
  • 内部脉冲:习惯上把晶振震动12次的时间称为一个机器周期。一个机器周期里单片机只完成一条指令。一个机器周期计数器自动+1
    - 系统时钟为12MHZ时,震动一下是1/12um,则震动12次就是1um,即机器周期就是1um
    - 系统时钟为11.0592MHZ时,那么一个机器周期=12/11.0592=1.085069444循环,虽然这里算出来的无限小数蛮恶心,但是11.0596在分频16分频产生波特率上就会特别漂亮。

波特率:码元传输速率RB,每秒发送码元的个数,码元由0和1构成的,01的个数取决于码元为几进制编码。
比特率:信息传输速率Rb,每秒发送0和1的个数。


/*------------------------------------------------
                 定时器中断子程序
------------------------------------------------*/
void Timer0_isr(void) interrupt 1  //定时器T0的中断号对应为1(查表,主要用于指明中断源的)
{
 static unsigned int num,i; 
 TH0=(65536-2000)/256;//重新赋值 2ms(16位定时器就是2^16=65536微秒,减去2000微秒剩下的就是定时器的初值,分别对低八位高八位=2^8次方取模取余)
 TL0=(65536-2000)%256;
 
 // DigDisplay();// 调用数码管扫描
 //--------------------------------------
 // 只用当计时器计2000个1us,即每2ms,才+1
i++;
 if(i==10)        	  //20ms更新一次
    {
	i=0;
	UpdateTimeFlag=1; 	//更新时间志位置1
	}
//--------------------------------------
// 只用当计时器计2000个1us,即每2ms,才+1
 num++;

 if(num==500)        //大致1s
   {
    num=0;
	second++;
	if(second==60) 		 //秒到60,分钟加1
	 {
	  second=0;	  
	  minute++;
	  if(minute==60)	//分钟到60,小时加1
	    {
		 minute=0;
		 hour++;
		 if(hour==24)		//小时到24,回零
		   hour=0;
		}
	 }
   
  }
}

但是使用定时中断的时候要对定时器进行初始化

/*------------------------------------------------
                    定时器初始化子程序
------------------------------------------------*/
void Init_Timer0(void)  
{
 TMOD |= 0x01;	  //使用定时器模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响		     
 //TH0=0x00;	      //给定初值
 //TL0=0x00;
 EA=1;            //总中断打开
 ET0=1;           //定时器中断打开
 TR0=1;           //定时器开关打开
}

按键控制

/*------------------------------------------------
按键扫描函数,返回扫描键值
----------------------------------------------*/
unsigned char KeyScan(void)
{
 unsigned char keyvalue;
 if(KeyPort!=0xff)
   {
    DelayMs(10);
    if(KeyPort!=0xff)
	   {
	    keyvalue=KeyPort;
	    while(KeyPort!=0xff);//延时消抖了之后在检测如果还有执行下面语句,(正常人的反应按一个按键要持续几百ms)
		switch(keyvalue)
		{
		 case 0xfe:return 1;break;
		 case 0xfd:return 2;break;
		 case 0xfb:return 3;break;
		 case 0xf7:return 4;break;
		 case 0xef:return 5;break;
		 case 0xdf:return 6;break;
		 case 0xbf:return 7;break;
		 case 0x7f:return 8;break;
		 default:return   0;break;
		}
	  }
   }
   return 0;
}

下面对应不同的取值有不同的函数


Key_Num=KeyScan();
switch(Key_Num)
	{
				case 1:hour++;if(hour==24)hour=0;     //正常时间 小时 加1
						 break;
				case 2:hour--;if(hour==255)hour=23;   //正常时间 小时减1
					 break;
				case 3:minute++;if(minute==60)minute=0;     //分钟加1
					 break;
				case 4:minute--;if(minute==255)minute=59;   //分钟减1
					 break;
				default:break;
	} 

总结一下

每次运行 DigDisplay()函数,都会让*P0=TempData[i]*中的i值从0到7变化,不同的i对对应不同的时间信息,此外了位方便映射到相应的数码管上了,i也会让相应的信息单独显示,这是通过switch(i)实现的(如从右往左数,第i=0个数码管显示,秒的低位,让i=1显示秒的高位,让i=2显示横线·····)。这样利用了视觉暂留的效果做到了,让一排数码管显示不同的数。
如何让数码管走动,只需要隔一秒改变一下时间变量TempData的取值。用到定时器,装上初值后让定时器,恰好经过1s后溢出,运行中断服务函中的内容,改变时间变量,也就是让对应的时间+1。

二:程序修改

看懂了源程序的基础上改我们要添加相应的功能。

  1. 数字钟应该具有不同的模式(如走钟模式,息屏模式,闹钟模式)
  2. 不同的模式对应时间的调整

不同模式的选择

闹钟模式

设一个新的变量存放,响铃时刻,如果时间到了,就让那种闪烁

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值