单片机 简单时钟

2019-04-15 17:20发布

单片机 简单时钟

一、简述     通过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、没有精确同步,有待改进。   ---------------------------------------回复qq_43795753 这位兄弟----------------------------------------- 利用定时器简单的实现简单时钟,并使用按键简单的设置时间: 链接: 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= 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; } } } } } }              ================以下回复 Yuki_fx ============ 拿到老师的例子的时候我也没有注意到年月日,例子中,年测的是后面2位,比如2019测出的是19 小时是24制的,所以有两位数 测试年月日 测试星期: 西方将星期日作为星期的第一天,   星期日   星期一     星期二    星期三     星期四      星期五      星期六       1           2              3             4              5                6              7 上传文件大小限制为5M,所以将测试效果放到 {MOD}: 链接: https://pan.baidu.com/s/1hJOa6qJWBYuXjlZ-BFovow 提取码: b9gu