前几天在网上闲逛发现有人搭棚焊了一个时钟,一时心动想自己也做一个送小姑娘。
说干咱就干,从万能的X宝上下单,几天货到!
先简单画了个草图 开焊,过程就不发图了,看成品。89c52+ds1302+18b20+共阳时钟数码管 串口线直接焊在10、11脚上,写程序。成了!!!
拷了几天机,发现时间不准,平均每天慢十几分钟,查了一下说是晶振没选好,电容没匹配。哎!调了几次没搞好,也不想再调试了。 
有人说DS3231好用,于是换方案。
同样草图
为了焊的好看一点,数码管自定义。 早晨上班,等红灯时发现一女司机抱着狗儿开车。
过程不多,看成品。还是串口线直接下载。底座是电池盒,用三节5号电池。不知能用多久,没测过电工作电流。没有万用表。你信吗,试验室里竟然没有万用表! 后来焊了一个USB接口可以手机充电器供电。
拷机一个月快了55秒!哎!勉强接授吧!
最后附上电路图和程序,这是我做完以后画的,各实际的不太一样,特别是单片机和数码管之间的连接不一样,所以才用了自定义的显示字库,其它的也不知有没有错误。 程序奉上一部分,其它的请下载吧!赚点积分!
单片机源码:
- #include <reg52.h>
- #include<intrins.h>[/color][/align][align=left][color=#000000]#define uchar unsigned char
- #define uint unsigned int[/color][/align][align=left][color=#000000]
- sbit SDA=P2^7; //数据传送位SDA
- sbit SCL=P2^6; //时钟控制位SCL
- sbit INT=P3^3; //中断位
- sbit RESET=P3^2; //复位
- sbit smg4=P1^1; //位码1声明 分个位
- sbit smg3=P1^3; //位码2声明 分十位
- sbit smg2=P1^5; //位码3声明 时个位
- sbit smg1=P1^7; //位码4声明 时十位
- sbit k1=P1^4; //按键k1的声明
- sbit k2=P1^6; //按键k2的声明
- sbit k3=P3^7; //按键k3的声明
- sbit bell=P3^5; //蜂鸣器声明
- sbit gm=P2^1; //测光声明,黑暗时等于1[/color][/align]
- bit gm_en=0; //光敏使能信号;
- bit ack; //应答标志位
- char code dis_code[]={0X28,0XEE,0X32,0XA2,0XE4,0XA1,0X21,0XEA,0X20,0XA0,0X60,0X25,0X37,0X26,0X31,0X71};//数码管没有按a-h的顺序连接,自定义的显示字库,0-9,A-F
- uchar bell_en=0,x=1;//闹钟使能信号,任意键关闭铃声标志位
- uchar tabtime[]={0x00,0x00,0x07,0x02,0x23,0x10,0x18}; //首次上电时默认的时间2018年 星期二 10月23日 07:00:00
- uchar year,month,day,week,hour,minute,second,temperature; //从1302读出的实时时间数据
- uchar gm_time=60,temp_en=0,n=0,a=0,b=0; //gm_time光敏控制亮度值,n是按键标志位,a产生一个1秒的计时,b控制字符闪烁,temp_en温度显示标志
- uint t=0; //用来产生30秒的计时
- uint TtempH,TtempL; //定义温度高位,低位
- char shi,fen,miao,b_shi=7,b_fen=0; //必须使用符号型:数据暂存单元shi/fen/miao,闹铃时间b_shi,b_fen,
- void baojing(); //报警函数
- void delay_50us(uint t); //延时50*T微妙函数的声明
- void Start_I2C(); //I2C总线产生起始信号函数
- void Stop_I2C(); //I2C总线产生停止信号函数
- void Ack_I2C(bit a); //接收应答信号函数;
- void SendByte(uchar c); //向I2C总线写入一个字节的数据函数
- uchar RcvByte(); //字节数据接收函数
- uchar write_byte(uchar addr, uchar write_data);//将一个字节写入DS3231指定地址
- uchar read_byte(uchar addr); //从DS3231指定地址读一个字节
- void Set_Time(); //设置时间
- void get_time(void); //读取时间
- void get_Temperature(); //读取温度
- void show_Temperature(); //显示温度
- void init(); //初始化函数
- void timer0(); //定时器0中断服务程序
- void display(uchar s,f); //显示子程序
- void key(); //按键控制函数
- void baojing()//报警函数
- {
- uint j=10,i=10;
- bell=0;
- while(i--)display(shi,fen);
- bell=1;
- while(j--)display(shi,fen);
- }
- //函数名称:void delay_50US(unsigned int t)
- //功能: 延时50*t(us)
- void delay_50us(uint t)
- {
- unsigned char j;
- for(;t>0;t--)
- {
- for(j=19;j>0;j--);
- }
- }
- void Start_I2C() //I2C总线产生起始信号函数
- {
- SDA=1; //拉高数据线,发送起始条件的数据信号
- SCL=1; //拉高时钟线
- SDA=0; //在时钟线为高电平时,拉低数据线,产生起始信号
- SCL=0; //钳住I2C总线,准备发送或接收数据
- }
- void Stop_I2C() //I2C总线产生停止信号函数
- {
- SDA=0; //拉低数据线,发送结束条件的数据信号
- SCL=1; //拉高时钟线,发送结束条件的时钟信号
- SDA=1; //时钟时线为高电平时,拉高数据线,发送I2C总线结束信号
- }
- /********************************************************************
- 应答子函数
- 函数原型: void I2CACK(bit a);
- 功能: 主控器进行应答信号(可以是应答或非应答信号,由位参数a决定)
- ********************************************************************/
- void Ack_I2C(bit a) //接收应答信号函数;a是定义的一个位变量,来暂存应答状态。
- {
-
- if(a==0)
- SDA=0; //在此发出应答或非应答信号
- else
- SDA=1;
- SCL=1;
- SCL=0; //清时钟线,钳住I2C总线以便继续接收
- }
- /*******************************************************************
- 字节数据发送函数
- 函数原型: void SendByte(uchar c);
- 功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
- 此状态位进行操作.(不应答或非应答都使ack=0)
- ack=1 发送数据正常,
- ack=0 被控器无应答或损坏。
- ********************************************************************/
- void SendByte(uchar c) //向I2C总线写入一个字节的数据函数
- {
- uchar BitCnt;
-
- for(BitCnt=0;BitCnt<8;BitCnt++) //要传送的数据长度为8位
- {
- if((c<<BitCnt)&0x80)
- SDA=1; //判断发送位
- else
- SDA=0;
- SCL=1; //置时钟线为高,通知被控器开始接收数据位
- SCL=0;
- }
-
- SDA=1; //8位发送完后释放数据线,准备接收应答位
- SCL=1;
- if(SDA==1)
- ack=0;
- else
- ack=1; //判断是否接收到应答信号
- SCL=0;
- }
- /*******************************************************************
- 字节数据接收函数
- 函数原型: uchar RcvByte();
- 功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
- 发完后请用应答函数应答从机。
- ********************************************************************/
- uchar RcvByte() //字节数据接收函数
- {
- uchar retc;
- uchar BitCnt;
-
- retc=0;
- SDA=1; //置数据线为输入方式
- for(BitCnt=0;BitCnt<8;BitCnt++)
- {
- SCL=0; //置时钟线为低,准备接收数据位
- SCL=1; //置时钟线为高使数据线上数据有效
- retc=retc<<1;
- if(SDA==1)
- retc=retc+1; //读数据位,接收的数据位放入retc中
- }
- SCL=0;
- return(retc);
- }
-
- /***********将一个字节写入DS3231指定地址***********/
- uchar write_byte(uchar addr, uchar write_data)
- {
- Start_I2C();
- SendByte(0xD0);
- if (ack == 0)
- return 0;
-
- SendByte(addr);
- if (ack == 0)
- return 0;
-
- SendByte(write_data);
- if (ack == 0)
- return 0;
-
- Stop_I2C();
- return 1;
- }
- /************从DS3231指定地址读一个字节************/
- uchar read_byte(uchar addr)
- {
- uchar read_data;
- Start_I2C();
- SendByte(0xD0);
- if(ack==0)
- return(0);
-
- SendByte(addr);
- if(ack==0)
- return(0);
-
- Start_I2C();
- SendByte(0xD1);
- if(ack==0)
- return(0);
- read_data = RcvByte();
- Ack_I2C(1);
- Stop_I2C();
- return read_data;
- }
-
- /**************************设置时间******************************************/
- void Set_Time()
- {
- uchar i;
- uchar ucAddr=0x00; //秒的地址
- for(i=0;i<7;i++)
- {
- write_byte(ucAddr,tabtime[i]);//从秒开始连续写入秒分时星期日月年
- ucAddr+=1;
- }
- }
-
- /*********************读取时间**********************/
- void get_time(void)
- {
- hour=read_byte(0x02)/16*10+read_byte(0x02)%16;
- minute=read_byte(0x01)/16*10+read_byte(0x01)%16; //因只显示时、分,故可只读取时、分
- year=read_byte(0x06)/16*10+read_byte(0x06)%16;
-
- /*
- uchar i;
- uchar ucCurtime[7];
- uchar ucAddr = 0x00; //秒地址
- for(i=0;i<7;i++)
- {ucCurtime[i]=0;}
-
- for(i=0; i<7; i++)
- {
- ucCurtime[i] = read_byte(ucAddr); //格式为: 从秒地址开始连续读取,秒分时星期日月年。
- ucAddr += 1;
- }
- year=ucCurtime[6]/16*10+ucCurtime[6]%16;
- month=ucCurtime[5]/16*10+ucCurtime[5]%16;
- day=ucCurtime[4]/16*10+ucCurtime[4]%16;
- week=ucCurtime[3]/16*10+ucCurtime[3]%16;
- hour=ucCurtime[2]/16*10+ucCurtime[2]%16;
- minute=ucCurtime[1]/16*10+ucCurtime[1]%16;
- second=ucCurtime[0]/16*10+ucCurtime[0]%16;
- */
- }
- /*********************读取温度**********************/
- void get_Temperature()
- {
- TtempH=read_byte(0X11); //读温度高字节
- //因数码管位数只有四位,故温度的小数部分不显示,可以不用读温度低字节
- //TtempL=read_byte(0x12); //读温度低字节
- //TtempL=(TtempL>>6)*25;//将BIT7,BIT6的数据移入BIT1,BIT0位;分辨率数值扩大100倍便于整数运算
-
- }
- ………………
复制代码
完整程序代码
|