51单片机入门——(新)简易数字时钟

发布者:知识智慧最新更新时间:2022-06-14 来源: eefocus关键字:51单片机  入门 手机看文章 扫描二维码
随时随地手机看文章

设计要求

实现正确稳定地显示小时(两位数)、分钟(两位数)、秒钟(两位数),同时数码管应无闪烁问题。


通过按键分别实现时、分信息的调整,方便用户对时间的校准。


加入闹铃功能在(本设计中用LED代替)。

原理图

在这里插入图片描述

按键部分介绍

在这里插入图片描述

key1用于切换时分秒的加减。例:第一次按下后,按key2key3时“秒”加减,第二次按下后,按key2key3时“分”加减,第三次按下后,按key2key3时“时”加减,第四次按下后,按key2key3不起作用,依次循环。


key2用于加。


key3用于减。


key4用于切换时间显示和闹钟显示切换。


代码解析

mian.c


#include

#include "SMG.H"


sbit KEY1 = P3^0;

sbit KEY2 = P3^1;

sbit KEY3 = P3^2;

sbit KEY4 = P3^3;  

sbit LED = P3^4;


uchar T0RH = 0 ; // T0 重载值的高字节

uchar T0RL = 0 ; // T0 重载值的低字节


uchar sec =10, min = 10, hour = 10; // 初始化时分秒

uchar alarmSec, alarmMin, alarmHour; // 闹铃时间

bit alarm = 0; // 闹铃标志位

bit key1,key2,key3,key4; // 按键标志位

uchar b = 0; // 切换标志位


void ConFigTimer0(uchar ms);

void LedDriver();

void KeyDriver();


void main()

{

EA = 1;

ConFigTimer0(2);

while (1)

{

LedDriver();

KeyDriver();

(alarmMin == min && alarmHour == hour) ? (LED = 1) : (LED = 0);

}

}


void LedDriver()

{

static unsigned char i;

if (!alarm)

{

switch (i)

{

case 0 : ledBuff[0] = ledChar[sec % 10]; break;

case 1 : ledBuff[1] = ledChar[sec / 10]; break;

case 2 : ledBuff[2] = ledChar[10]; break;

case 3 : ledBuff[3] = ledChar[min % 10]; break;

case 4 : ledBuff[4] = ledChar[min / 10]; break;

case 5 : ledBuff[5] = ledChar[10]; break;

case 6 : ledBuff[6] = ledChar[hour % 10]; break;

case 7 : ledBuff[7] = ledChar[hour / 10]; break;

}

}

else

{

switch (i)

{

case 0 : ledBuff[0] = ledChar[alarmSec % 10]; break;

case 1 : ledBuff[1] = ledChar[alarmSec / 10]; break;

case 2 : ledBuff[2] = ledChar[10]; break;

case 3 : ledBuff[3] = ledChar[alarmMin % 10]; break;

case 4 : ledBuff[4] = ledChar[alarmMin / 10]; break;

case 5 : ledBuff[5] = ledChar[10]; break;

case 6 : ledBuff[6] = ledChar[alarmHour % 10]; break;

case 7 : ledBuff[7] = ledChar[alarmHour / 10]; break;

}

}

i ++;

i &= 0x07;

}

void KeyDriver()

{


if (KEY1 == 0 && key1 == 0)

{

key1 = 1;

}

else if (KEY1 == 1 && key1 == 1)

{

key1 = 0;

b ++;

b &= 0x03;

}


if (KEY2 == 0 && key2 == 0)

{

key2 = 1;

}

else if (KEY2 == 1 && key2 == 1 )

{

key2 = 0;

if(!alarm)

{

switch (b)

{

case 1 : sec ++; break;

case 2 : min ++; break;

case 3 : hour ++; break;

}

}

else

{

switch (b)

{

case 1 : alarmSec ++; break;

case 2 : alarmMin ++; break;

case 3 : alarmHour ++; break;

}

}

}


if (KEY3 == 0 && key3 == 0)

{

key3 = 1;

}

else if (KEY3 == 1 && key3 == 1 )

{

key3 = 0;

if(!alarm)

{

switch (b)

{

case 1 : sec --; break;

case 2 : min --; break;

case 3 : hour --; break;

}

}

else

{

switch (b)

{

case 1 : alarmSec --; break;

case 2 : alarmMin --; break;

case 3 : alarmHour --; break;

}

}

}


if (KEY4 == 0 && key4 == 0)

{

key4 = 1;

}

else if (KEY4 == 1 && key4 == 1)

{

key4 = 0;

alarm = ~alarm;

}


}


/* 配置并启动T0 ,12MHz */

void ConFigTimer0(uchar ms)

{

ulong tmp ;


tmp = 12000000 / 12;

tmp = (tmp * ms) / 1000;

tmp = 65536 - tmp;

tmp += 2;

T0RH = (uchar)(tmp >> 8);

T0RL = (uchar)tmp;

TMOD &= 0xF0;

TMOD |= 0x01;

TH0 = T0RH;

TL0 = T0RL;

ET0 = 1;

TR0 = 1;

}


void InterruptTimer0() interrupt 1 

{

uint i;

TH0 = T0RH;

TL0 = T0RL;

LedScan();

i ++;

if (i >= 500)

{

i = 0;

sec ++;

if (sec >= 60)

{

sec = 0;

min ++;

if (min >= 60)

{

min = 0;

hour ++;

if (hour >= 24)

hour = 0;

}

}

}


SMG.H


#ifndef _SMG_H_

#define _SMG_H_


#include


typedef unsigned char uchar ;

typedef unsigned int uint ;

typedef unsigned long ulong ;


extern uchar ledChar[12], ledBuff[8];


extern void LedScan();


#endif


SMG.C


#include"SMG.H"


uchar ledChar[12] = { //共阴极数码管显示字符转换表

0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x40, 0x00

};


uchar ledBuff[8] = { //数码管显示缓冲区

 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

};


/* 数码管动态扫描刷新函数,需在定时中断中调用 */

void LedScan()

{

static uchar i = 0; //动态扫描的索引

 想·

P0 = ledChar[11]; //显示消隐

P2 = ~(0x80 >> i);

P0 = ledBuff[i];

i ++;

i &= 0x07;

}


在学习数码管动态扫描的时候,为了方便大家理解,我们程序写的细致一些,给大家引入了 switch 的用法,随着编程能力与领悟能力的增强,我们在编程上也可以改进一下逻辑算法,让程序变的更简洁。这种逻辑算法,通常不是靠学一下可以全部掌握的,而是通过不断的编写程序以及研究他人程序的过程中一点点积累起来的。


前边动态扫描刷新函数我们是这么写的:


P0 = 0x00;

switch (i)

{

case 0 : P2 = 0xfe; P0 = ledTemp[0]; break;

case 1 : P2 = 0xfd; P0 = ledTemp[1]; break;

case 2 : P2 = 0xfb; P0 = ledTemp[2]; break;

case 3 : P2 = 0xf7; P0 = ledTemp[3]; break;

case 4 : P2 = 0xef; P0 = ledTemp[4]; break;

case 5 : P2 = 0xdf; P0 = ledTemp[5]; break;

case 6 : P2 = 0xbf; P0 = ledTemp[6]; break;

case 7 : P2 = 0x7f; P0 = ledTemp[7]; break;

}

i ++;

i &= 0x07;


我们来分析每一个 case 分支,它们的结构是相同的,即改变 P2、改变索引 i、取数据写入 P0,只要把 case 后的常量与 P2和 LedBuff 的下标对比,就可以发现它们其实是相等的,那么我们可以直接把常量值(实际上就是 i 在改变前的值)赋值给它们即可,而不必写上 6 遍。还剩下一个 i 的操作,它进行了 7 次相同的++与一次归 0 操作,那么很明显用++和 if 判断就可以替代这些操作。下面就是我们据此改进后的代码:


P0 = 0x00; //显示消隐

P2 = ~(0x80 >> i);

P0 = ledBuff[i];

i ++;

i &= 0x07;


大家看一下,P2 = ~(0x80 >> i);这行代码就利用了上面讲到的~和>>,利用右移运算符来实现1的右移,因为我们使用的是共阴数码管,所以有一个取反来实现0的右移,而 P0 的赋值也只需要一行代码,i 的处理也很简单。这样写成的代码是不是要简洁的多,也巧妙的多,而功能与前面的 switch 是一样的,同样可以完美实现动态显示刷新的功能。


而且一般的if在该代码中我们也换成了&=,例如i &= 0x07;b &= 0x03;在这里我是要让b 在 0~3 之间变化,加到 4 就自动归零,i在0~7之间变化,加到8就自动清零按照常规你可以用前面讲过的 if 语句轻松实现,但是你现在看一下这样程序是不是同样可以做到这一点呢?因为 0、1、2、3 这四个数值正好占用 2 个二进制的位(0~7刚好占3个),所以我们把一个字节的高 6 (5)位一直清零的话,这个字节的值自然就是一种到 4 (8)归零的效果了。看一下,这样一句代码比 if 语句要更为简洁吧,而效果完全一样。

关键字:51单片机  入门 引用地址:51单片机入门——(新)简易数字时钟

上一篇:51单片机入门——步进电机
下一篇:51单片机入门——16路抢答器

推荐阅读最新更新时间:2024-11-12 23:46

51单片机演奏乐曲的程序
sbit SPK = P1^7 ;指定扬声器接口 ORG 0000H LJMP START ORG 000BH LJMP T0_INT ORG 001BH LJMP T1_INT ;------------------------------------------------------------ B_ZQ_TAB: ;定时半周期初始值数据表(数据的意义可见前一篇博文) DW 63625, 63833, 64019, 64104, 64260, 64400, 64524 ;低音区:1 2 3 4 5 6 7 DW 64580,
[单片机]
C51单片机0~60计数器
源码 #includeunsigned char code table ={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; unsigned char second=0; bit a=0; char t=0; int0_srv() interrupt 1{ t++; if(t==20){t=0;a=1;} TH0=0x3c; TL0=0xb0; } main(){ P0=P2=table ; TMOD=0x01; TH0=0x3c; TL0=0xb0; ET0=1; EA=1; TR0=1; while(1){ if(a){
[单片机]
C<font color='red'>51单片机</font>0~60计数器
16-基于51单片机的烟雾和温湿度检测控制系统仿真
具体实现功能 由STC89C52单片机+LCD1602液晶显示屏+ADC0832模块+蜂鸣器+DHT11温湿度传感器 +烟雾传感器+LED+按键构成。 具体功能: 1、LCD1602液晶第一行显示当前的烟雾值,第二行显示当前的温度和湿度值; 2、可以设置烟雾、温湿度上下限报警值。共4个按键:复位按键、减键、加键、设置键;设定的参数具有掉电保存,保存在STC单片机的内部,上电无需重新设置; 3、当烟雾值高于设定的报警值或温度湿度超出上下限范围,蜂鸣器和指示灯会发出声光报警; 4、当温湿度值低于或高于设定的范围时,相应的指示灯亮,蜂鸣器报警; 5、当同时满足烟雾过高、温度过高、湿度过低三个条件时,控制继电器吸合
[单片机]
16-基于<font color='red'>51单片机</font>的烟雾和温湿度检测控制系统仿真
8051单片机实战分析(以STC89C52RC为例) | 07 - 独立按键驱动
按键是比较常用的也是比较简单的人机交互操作,按键实际上是一个非自锁的轻触开关,按下时触点会闭合,松开是触点断开。 1 独立式按键 常用的按键电路有两种形式,独立式按键和矩阵式按键,独立式按键比较简单,它们各自与独立的输入线相连接,如图所示。 4 条输入线接到单片机的 IO 口上,当按键 K3 按下时,K3两边的线路将会导通,P32这个单片机IO口直接接到GND,此时P32这个引脚就是低电平了。当松开按键后,当松开按键后,线路断开,就不会有电流通过,那么P32引脚就会是默认的电平状态,是一个高电平,因为被上拉电阻R8拉到了VCC状态。所以我们就可以通过 P32 这个 IO 口的高低电平来判断是否有按键按下。 2
[单片机]
80<font color='red'>51单片机</font>实战分析(以STC89C52RC为例) | 07 - 独立按键驱动
基于51单片机的串口控制led灯的亮灭源程序
/*注意设置波特率为4800,向串口不断输入0,便可实现led的亮灭*/ #include reg52.h //此文件中定义了单片机的一些特殊功能寄存器 typedef unsigned int u16; //对数据类型进行声明定义 typedef unsigned char u8; sbit led0=P2^0; sbit led1=P2^1; sbit led2=P2^2; /******************************************************************************* * 函数名 :UsartInit() * 函数功能 :设置串口 * 输入
[单片机]
STM32开发板入门教程(十三) - SPI模式读写SD卡
功能介绍 :使用SPI模式 读写SD卡block数据 可通过串口发送到PC机查看 SD卡是Secure Digital Card卡的简称,直译成汉语就是“安全数字卡”,是由日本松下公司、东芝公司和美国SANDISK公司共同开发研制的全新的存储卡产品。SD存储卡是一个完全开放的标准(系统),多用于MP3、数码摄像机、数码相机、电子图书、AV器材等等,尤其是被广泛应用在超薄数码相机上。呵呵 现在偶们做东西也喜欢用sd卡了 为啥? 容量大啊 价格便宜啊 读写次数100000次以上(也有资料说是300000次 呵呵) 这个次数够猛了啊 读写的速度也很快 现在高速的SD卡写速度可以达到20M/S 呵呵 如果你买的SD卡达不到这个速度
[单片机]
STM32开发板<font color='red'>入门</font>教程(十三) - SPI模式读写SD卡
51单片机入门——16路抢答器
设计要求 同时为16支参赛队提供抢答功能,抢答成功后应能通过数码管显示出参赛队号数,同时点亮发光二极管示意抢答成功。 加入独立开关,可启动10秒倒计时功能,通过数码管显示出倒计时时间(倒计时状态下抢答功能不起作用,反之亦然)。 电路原理图 硬件原理 时钟信号(晶振) 单片机晶振部位电路,详情请参考《51单片机入门——单片机最小系统》,在此项目中我们选择 11.0592 MHz的晶振。 矩阵按键与独立按键 在该项目中矩阵按键用于选手的抢答器,独立按键用于主持人复位重置抢答。 代码解析 矩阵按键部分代码: keyboard.c #include KEYBOARD.H uchar keySta = {
[单片机]
<font color='red'>51单片机</font><font color='red'>入门</font>——16路抢答器
MCS-51单片机内部程序存储器ROM结构详解
MCS-51单片机的程序存储器用于存放编好的程序和表格常数。8051片内有4 KB的ROM,8751片内有4 KB的EPROM,8031片内无程序存储器。 MCS-51的片外最多能扩展64 KB程序存储器,片内外的ROM是统一编址的。如端保持高电平,8051的程序计数器PC在0000H~0FFFH地址范围内 (即前4 KB地址) 是执行片内ROM中的程序,当PC在1000H~FFFFH地址范围时,自动执行片外程序存储器中的程序;当保持低电平时,只能寻址外部程序存储器,片外存储器可以从0000H开始编址。 MCS-51的程序存储器中有些单元具有特殊功能,使用时应予以注意。 其中一组特殊单元是0000H~0002H。系统复位后,(
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved