历史上的今天
返回首页

历史上的今天

今天是:2025年04月16日(星期三)

正在发生

2020年04月16日 | 单片机 简单时钟

2020-04-16 来源:eefocus

一、简述

通过Proteus仿真电路,用7段数码管实时显示当前时间时间。


工程文件:链接:https://pan.baidu.com/s/1CkKAF8h2dzn-bMoqgL-mhQ 密码:i3a2


二、效果

三、工程结构


1、Keil 工程

2、仿真电路图

四、源文件


clock.c文件


#include

#include

#define uint unsigned int

#define uchar unsigned char

uint time_t;//毫秒统计值

uchar hour,min,sec;//数码管显示值,小时,分,秒

uchar code led[10]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6};//数码管显示0-9

 

/********************************************************************/

//函数名:delay_1ms(uint x)

//功能:利用定时器0精确定时1ms; 自加 time_t的值为后面时间调整函数服务

//调用函数:

//输入参数:x,1ms计数

//输出参数:

//说明:延时的时间为1ms乘以x

/********************************************************************/

void delay_1ms(uint x)

{

TMOD=0X01;//开定时器0,工作方式为1

TR0=1;//启动定时器0;

while(x--)

{

TH0=0Xfc;//定时1ms初值的高8位装入TH0

TL0=0X18;//定时1ms初值的低8位装入TL0

while(!TF0);//等待,直到TF0为1

TF0=0;    //重置溢出位标志

time_t++;//毫秒统计值自加1

}

TR0=0;//停止定时器0;

}

/**********************************************************/

//函数名:display_num(uchar num,dis_w)

//功能:数码管显示

//调用函数:delay_1ms(uint x)

//输入参数:num,dis_w

//输出参数:

//说明:P0口做数码管段选,P2口做位选

//通过dis_w的值确定num值在数码管上显示的位置

/**********************************************************/

void display_num(uchar num,dis_w)

{

uchar j;

for(j=0;j<2;j++)

{

P0=0xff;//段选口置高,消影 ,设置前保证7段二极管是灭的状态

P2=~dis_w;//装入位选值 (低电平选中)

if(j)

P0=~led[num/10];//显示num个位

else

P0=~led[num%10];//显示num十位

dis_w=dis_w<<1;    //显示十位后显示个位

delay_1ms(2);//延时2ms

}

}

/**********************************************************/

//函数名:display_char()

//功能:显示时间分隔符“-”

//调用函数:delay_1ms(uint x)

//输入参数:

//输出参数:

//说明:

/**********************************************************/

void display_char()

{

P0=0xff;//段选口置高,消影

P2=~0x24; //P2端口的十六进制~0x24转换成二进制为11011011可知,点亮的是第3、第6两只数码管

P0=~0x02;//显示字符“-” ,P0端口的十六进制值~0x02值转换为二进制为11111101,是接数码管的g脚,亮的效果刚好是“-”

}

 

void main()

{

SYSTEMTIME CurrentTime; //时间结构体

 

while(1)

{

//获取当前时间

DS1302_GetTime(&CurrentTime);

sec=CurrentTime.Second; //获取当前秒数

min=CurrentTime.Minute; //获取当前分钟数

hour=CurrentTime.Hour;  //获取当前小时数

display_num(sec,0x01);//显示秒 00000001 右边第1个数码管开始显示

display_num(min,0x08);//显示分 00001000 右边第4个数码管开始显示

display_num(hour,0x40);//显示小时  01000000 右边第7个数码管开始显示

display_char();//显示分隔符“-”

}

}

realTime.h文件


#ifndef _REAL_TIMER_H_

#define _REAL_TIMER_H_

 

sbit  DS1302_CLK = P1^6;              //实时时钟时钟线引脚 

sbit  DS1302_IO  = P1^7;              //实时时钟数据线引脚 

sbit  DS1302_RST = P1^5;              //实时时钟复位线引脚

sbit  ACC0 = ACC^0;

sbit  ACC7 = ACC^7;

 

typedef struct __SYSTEMTIME__

{

unsigned char Second;

unsigned char Minute;

unsigned char Hour;

unsigned char Week;

unsigned char Day;

unsigned char Month;

unsigned char  Year;

unsigned char DateString[9];

unsigned char TimeString[9];

}SYSTEMTIME; //定义的时间类型

 

#define AM(X) X

#define PM(X) (X+12)              // 转成24小时制

#define DS1302_SECOND 0x80

#define DS1302_MINUTE 0x82

#define DS1302_HOUR 0x84 

