📄 ds1303_.c
字号:
/*这个程序,是我用51电子网,298元C51学习板调试通的单片机控制DS1303时钟芯片
一个时钟程序,它能实现断电走时(用纽扣电池),显示和设置年月日星期时分秒(8位数码管)。
*/
#include<reg51.h>
#define uchar unsigned char//宏定义字符型
#define uint unsigned int//宏定义长整型
sbit CE=P3^5;//
sbit IO=P3^4;//
sbit SCLK=P3^3;//
int yr,mn,dy,date,hr,min,sec,gnj,wz;//分别代表年月日星期时分秒功能键,wz是判断键盘输入的
uchar code table[]={//定义字串数组数码管显示用分别是 0123456789_和全灭,这个数码管是共阳极的
0x12,0xfa,0x1c,0x98,
0xf0,0x91,0x11,0xda,
0x10,0x90,0xbf,0xff};
uchar code table2[]={//字串数组 数码管段位用,通74HC164N芯片控制8个数码管,的段显示
0x80,0x40,0x20,0x10,
0x08,0x04,0x02,0x01};
/***************************************************************************/
void delay()//延时函数,很短的延时
{ ;; }
void delay1(uint x)//延时函数
{
uint a,b;
for(a=x;a>0;a--)for(b=110;b>0;b--);
}
void xianshi(uchar s,uchar f,uchar m,uchar t)//显示函数,显示0-8个数码管,分别是四组两位数。
{
uint ssw,sgw,fsw,fgw,msw,mgw,tsw,tgw;//分别代表四组数的十位和个位
if(s==0xff)//当变量=0xff时数码管显示 _ 表示光标。
{
ssw=10;
sgw=10;
}
else
{
ssw=s/10;
sgw=s%10;
}
if(f==0xff)
{
fsw=10;
fgw=10;
}
else
{
fsw=f/10;
fgw=f%10;
}
if(m==0xff)
{
msw=10;
mgw=10;
}
else
{
msw=m/10;
mgw=m%10;
}
if(t==0xff)
{
tsw=10;
tgw=10;
}
else
{
tsw=t/10;
tgw=t%10;
}
if(t==0xfd)//最后一组数码管不显示。
{
tsw=11;
tgw=11;
}
REN=1;//打开74H164N芯片写入口
SBUF=table2[0];//发送数据,数码管第一位亮
delay();
P1=table[ssw];//显示第一组数的十位
delay1(3);
P1=0xff; //消除数码管光影。
SBUF=table2[1];
delay();
P1=table[sgw];//第一组数的个位
delay1(3);
P1=0xff;
SBUF=table2[2];
delay();
P1=table[fsw];
delay1(3);
P1=0xff;
SBUF=table2[3];
delay();
P1=table[fgw];
delay1(3);
P1=0xff;
SBUF=table2[4];
delay();
P1=table[msw];
delay1(3);
P1=0xff;
SBUF=table2[5];
delay();
P1=table[mgw];
delay1(3);
P1=0xff;
SBUF=table2[6];//
delay();
P1=table[tsw];//
delay1(3);
P1=0xff;
SBUF=table2[7];
delay();
P1=table[tgw];//
delay1(3);
P1=0xff;
REN=0;
}
void reset_3w() /* ----- 时钟芯片通信口初始化 ------ */
{
SCLK = 0;
CE = 0;
CE = 1;
}
void wbyte_3w(uchar W_Byte) /* ------ 写一个字节的数据到时钟芯片 ------- */
{
uchar i;
for(i = 0; i < 8; ++i)//几次循环8次,发送8位数据
{
IO = 0;
if(W_Byte & 0x01)
{
IO = 1; /* 当IO口位高电平时发送数据 */
}
SCLK = 0;
SCLK = 1;
W_Byte >>= 1;//数据右移
}
}
uchar rbyte_3w() /* ------- 读芯片一个字节的数据 -------- */
{
uchar i,j;
uchar R_Byte;//用作存储读出的数据
uchar TmpByte;//临时变量
R_Byte = 0x00;
IO = 1;//初始化数据口
for(i = 0; i < 8; i++)
{
SCLK = 1;
SCLK = 0;
TmpByte = (uchar)IO;//读出数据
TmpByte <<= 7;//移到高位
R_Byte >>= 1;
R_Byte |= TmpByte;
}
j=R_Byte>>4;//以下是将BCD码转换位正常十六进制数。(BCD码0x16,的意思是十进制的16转换后0x0f)
j*=10;
R_Byte=R_Byte&0x0f;
R_Byte+=j;
return R_Byte;
}
uchar ask_bcd(uchar ask)//把正常十六进制数转换成BCD码
{
uchar g4,d4,bcd;
g4=ask/10;//取出十位数例如0x0f,十进制为16
d4=ask%10;//取出个位数 g4=0x01,d4=0x06
bcd=g4<<4;//把十位数移到高位g4=0x10,
bcd=bcd+d4;//得出BCD码 bcd=0x16
return bcd;
}
void writebyte(uchar ClkAdd,uchar ClkData) /* --- 在指定位置写入一个字节数据 --- */
{
reset_3w();
wbyte_3w(ClkAdd);//地址
wbyte_3w(ClkData);//数据
reset_3w();
}
uchar dusj(uchar add)//在指定位置读出数据用来读时钟芯片内部存储器的数据add是地址数据
{
uchar j;
reset_3w();
wbyte_3w(add);
j= rbyte_3w();
reset_3w();
return j;
}
void read_clk_regs() /* ---- 读出年周月日时分秒---- */
{
reset_3w();//初始化数据口
wbyte_3w(0xBF); /* 写入读命令 */
sec = rbyte_3w();
min = rbyte_3w();
hr = rbyte_3w();
dy = rbyte_3w();
mn= rbyte_3w();
date = rbyte_3w();
yr = rbyte_3w();
reset_3w();
}
void write_clk_regs(uchar yr2,date2, mn2, dy2,hr2,min2,sec2)
/*一次性写入年周月日时分秒 */
{
reset_3w();
wbyte_3w(0x8e); /* 置写入命令 */
wbyte_3w(0); /* 打开写保护 */
reset_3w();
wbyte_3w(0x90); /* 容许时钟芯片给纽扣电池充电 */
wbyte_3w(0xab); /* 容许读写时钟芯片外部寄存器 */
reset_3w();
wbyte_3w(0xbe); /* 命令时钟芯片一次接收8组数据分别是秒分时日月周年*/
wbyte_3w(sec2);
wbyte_3w(min2);
wbyte_3w(hr2);
wbyte_3w(dy2);
wbyte_3w(mn2);
wbyte_3w(date2);
wbyte_3w(yr2);
wbyte_3w(0); /* 最后一位结束位发0*/
reset_3w();
}
uchar key()//键盘扫描程序
{
uchar fhz;//返回值
wz=0;//设置键盘控制变量初始化
fhz=0;
P0=0xfe;//P0 口的矩阵键盘(这里只用了4个键盘k4,k8,k12,k16)
/*k4为加号键。k8是减号键。K12是功能键(gnj变量是1,正常显示时分秒,gnj是2,设置年
gnj是3,设置月。gnj是4,设置日,。gnj是5,设置 星期,。gnj是6,设置 时,。gnj是7,设置
分。gnj是8,设置秒*/
while(P0==0xee)//k16按下显示年月日星期
{
if(sec==1)read_clk_regs();
xianshi(yr,mn,dy,date);
}
if(P0==0xde)//k12按下设置功能
{
delay1(5);//等待5毫秒
if(P0==0xde)//判断真的按键了
{
gnj++;
if(gnj==9)gnj=1;
}
while(P0==0xde);//等待松手
fhz=0;
}
if(P0==0x7e)//k4,返回值=2为减号键
{
delay1(15);
if(P0==0x7e)
{
while(P0==0x7e);
fhz=2;
}
}
if(P0==0xbe)//k8,返回值=1为加号键
{
delay1(15);
if(P0==0xbe)
{
while(P0==0xbe);
fhz=1;
}
}
return fhz;
}
int suanxingqi(int y,int m,int d)//计算星期函数变量为年月日,例如2009,1,18
{
int w;
if(m<2)m=m+12;
w= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7;
if(w==0)w=7;
return w;
}
uchar runnian(uchar nian)//计算闰年函数判断变量nian是否是闰年。如2009
{
if((nian%400==0) || (nian%100!=0) && (nian%4==0))
{
return 1;//是闰年
}
else
{
return 0;//不是闰年
}
}
void main()
{
uint san;//这个变量是用来起到光标闪烁效果的,san=0到10时显示正常,san=10-20显示光标
uint yr1,mn1,dy1,date1,hr1,min1,sec1;//年月日时分秒星期临时变量,转换BCD码用的。
gnj=1;//初始化功能键变量
while(1)//进入大循环
{
while(gnj==1)//正常显示(时分秒)
{
read_clk_regs();
xianshi(hr,min,sec,0xfd);
delay1(1);
wz=key();
}
while(gnj==2)//功能键=2时设置年
{
if(san<20)//闪烁显示秒
{
xianshi(yr,mn,dy,date);
}
if(san>30)
{
xianshi(0xff,mn,dy,date);//让显示函数,在年的位置显示光标
}
san++;
if(san>40)
{
san=0;
}
wz=key();
if(wz==1)//按了加号键
{
yr++;//年加一(年的数值范围00-99)
if(yr==100)
{
yr=0;
}
}
if(wz==2)//按了减号键
{
if(yr==0)
{
yr=100;
}
yr--;
}
}
while(gnj==3)//功能键=3时设置几月
{
if(san<20)//闪烁显示秒
{
xianshi(yr,mn,dy,date);
}
if(san>30)
{
xianshi(yr,0xff,dy,date);//让显示函数,在月的位置显示点
}
san++;
if(san>40)
{
san=0;
}
wz=key();
if(wz==1)
{
mn++;
if(mn>12)
mn=1;
}
if(wz==2)
{
mn--;
if(mn<1)
mn=12;
}
}
while(gnj==4)//功能键=4时设置日期
{
if(san<20)//闪烁显示秒
{
xianshi(yr,mn,dy,date);
}
if(san>30)
{
xianshi(yr,mn,0xff,date);//让显示函数,在日期的位置显示光标
}
san++;
if(san>40)
{
san=0;
}
wz=key();
if(wz==1)
{
dy++;
}
if(wz==2)
{
if(dy<=1)
{dy=32;}
dy--;
}
yr1=2000+yr;
if((dy>28)&&(runnian(yr1)==0)&&(mn==2))//判断不是闰年且是2月份只有28天
{
if(wz==1)
{
dy=1;
}
else
{
dy=28;
}
}
if((dy>29)&&(mn==2))//判断是闰年且是2月份,最多29天
{
if(wz==1)
{
dy=1;
}
else
{
dy=29;
}
}
if(dy>30)
{
if((mn==4)||(mn==6)||(mn==9)||(mn==11))//判断是小月的话只有30天
{
if(wz==1)
{
dy=1;
}
else
{
dy=30;
}
}
}
if(dy>31)//剩下最后的大月才有31天
{
if(wz==1)//判断按的是加号键31天归1
{
dy=1;
}
else//否则按的是减号键1回到31
{
dy=31;
}
}
}
while(gnj==5)//功能键=5时设置星期
{
date1=2000+yr;
date=suanxingqi(date1,mn,dy);//自动算出星期
if(san<20)//闪烁显示秒
{
xianshi(yr,mn,dy,date);
}
if(san>30)
{
xianshi(yr,mn,dy,0xff);//让显示函数,在星期的位置显示点
}
san++;
if(san>40)
{
san=0;
}
wz=key();//不能调节
}
while(gnj==6)//功能键=6时设置小时
{
if(san<20)//闪烁显示秒
{
xianshi(hr,min,sec,0xfd);
}
if(san>30)
{
xianshi(0xff,min,sec,0xfd);//让显示函数,在秒的位置显示光标,0xfd让数码管无显示
}
san++;
if(san>40)
{
san=0;
}
wz=key();
if(wz==1)
{
hr++;
if(hr>23)
{hr=0;}
}
if(wz==2)
{
if(hr==0)
{hr=24;}
hr--;
}
}
while(gnj==7)//功能键=7时设置分钟
{
if(san<20)//闪烁显示秒
{
xianshi(hr,min,sec,0xfd);
}
if(san>30)
{
xianshi(hr,0xff,sec,0xfd);//让显示函数,在分的位置显示点
}
san++;
if(san>40)
{
san=0;
}
wz=key();
if(wz==1)
{
min++;
if(min>59)
{min=0;}
}
if(wz==2)
{
if(min==0)
{min=60;}
min--;
}
}
while(gnj==8)//功能键=8时设置秒钟
{
if(san<20)//闪烁显示秒
{
xianshi(hr,min,sec,0xfd);
}
if(san>30)
{
xianshi(hr,min,0xff,0xfd);//让显示函数,在秒的位置显示光标
}
san++;
if(san>40)
{
san=0;
}
wz=key();
if(gnj==1)//完成设置存入DS1303芯片
{
date1=2000+yr;
date=suanxingqi(date1,mn,dy);//根据年月日算出星期
yr1=ask_bcd(yr);//转换成BCD码
mn1=ask_bcd(mn);
dy1=ask_bcd(dy);
date1=ask_bcd(date);
hr1=ask_bcd(hr);
min1=ask_bcd(min);
sec1=ask_bcd(sec);
write_clk_regs(yr1,date1,mn1,dy1,hr1,min1,sec1);//写入数据
delay1(20);
}
if(wz==1)
{
sec++;
if(sec>59)
{sec=0;}
}
if(wz==2)
{
if(sec==0)
{sec=60;}
sec--;
}
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -