DS1302实时时钟
1、基本知识讲解
2、用DS1302做时钟
main.c
#include <regx52.h>
#include<delay.h>
#include<DS1302.h>
#include<LCD1602.h>
void main()
{
LCD_Init();
ds1302_init();
LCD_ShowString(1,1," - -");
LCD_ShowString(2,1," : : : ");
//如果读出时间为一个大于59并且不动的数则芯片有可能是处于写保护状态,在此处加上DS1302 WriteByte(0x8E,0x00)即可解除芯片写保护
//ds1302_writebyte(0x8e,0x00);
ds1302_set_time();
while(1)
{
ds1302_read_time();
LCD_ShowNum(1,1,ds1302_time[0],2); //在DS1302中数据存储是以BCD码形式存储的,0001 0000在BCD码中是9 ,如果以十进制显示就是16。
//BCD码用4位二进制数来表示1位十进制数 ,从0~9,而十六进制从0~F,0001 1100 十六进制是1C,在BCD码中不合法。
LCD_ShowNum(1,4,ds1302_time[1],2);
LCD_ShowNum(1,7,ds1302_time[2],2);
LCD_ShowNum(2,1,ds1302_time[3],2);
LCD_ShowNum(2,4,ds1302_time[4],2);
LCD_ShowNum(2,7,ds1302_time[5],2);
LCD_ShowNum(2,10,ds1302_time[6],2);
}
}
ds1302.c
#include<REGX52.h>
sbit DS1302_SCLK =P3^6;
sbit DS1302_IO =P3^4;
sbit DS1302_CE=P3^5;
#define ds1302_write_second 0x80 //注意define后面不用加分号
#define ds1302_write_minute 0x82 //对位地址重新定义,这样方便调用,直接调用0x80如果不看手册不知道对应的是什么
#define ds1302_write_hour 0x84
#define ds1302_write_date 0x86
#define ds1302_write_month 0x888
#define ds1302_write_day 0x8a
#define ds1302_write_year 0x8c
#define ds1302_write_wp 0x8e
unsigned char ds1302_time[]={19,11,16,12,59,55,6}; //年 月 日 小时 分钟 秒 星期 定义初值
void ds1302_init()
{
DS1302_SCLK =0;
DS1302_CE=0;//上电默认高电平,初始化给0
}
void ds1302_writebyte(unsigned char command,Data)
{
unsigned char i;
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=command&(0x01<<i);
DS1302_SCLK =1;
DS1302_SCLK =0;
}
for(i=0;i<8;i++)
{
DS1302_IO=(Data&(0x01<<i));
DS1302_SCLK =1;
DS1302_SCLK =0;
}
DS1302_CE=0;
}
unsigned char ds1302_readbyte(unsigned char command)
{
unsigned char i,Data=0x00; //注意这里的data存放的是一个字节,有8位数据
DS1302_CE=1;
command|=0x01; //这一步必加,不然全部显示65.因为读命令最低位是1,所以这一步的作用就是先把最低位置1
for(i=0;i<8;i++)
{
DS1302_IO=(command&(0x01<<i)); //io口从最低位开始读取读命令地址,直到8位读取完毕
DS1302_SCLK =0;
DS1302_SCLK =1;
}
for(i=0;i<8;i++)
{
DS1302_SCLK =1;
DS1302_SCLK =0; //假设写入的0011 0101
if(DS1302_IO){Data=(Data|0x01<<i);}// 当DS1302_IO=1时,才执行,则data=0000 0000|0000 0001=0000 0001
//第二位是0,不执行,此时i=1;第三位是1,执行if里的语句,i=2,则data=0000 0001|0000 0100=0000 0101
//第四位是0,不执行,此时i=3,第五位是1,执行,i=4,则data=0000 0101|0001 0000=0001 0101,以此类推
}
DS1302_CE=0;
DS1302_IO=0;//读取后将IO设置为0,否则读出的数据会出错
return Data;
}
void ds1302_set_time()
{
ds1302_writebyte(ds1302_write_wp,0x00);//解除写保护
ds1302_writebyte(ds1302_write_year,ds1302_time[0]/10*16+ds1302_time[0]%10); //十进制转BCD码写入
ds1302_writebyte(ds1302_write_month,ds1302_time[1]/10*16+ds1302_time[1]%10);
ds1302_writebyte(ds1302_write_date,ds1302_time[2]/10*16+ds1302_time[2]%10);
ds1302_writebyte(ds1302_write_hour,ds1302_time[3]/10*16+ds1302_time[3]%10);
ds1302_writebyte(ds1302_write_minute,ds1302_time[4]/10*16+ds1302_time[4]%10);
ds1302_writebyte(ds1302_write_second,ds1302_time[5]/10*16+ds1302_time[5]%10);
ds1302_writebyte(ds1302_write_day,ds1302_time[6]/10*16+ds1302_time[6]%10);
ds1302_writebyte(ds1302_write_wp,0x80);//恢复写保护
}
void ds1302_read_time()
{
unsigned char temp;//定义一个变量temp,用来存放年月日时分秒星期的地址
temp= ds1302_readbyte(ds1302_write_year); //调用年地址,再读年的数据
ds1302_time[0]=temp/16*10+temp%16;//BCD码转十进制
temp= ds1302_readbyte(ds1302_write_month);
ds1302_time[1]=temp/16*10+temp%16;
temp=ds1302_readbyte(ds1302_write_date);
ds1302_time[2]=temp/16*10+temp%16;
temp=ds1302_readbyte(ds1302_write_hour);
ds1302_time[3]=temp/16*10+temp%16;
temp=ds1302_readbyte(ds1302_write_minute);
ds1302_time[4]=temp/16*10+temp%16;
temp=ds1302_readbyte(ds1302_write_second);
ds1302_time[5]=temp/16*10+temp%16;
temp=ds1302_readbyte(ds1302_write_day);
ds1302_time[6]=temp/16*10+temp%16;
}
效果:
3、用DS1302做可调时钟
#include <regx52.h>
#include<delay.h>
#include<DS1302.h>
#include<LCD1602.h>
#include<Key.h>
#include<timer0.h>
unsigned char keynum,mode,timesetselect,timesetflashflag;
void timeshow()
{
ds1302_read_time();
LCD_ShowNum(1,1,ds1302_time[0],2); //在DS1302中数据存储是以BCD码形式存储的,0001 0000在BCD码中是9 ,如果以十进制显示就是16。
//BCD码用4位二进制数来表示1位十进制数 ,从0~9,而十六进制从0~F,0001 1100 十六进制是1C,在BCD码中不合法。
LCD_ShowNum(1,4,ds1302_time[1],2);
LCD_ShowNum(1,7,ds1302_time[2],2);
LCD_ShowNum(2,1,ds1302_time[3],2);
LCD_ShowNum(2,4,ds1302_time[4],2);
LCD_ShowNum(2,7,ds1302_time[5],2);
}
void timeset()
{
if (keynum==2)
{
timesetselect++; //按k2实现对年月日 时分秒的切换
timesetselect%=6; //用取余来实现循环复位,
}
if(keynum==3)//k3按下开始加
{
ds1302_time[timesetselect]++;
if(ds1302_time[0]>99){ds1302_time[0]=0;}//越上界年复位
if(ds1302_time[1]>12){ds1302_time[1]=1;}//越上界月复位
if(ds1302_time[1]==1||ds1302_time[1]==3||ds1302_time[1]==5||ds1302_time[1]==7||ds1302_time[1]==8||ds1302_time[1]==10||ds1302_time[1]==12)
{
if(ds1302_time[2]>31)
ds1302_time[2]=1; //越上界日复位 ,同时区分31日和30天
}
else if (ds1302_time[1]==4||ds1302_time[1]==6||ds1302_time[1]==9||ds1302_time[1]==11)
{
if(ds1302_time[2]>30)
ds1302_time[2]=1;
}
else if(ds1302_time[1]==2)
{
if(ds1302_time[0]%4==0) //通过平年和闰年来判断2月是28天还是29天
{
if(ds1302_time[2]>29)
ds1302_time[2]=1;
}
else
{
if(ds1302_time[2]>28)
ds1302_time[2]=1;
}
}
if(ds1302_time[3]>23)ds1302_time[3]=0;
if(ds1302_time[4]>59)ds1302_time[4]=0;
if(ds1302_time[5]>59)ds1302_time[5]=0; //时分秒 越上界复位
}
if(keynum==4)//k4按下开始减,同理越下界复位
{
ds1302_time[timesetselect]--;
if(ds1302_time[0]<0)ds1302_time[0]=99;//注意这里是有符号函数才能写<0;因为无符号函数从0~255,<0会回到255报错
if(ds1302_time[1]<1)ds1302_time[1]=12;
if(ds1302_time[1]==1||ds1302_time[1]==3||ds1302_time[1]==5||ds1302_time[1]==7||ds1302_time[1]==8||ds1302_time[1]==10||ds1302_time[1]==12)
{
if(ds1302_time[2]<1)
ds1302_time[2]=31;
if(ds1302_time[2]>31)
ds1302_time[2]=1;
}
else if (ds1302_time[1]==4||ds1302_time[1]==6||ds1302_time[1]==9||ds1302_time[1]==11)
{
if(ds1302_time[2]<1)
ds1302_time[2]=30;
if(ds1302_time[2]>30) //这一步是为了解决bug,因为当12月有31天,此时我对月份进行减,到11月份,则会变成11:31日,这是错的,因为11月没有31号
ds1302_time[2]=1;//所以增加一个上位越界的判断
}
else if(ds1302_time[1]==2)
{
if(ds1302_time[0]%4==0)
{
if(ds1302_time[2]<1)
ds1302_time[2]=29;
if(ds1302_time[2]>29)
ds1302_time[2]=1;
}
else
{
if(ds1302_time[2]<1)
ds1302_time[2]=28;
if(ds1302_time[2]>28)
ds1302_time[2]=1;
}
}
if(ds1302_time[3]<0)ds1302_time[3]=23;
if(ds1302_time[4]<0)ds1302_time[4]=59;
if(ds1302_time[5]<0)ds1302_time[5]=59;
}
if(timesetselect==0&×etflashflag==1)
{LCD_ShowString(1,1," ");}
else{LCD_ShowNum(1,1,ds1302_time[0],2); }//在DS1302中数据存储是以BCD码形式存储的,0001 0000在BCD码中是9 ,如果以十进制显示就是16。
//BCD码用4位二进制数来表示1位十进制数 ,从0~9,而十六进制从0~F,0001 1100 十六进制是1C,在BCD码中不合法。
if(timesetselect==1&×etflashflag==1)
{LCD_ShowString(1,4," ");}
else{LCD_ShowNum(1,4,ds1302_time[1],2); }
if(timesetselect==2&×etflashflag==1)
{LCD_ShowString(1,7," ");}
else{LCD_ShowNum(1,7,ds1302_time[2],2); }
if(timesetselect==3&×etflashflag==1)
{LCD_ShowString(2,1," ");}
else{LCD_ShowNum(2,1,ds1302_time[3],2); }
if(timesetselect==4&×etflashflag==1)
{LCD_ShowString(2,4," ");}
else{LCD_ShowNum(2,4,ds1302_time[4],2); }
if(timesetselect==5&×etflashflag==1)
{LCD_ShowString(2,7," ");}
else{ LCD_ShowNum(2,7,ds1302_time[5],2); }
}
void main()
{
LCD_Init();
ds1302_init();
Timer0_Init();
LCD_ShowString(1,1," - -");
LCD_ShowString(2,1," : : ");
ds1302_set_time(); //这个是数据原本写入的初始值,如果没有这不,LCD1602将不显示数值
//如果读出时间为一个大于59并且不动的数则芯片有可能是处于写保护状态,在此处加上DS1302 WriteByte(0x8E,0x00)即可解除芯片写保护
//ds1302_writebyte(0x8e,0x00);
while(1)
{
keynum=key();
if(keynum==1)
{
if (mode==0) {mode=1;timesetflashflag=0; } //实现按下按键实现来回切换功能
else if (mode==1){mode=0; ds1302_set_time();} //当timeset()切换回yimeshow()之前,对ds1302_set_time()进行写入,这样数据才能得到保存
}
switch (mode)
{
case 0: timeshow();break;
case 1: timeset();break;
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int count; //
TL0 = 0x18;
TH0 = 0xFC;
count++;
if(count>=500) //定时1s的代码 ,可直接复制
{
count=0;
timesetflashflag=!timesetflashflag;
}
}
注释:按键1实现LCD1602屏幕显示和设置(年月日时分秒)模式
按键2实现年月日时分秒之间切换,按一下,对年设置,再按对月进行设置
按键3实现对年月日时分秒的加
按键4实现对年月日时分秒的减
这里了解的不够透彻,后续要回顾
蜂鸣器
1、基本知识讲解
2、按下按键蜂鸣器响
main.c
#include <regx52.h>
#include<delay.h>
#include "nixie.h"
#include<LCD1602.h>
#include<Key.h>
#include"buzzer.h"
unsigned char keynum;
void main ()
{
nixie(1,0);
while(1)
{
keynum =key();
if(keynum)
{
buzzer_time(100);
nixie(1,keynum); //数码管显示第二个位置,显示数值是keynum
}
}
}
buzzer.c
#include <REGX52.H>
#include<intrins.h>
sbit buzzer=P2^5;
void buzzer_Delay500us() //@11.0592MHz
{
unsigned char data i;
_nop_();
i = 227;
while (--i);
}
unsigned buzzer_time(unsigned int ms)
{
unsigned int i=0;
for(i=0;i<ms*2;i++)//乘以2是因为一个for循环是0.5ms,而我们定义的是1ms
{
buzzer=!buzzer; //蜂鸣器要低电平才响,P2^5上电默认是高电平
buzzer_Delay500us(); //每500us翻转一次,周期为1000us,频率为1000HZ
}
}
3、蜂鸣器播放音乐--天空之城
机器周期 = 12 / 晶振频率
#include <regx52.h>
#include"delay.h"
#include "timer0.h"
sbit buzzer=P2^5; //定义蜂鸣器端口
#define P 0 //定义音符的索引号
#define L1 1
#define L1_ 2
#define L2 3
#define L2_ 4
#define L3 5
#define L4 6
#define L4_ 7
#define L5 8
#define L5_ 9
#define L6 10
#define L6_ 11
#define L7 12
#define M1 13
#define M1_ 14
#define M2 15
#define M2_ 16
#define M3 17
#define M4 18
#define M4_ 19
#define M5 20
#define M5_ 21
#define M6 22
#define M6_ 23
#define M7 24
#define H1 25
#define H1_ 26
#define H2 27
#define H2_ 28
#define H3 29
#define H4 30
#define H4_ 31
#define H5 32
#define H5_ 33
#define H6 34
#define H6_ 35
#define H7 36
#define speed 500 //定义一个4分音符的时间500ms
//c调音符频率对应定时器重装值
unsigned int code freqtable[]= {
0,63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283
};
//天空之城乐谱
unsigned char code music[]= {
//音符,时值
//1
P,4,
P,4,
P,4,
M6,2,
M7,2,
H1,6,
M7,2,
H1,4,
H3,4,
M7,12,
M3,2,
M3,2,
//2
M6,6,
M5,2,
M6,4,
H1,4,
M5,12,
M3,4,
M4,6,
M3,2,
M4,4,
H1,4,
//3
M3,8,
P,2,
H1,2,
H1,2,
H1,2,
M7,6,
M4_,2,
M4,2,
M7,4,
M7,8,
P,4,
M6,2,
M7,2,
//4
H1,6,
M7,2,
H1,4,
M3,4,
M7,12,
M3,2,
M3,2,
M6,6,
M5,2,
M6,4,
H1,4,
//5
M5,12,
M2,2,
M3,2,
M4,4,
H1,2,
M7,4,
H1,6,
H2,2,
H2,2,
H3,2,
H1,2+4+4,
//6
H1,2,
M7,2,
M6,2,
M6,2,
M7,4,
M5_,4,
M6,12,
H1,2,
H2,2,
H3,6,
H2,2,
H3,4,
H5,4,
//7
H2,12,
M5,2,
M5,2,
H1,6,
M7,2,
H1,4,
H3,4,
H3,16,
//8
M6,2,
M7,2,
H1,4,
M7,4,
H2,2,
H2,2,
H1,6,
M5,2+4+4,
H4,4,
H3,4,
H2,4,
H1,4,
//9
H3,12,
H3,4,
H6,8,
H5,4,
H5,4,
H3,2,
H2,2,
H1,8,
P,2,
H1,2,
//10
H2,4,
H1,2,
H2,2,
H2,4,
H5,4,
H3,12,
H3,4,
H6,8,
H5,8,
//11
H3,2,
H2,2,
H1,8,
P,2,
H1,2,
H2,4,
H1,2,
H2,2+4,
M7,4,
M6,12,
M6,2,
M7,2,
0xff //停止位
};
unsigned char select,musicselect;
void main ()
{
Timer0_Init();
while(1)
{
if(music[musicselect]!=0xff) //当播放完了,设置一个停止位
{
select=music[musicselect];
musicselect++;
Delay(speed/4*music[musicselect]);
musicselect++;
TR0 = 0;//定时器关闭,这样两个相同音符才不会连音
Delay(5);
TR0 = 1;
}
else
{
TR0=0;
while(1);
}
}
}
void Timer0_Routine() interrupt 1
{
if(freqtable[musicselect]) //如果不是休止符
{
TL0 = freqtable[select]%256;//定时不同时间溢出
TH0 = freqtable[select]/256;
buzzer=!buzzer; //翻转蜂鸣器IO口
}
}
ps:播放一开始会有杂音,乐谱一开始有3个休止符,但程序是休止符不进行中断,所以这个时候就乱了。我的单片机晶振是11.0592mhz,但程序实际是以12mhz晶振举例的。但是我把老师的代码搬过来,前面那三个休止符又没乱,所以应该还是代码有点问题。目前这部分对晶振周期、时钟周期,及音谱的频率怎么计算,或者换成D调音高对应频率是多少,以及晶振频率是11.0592mhz又改如何修改还有疑问,后续再学习。
标签:第四课,H1,主江协,ds1302,---,M7,time,include,define From: https://blog.csdn.net/m0_51664996/article/details/142234084