#define DS1302_WEEK 0x8A

#define DS1302_DAY 0x86

#define DS1302_MONTH 0x88

#define DS1302_YEAR 0x8C

#define DS1302_RAM(X) (0xC0+(X)*2)    //用于计算 DS1302_RAM 地址的宏 

 

void DS1302InputByte(unsigned char d) //实时时钟写入一字节(内部函数)

    unsigned char i;

    ACC = d;

    for(i=8; i>0; i--)

    {

        DS1302_IO = ACC0;            //相当于汇编中的 RRC

        DS1302_CLK = 1;

        DS1302_CLK = 0;

        ACC = ACC >> 1; 

    } 

}

 

unsigned char DS1302OutputByte(void) //实时时钟读取一字节(内部函数)

    unsigned char i;

    for(i=8; i>0; i--)

    {

        ACC = ACC >>1;          //相当于汇编中的 RRC 

        ACC7 = DS1302_IO;

        DS1302_CLK = 1;

        DS1302_CLK = 0;

    } 

    return(ACC); 

}

 

 

unsigned char Read1302(unsigned char ucAddr) //读取DS1302某地址的数据

{

    unsigned char ucData;

    DS1302_RST = 0;

    DS1302_CLK = 0;

    DS1302_RST = 1;

    DS1302InputByte(ucAddr|0x01);        // 地址,命令 

    ucData = DS1302OutputByte();         // 读1Byte数据

    DS1302_CLK = 1;

    DS1302_RST = 0;

    return(ucData);

}

 

 

void DS1302_GetTime(SYSTEMTIME *Time)

{

unsigned char ReadValue;

ReadValue = Read1302(DS1302_SECOND);

Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_MINUTE);

Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_HOUR);

Time->Hour = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_DAY);

Time->Day = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_WEEK);

Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_MONTH);

Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

ReadValue = Read1302(DS1302_YEAR);

Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);

}

 

#endif

 


五、总结


1.数码管显示数字机理


数码管是由7段发光二极管组成的,可通过加电平的高低来控制发光二极管的导通与截止。对7个二极管进行合理地组合控制,将会得到0~9这10种数字的显示效果。本工程使用共阳数码管,即低电平时二极管亮。

2、动态显示(我们没有这么多的端口来一 一对应控制8个数码管,所以采用动态显示,每次控制一个数码管)


用P0端口输出一个数字的段码,用P2端口选择那个数码管。一共有6个数字2个“-”(如:13-30-12显示13点30分12秒)


每一次显示一个字符,只要间隔时间足够短,肉眼就能看到所有的数字。


采用一个机制协调段码、位码输入,控制某字符在某位显示,其它各位熄灭,只要每位数码管显示——熄灭——显示,这显示到显示间隔足够短,由于数码管的余辉特性和人眼有视觉暂留现象,给人眼的视觉印象就会是连续稳定地显示。


3、没有精确同步,有待改进。


利用定时器简单的实现简单时钟,并使用按键简单的设置时间:


链接: https://pan.baidu.com/s/1cxenVFHhL2_WAJ8dFqb2uw 提取码: vemy 


效果:

keil:

proteus仿真文件:

代码: 


#include

 

#define uint unsigned int

#define uchar unsigned char

uint ms;//微秒

uchar hour,min,sec;//数码管显示值,小时,分,秒

uchar code led[10]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6};//数码管显示0-9

 

sbit key_Hour = P1^0;//用来控制小时的按键

sbit key_Min = P1^1; //用来控制分钟的按键

sbit key_Sec = P1^2;//用来控制秒的按键

 

/*************************************************

函数功能:软件消抖延时   

**************************************************/

void delay( uint s)

{

   uchar i,j;

        for(i=0;i          for(j=0;j<60;j++)

            ;

}

 

/**********************************************************/

//函数名:display_num(uchar num,dis_w)

//功能:数码管显示

//调用函数:delay_1ms(uint x)

//输入参数:num,dis_w

//输出参数:

//说明:P0口做数码管段选,P2口做位选

//通过dis_w的值确定num值在数码管上显示的位置

/**********************************************************/

void display_num(uchar num, uint dis_w)

{

uchar j;

for(j=0;j<2;j++)

{

P0=0xff;//段选口置高,消影 ,设置前保证7段二极管是灭的状态

P2=~dis_w;//装入位选值 (低电平选中)

if(j)

P0=~led[num/10];//显示num个位

else

P0=~led[num%10];//显示num十位

dis_w=dis_w<<1;    //显示十位后显示个位

delay(2);//延时

}

}

/**********************************************************/

//函数名:display_char()

//功能:显示时间分隔符“-”

//调用函数:delay_1ms(uint x)

//输入参数:

//输出参数:

//说明:

/**********************************************************/

void display_char()

{

P0=0xff;//段选口置高,消影

P2=~0x24; //P2端口的十六进制~0x24转换成二进制为11011011可知,点亮的是第3、第6两只数码管

P0=~0x02;//显示字符“-” ,P0端口的十六进制值~0x02值转换为二进制为11111101,是接数码管的g脚,亮的效果刚好是“-”

}

 

void Timer0(void) interrupt 1 //定时器0中断处理函数(即当定时器溢出的时候会执行该函数)

{

TR0=0;   //关闭定时器0

TF0=0;    //重置溢出位标志

 

ms++; //毫秒值+1

if(ms%130==0)  //每隔一段时间检测一下按键是否有按下,(每1ms中断一次,这个时间太频繁,所以要加if判断一下,不能每1ms就检查)

{

if(key_Hour == 0)  //检测 小时 按键有没有按下

{

delay(100);//按键消抖

hour++;

if(hour >= 24)

{

hour = 0;

}

}

else if(key_Min == 0)   //检测 分钟 按键有没有按下

{

delay(100);

min++;

if(min >= 60)

{

min = 0;

}

}

else if(key_Sec == 0) //检测 秒 按键有没有按下

{

delay(100);

sec++;

if(sec >= 60)

{

sec = 0;

}

}

}

 

//重新装值

TH0=0Xfc;//定时1ms初值的高8位装入TH0

TL0=0X18;//定时1ms初值的低8位装入TL0

 

TR0=1;//启动定时器0;

}

 

 

void main()

{

    ms = 0;

TMOD=0X01;//开定时器0,工作方式为1

//定时器赋初值值

TH0=0Xfc;//定时1ms初值的高8位装入TH0

TL0=0X18;//定时1ms初值的低8位装入TL0

 

//设置开始的时间

sec = 45; //秒数

min = 58; //分钟数

hour = 21;  //小时数

 

ET0=1; //允许外部中断0

EA=1;  //开启总中断

TR0=1;//启动定时器0;

 

while(1)

{

display_num(sec,0x01);//显示秒 00000001 右边第1个数码管开始显示

display_num(min,0x08);//显示分 00001000 右边第4个数码管开始显示

display_num(hour,0x40);//显示小时  01000000 右边第7个数码管开始显示

display_char();//显示分隔符“-”

    //调整时间

if( ms >= 1000 )  //1000ms = 1s

{

ms = 0;

sec++;

if(sec >= 60)//60秒 进位1分

{

sec = 0;

min++;//分钟数加1

if(min >= 60)//60 分钟小时加1

{

min = 0;

hour++;

if(hour >= 24)

{

hour = 0;

}

}

}

}

}

}

 


拿到老师的例子的时候我也没有注意到年月日,例子中,年测的是后面2位,比如2019测出的是19小时是24制的,所以有两位数

测试年月日

测试星期:

西方将星期日作为星期的第一天,


星期日 星期一 星期二 星期三 星期四 星期五 星期六

   1     2      3      4      5      6      7

上传文件大小限制为5M,所以将测试效果放到网盘:


链接: https://pan.baidu.com/s/1hJOa6qJWBYuXjlZ-BFovow 提取码: b9gu

推荐阅读

史海拾趣

联智(Celfras)公司的发展小趣事

江西联智集成电路有限公司(简称联智)自2016年成立以来,始终致力于无线充电技术的研发和创新。公司凭借其先进的无线充电TX/RX芯片解决方案,迅速在行业内崭露头角。联智不仅拥有由多名高校博士和高级研发人员组成的核心研发团队,还在中国及韩国设立了研发中心,确保技术的领先性和市场竞争力。

Crocus Technology公司的发展小趣事

Crocus Technology的创立源于2006年法国格勒诺布尔的自旋电子学Spintec研究实验室。这家公司在创立之初就面临着巨大的挑战,因为磁阻随机存取存储器(MRAM)市场当时竞争激烈,众多公司因技术难题和市场定位不当而相继倒闭。然而,Crocus Technology凭借其独特的技术视角和坚定的市场信念,成功地在这场竞争中生存下来。

EHC(ELECTRONICHARDWARE)公司的发展小趣事

在电子行业中,产品质量是企业生存和发展的关键。EHC公司深知这一点,因此一直将品质管理作为企业发展的重要战略。公司建立了完善的质量管理体系,从原材料采购到生产制造再到产品出厂,每一个环节都经过严格的把控。这种对品质的坚持和追求让EHC公司的产品在市场上赢得了广泛的信任和认可。

承兴(CX)公司的发展小趣事

1996年,承兴公司在香港成立,并迅速设立了亚洲总部。起初,公司专注于电子产品的代理和销售,凭借其敏锐的市场洞察力和高效的供应链管理能力,承兴逐渐在亚洲市场崭露头角。通过与宝洁中国、百事中国等知名品牌建立合作关系,承兴获得了稳定的供应链资源和市场份额。

EPIC公司的发展小趣事

随着公司的发展,Epic Games开始通过收购和投资来扩展其业务范围。2007年8月20日,Epic Games收购了一家位于波兰的游戏开发商People Can Fly,并成为其第一大股东。这次收购不仅为Epic Games带来了更多的开发资源,也使其在游戏开发领域的实力得到了进一步提升。此外,Epic Games还积极投资其他有潜力的游戏开发商和团队,以推动整个游戏行业的创新和发展。

Axiomtek公司的发展小趣事

2006年,Epic Games公布了基于Xbox 360游戏平台的《战争机器》。这款游戏以其出色的画面效果、紧张刺激的战斗场景和深入人心的故事情节,迅速在市场上取得了成功。它的畅销不仅进一步巩固了Epic Games在游戏开发领域的地位,也证明了虚幻引擎在开发高质量游戏方面的强大能力。

问答坊 | AI 解惑

单片机控制GSM模块

我最近在做单片机控制GSM模块的实验,先做的是单片机通过串口与GSM模块连接,谁知道单片机通过串口发送什么数据可以发送短信呢? 希望有高手指点!!!…

查看全部问答>

wince u盘?FAT32?

尊敬的斑竹及各位大牛们好,小弟一直关注次论坛!现在想问个问题?我的wince5.0系统现在可以识别u盘了,但是如果是FAT32格式的u盘识别时间要1分半钟(有时候可能还长),如果是FAt格式的u盘很快就识别了时间大概是10秒左右,我已经测试了n遍都是这样 ...…

查看全部问答>

高分求hotkey driver 工作原理

小弟请教一下hotkey driver 工作原理,比如thinkpad hotkey driver  ,他的工作原理是什么?是EC发送出来的数据吗?为什么能感应到FN+FX的键值呢,我做了一个键盘过滤驱动程序,在笔记本上安装后,可是还是抓不到FN+FX的键值,是不是FN+FX根本就 ...…

查看全部问答>

委托开发工业机器人控制系统,有兴趣的请回复QQ79500116

委托开发工业机器人控制系统,有兴趣的请回复QQ79500116…

查看全部问答>

Windows ce 下的一个简单的驱动设计问题

我的设备的物理地址基址是0x3800 0000,映射成虚拟地址是0x9000 0000 和0x B000 0000 我设计的流接口驱动程序使用VirtualAlloc()函数、VirtualCopy()以及MmMapIoSpace()函数应该怎么映射地址?…

查看全部问答>

uboot移植不进开发板,不知是何问题

uboot移植不进开发板(OMAP核的),不知是不是JTAG除了什么问题? 下面是报错详情: Error connecting to the target: Error 0x80000240/-180 Fatal Error during: Initialization, OCS, This error was generated by TI\'s USCIF driver. ...…

查看全部问答>

关于COM口的问题

串口硬件资源的问题,它有个输入/输出范围是03F8-03FF,但是书上好象只说串口芯片8251A只有两个字节的范围,就是命令/状态字缓冲器和数据缓冲器呀,这这么解释呢?还有就是想弄明白输入/输出地址范围和内存范围,网卡也是IO设备,怎么会变成内存范围呢?…

查看全部问答>

求教M3高速AD采样的实现

本人菜鸟,请高手指点。有个M3(lm3s8962)小系统,要高速进行AD采样搜集,>=600K 采样频率。AD本身采样频率大于100MSPS,再加上SDRAM做数据缓冲。请问用M3能否实现?采用什么接口接受AD数据。SDRAM那里有什么要注意的?谢谢!…

查看全部问答>

【信号处理】:FPGA在数字信号处理平台中的应用

信号采集和回放的模块化设计成为现代数字信号处理系统实现的一个趋势,随着集成电路工艺水平的不断发展,现场可编程门阵列芯片(EPGA)和数字信号处理芯片(DSP)性能不断提高,为系统的模块化设计提供了器件保证。本文结合某雷达干扰机的高速数据采集 ...…

查看全部问答>