#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
uchar sec,min,hour,day,month,year,week;
bit w;
uchar next;
void DelayUs2x(unsigned char t)
{
while(--t);
}
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
void delay(uint ms) // 延时子程序
{
uint a,b,c; //ms=1000为1.015s
for(a=2;a>0;a--)
for(b=46;b>0;b--)
for(c=ms;c>0;c--);
}
sbit RS = P2^4;
sbit RW = P2^5;
sbit E = P2^6;
sbit RES = P2^1;
sbit PSB = P2^0;
sbit sclk=P1^0; //1302串行时钟
sbit I_O=P1^1; //1302数据端口
sbit rst=P1^2; //1302复位
sbit k1=P3^4; //功能健
sbit k2=P3^5; //时间加
sbit k3=P3^6; //时间减
sbit k4=P3^7; //确定键
#define DataPort P0 //单片机 P0<------> 液晶DB0-DB7
uchar code DayCode1[9]={0x00,0x1f,0x3b,0x5a,0x78,0x97,0xb5,0xd4,0xf3};
uint code DayCode2[3]={0x111,0x130,0x14e};
uchar LunarMonth,LunarDay,LunarYear;
bit c_moon;
uchar code YearCode[300]=
{
0x0C,0x96,0x45, //2000 297
0x4d,0x4A,0xB8, //2001 300
0x0d,0x4A,0x4C, //2002 303
0x0d,0xA5,0x41, //2003 306
0x25,0xAA,0xB6, //2004 309
0x05,0x6A,0x49, //2005 312
0x7A,0xAd,0xBd, //2006 315
0x02,0x5d,0x52, //2007 318
0x09,0x2d,0x47, //2008 321
0x5C,0x95,0xBA, //2009 324
0x0A,0x95,0x4e, //2010 327
0x0B,0x4A,0x43, //2011
0x4B,0x55,0x37, //2012
0x0A,0xd5,0x4A, //2013
0x95,0x5A,0xBf, //2014
0x04,0xBA,0x53, //2015
0x0A,0x5B,0x48, //2016
0x65,0x2B,0xBC, //2017
0x05,0x2B,0x50, //2018
0x0A,0x93,0x45, //2019
0x47,0x4A,0xB9, //2020
0x06,0xAA,0x4C, //2021
0x0A,0xd5,0x41, //2022
0x24,0xdA,0xB6, //2023
0x04,0xB6,0x4A, //2024
0x69,0x57,0x3d, //2025
0x0A,0x4e,0x51, //2026
0x0d,0x26,0x46, //2027
0x5e,0x93,0x3A, //2028
0x0d,0x53,0x4d, //2029
0x05,0xAA,0x43, //2030
0x36,0xB5,0x37, //2031
0x09,0x6d,0x4B, //2032
0xB4,0xAe,0xBf, //2033
0x04,0xAd,0x53, //2034
0x0A,0x4d,0x48, //2035
0x6d,0x25,0xBC, //2036
0x0d,0x25,0x4f, //2037
0x0d,0x52,0x44, //2038
0x5d,0xAA,0x38, //2039
0x0B,0x5A,0x4C, //2040
0x05,0x6d,0x41, //2041
0x24,0xAd,0xB6, //2042
0x04,0x9B,0x4A, //2043
0x7A,0x4B,0xBe, //2044
0x0A,0x4B,0x51, //2045
0x0A,0xA5,0x46, //2046
0x5B,0x52,0xBA, //2047
0x06,0xd2,0x4e, //2048
0x0A,0xdA,0x42, //2049
0x35,0x5B,0x37, //2050
0x09,0x37,0x4B, //2051
0x84,0x97,0xC1, //2052
0x04,0x97,0x53, //2053
0x06,0x4B,0x48, //2054
0x66,0xA5,0x3C, //2055
0x0e,0xA5,0x4f, //2056
0x06,0xB2,0x44, //2057
0x4A,0xB6,0x38, //2058
0x0A,0xAe,0x4C, //2059
0x09,0x2e,0x42, //2060
0x3C,0x97,0x35, //2061
0x0C,0x96,0x49, //2062
0x7d,0x4A,0xBd, //2063
0x0d,0x4A,0x51, //2064
0x0d,0xA5,0x45, //2065
0x55,0xAA,0xBA, //2066
0x05,0x6A,0x4e, //2067
0x0A,0x6d,0x43, //2068
0x45,0x2e,0xB7, //2069
0x05,0x2d,0x4B, //2070
0x8A,0x95,0xBf, //2071
0x0A,0x95,0x53, //2072
0x0B,0x4A,0x47, //2073
0x6B,0x55,0x3B, //2074
0x0A,0xd5,0x4f, //2075
0x05,0x5A,0x45, //2076
0x4A,0x5d,0x38, //2077
0x0A,0x5B,0x4C, //2078
0x05,0x2B,0x42, //2079
0x3A,0x93,0xB6, //2080
0x06,0x93,0x49, //2081
0x77,0x29,0xBd, //2082
0x06,0xAA,0x51, //2083
0x0A,0xd5,0x46, //2084
0x54,0xdA,0xBA, //2085
0x04,0xB6,0x4e, //2086
0x0A,0x57,0x43, //2087
0x45,0x27,0x38, //2088
0x0d,0x26,0x4A, //2089
0x8e,0x93,0x3e, //2090
0x0d,0x52,0x52, //2091
0x0d,0xAA,0x47, //2092
0x66,0xB5,0x3B, //2093
0x05,0x6d,0x4f, //2094
0x04,0xAe,0x45, //2095
0x4A,0x4e,0xB9, //2096
0x0A,0x4d,0x4C, //2097
0x0d,0x15,0x41, //2098
0x2d,0x92,0xB5, //2099
};
/***复位1302***/
void reset1302()
{
sclk=0;
rst=0;
rst=1;
}
/***向1302写入1字节***/
void wrieteByte1302(uchar add)
{
uchar i;
for(i=0;i<8;i++)
{
I_O=0;
if(add&0x01)I_O=1;
sclk=0;
sclk=1;
add>>=1;
}
}
void writeClkBye(uchar add,uchar num)
{
reset1302();
wrieteByte1302(add);
wrieteByte1302(num);
}
/***1302初始化***/
void init1302()
{
reset1302();
writeClkBye(0x8e,0); //允许写入
reset1302();
writeClkBye(0x90,0xaa); //慢充电
reset1302();
writeClkBye(0x80,00); //秒初值
reset1302();
writeClkBye(0x82,0x12); //分
reset1302();
writeClkBye(0x84,0x12); // 时
reset1302();
writeClkBye(0x86,0x04); // 日
reset1302();
writeClkBye(0x88,0x03); // 月
reset1302();
writeClkBye(0x8a,0x07); // 星期
reset1302();
writeClkBye(0x8c,0x12); // 年
reset1302();
}
/***检测忙位***/
void Check_Busy()
{
RS=0;
RW=1;
E=1;
DataPort=0xff;
while((DataPort&0x80)==0x80);//忙则等待
E=0;
}
/***写命令***/
void Write_Cmd(unsigned char Cmd)
{
Check_Busy();
RS=0;
RW=0;
E=1;
DataPort=Cmd;
DelayUs2x(5);
E=0;
DelayUs2x(5);
}
/****写数据***/
void Write_Data(unsigned char Data)
{
Check_Busy();
RS=1;
RW=0;
E=1;
DataPort=Data;
DelayUs2x(5);
E=0;
DelayUs2x(5);
}
/****液晶屏初始化***/
void Init_ST7920()
{
DelayMs(40); //大于40MS的延时程序
PSB=1; //设置为8BIT并口工作模式
DelayMs(1); //延时
RES=0; //复位
DelayMs(1); //延时
RES=1; //复位置高
DelayMs(10);
Write_Cmd(0x30); //选择基本指令集
DelayUs2x(50); //延时大于100us
Write_Cmd(0x30); //选择8bit数据流
DelayUs2x(20); //延时大于37us
Write_Cmd(0x0c); //开显示(无游标、不反白)
DelayUs2x(50); //延时大于100us
Write_Cmd(0x01); //清除显示,并且设定地址指针为00H
DelayMs(15); //延时大于10ms
Write_Cmd(0x06); //指定在资料的读取及写入时,设定游标的移动方向及指定显示的移位,光标从右向左加1位移动
DelayUs2x(50); //延时大于100us
}
/****从1302读取***/
uchar ReadByte1302()
{
uchar i;
uchar RByte;
uchar TempByte;
RByte=0x00;
I_O=1;
for(i=0;i<8;i++)
{
sclk=1;
sclk=0;
TempByte=(uchar)I_O;
TempByte<<=7;
RByte>>=1;
RByte|=TempByte;
}
return RByte;
}
uchar read1302(uchar add)
{
uchar num;
reset1302();
wrieteByte1302(add);
ReadByte1302();
num=ReadByte1302();
return num;
}
void LCDTestWord(bit i, uchar word)
{
if(i==0)
{
Write_Cmd(word); //i=0;则写入指令
}
else
{
Write_Data(word); //i=1;则写入数据
}
}
/***向LCD发送一个字符串即写汉字***/
void LCDSendWord(uchar *p)
{
while(*p>0)
{
Write_Data(*p);
p++;
}
}
/***往LCD上填写 年 月 日 小时 分钟 秒 星期以及它们的 数据***/
void DisplayYear()
{
year=read1302(0x8d); //从1302的0X8D处读出年数据
LCDTestWord(0,0x81); //写指令 在第一行第二个字符位置显示 年
LCDTestWord(1,(year/16)+0x30); //写数据
LCDTestWord(1,year%16+0x30); //写数据
LCDTestWord(0,0x82); //写指令 第一行第三个字符位置
LCDSendWord("年"); //显示 年 LCD DDRAM第一行地址为80-87,
//第二行为90-97,第三行为88-8F,第四行为98-9F
}
void DisplayMonth()
{
month=read1302(0x89); //从1302中读取月份数据
LCDTestWord(0,0x83); //写命令
if(month/16!=0)
{
LCDTestWord(1,(month/16)+0x30); //写数据
}
else
{
LCDTestWord(1,0x20); //写数据
}
LCDTestWord(1,month%16+0x30); //写数据
LCDTestWord(0,0x84); //写指令
LCDSendWord("月"); //显示 月
}
void DisplayDay()
{
day=read1302(0x87); //从1302中读取日期数据
LCDTestWord(0,0x85);
if(day/16!=0)
{
LCDTestWord(1,(day/16)+0x30);
}
else
{
LCDTestWord(1,0x20);
}
LCDTestWord(1,day%16+0x30);
LCDTestWord(0,0x86);
LCDSendWord("日");
}
void DisplayWeek()
{
week=(read1302(0x8b))%16; //从1302中读取星期数据
LCDTestWord(0,0x95);
LCDSendWord("星期");
LCDTestWord(0,0x97);
if(week==7) {LCDSendWord("日");}
if(week==6) {LCDSendWord("六");}
if(week==5) {LCDSendWord("五");}
if(week==4) {LCDSendWord("四");}
if(week==3) {LCDSendWord("三");}
if(week==2) {LCDSendWord("二");}
if(week==1) {LCDSendWord("一");} //显示星期一到星期日
}
void DisplayHour()
{
hour=read1302(0x85);
LCDTestWord(0,0x90);
LCDTestWord(1,(hour/16)+0x30);
LCDTestWord(1,hour%16+0x30);
}
void DisplayMin()
{
min=read1302(0x83);
LCDTestWord(0,0x91);
LCDTestWord(1,0x3a); //写入分割号 :
LCDTestWord(1,(min/16)+0x30);
LCDTestWord(1,min%16+0x30);
LCDTestWord(1,0x3a); //写入分割号 :
}
void DisplaySec()
{
sec=read1302(0x81);
LCDTestWord(0,0x93);
LCDTestWord(1,(sec/16)+0x30);
LCDTestWord(1,sec%16+0x30);
}
void UpDate()
{
DisplayYear(); //显示年
DisplayMonth(); //显示月
DisplayDay(); //显示日
DisplayWeek(); //显示星期
DisplayHour(); //显示时
DisplayMin(); //显示分
DisplaySec(); //显示秒
}
void SetTime(uchar count)
{
unsigned char address,item;
unsigned char max,mini;
LCDTestWord(0,0x98);
LCDSendWord("设置");
if(count==5) {LCDSendWord("秒钟 ");address=0x81; max=59;mini=0;}
if(count==4) {LCDSendWord("分钟 ");address=0x83; max=59;mini=0;}
if(count==3) {LCDSendWord("小时 ");address=0x85; max=23;mini=0;}
if(count==6) {LCDSendWord("星期 ");address=0x8b; max=7;mini=1;}
if(count==2) {LCDSendWord("日期 ");address=0x87; max=31; mini=1;}
if(count==1) {LCDSendWord("月份 ");address=0x89; max=12;mini=1;}
if(count==0) {LCDSendWord("年份 ");address=0x8d; max=99;mini=0;}
item=read1302(address);//读取DS1302某地址上的数值赋给item
item=(item/16)*10+item%16;
if(k2==0) //加
item++; //数加 1
if(k3==0) //-减
item--; //数减 1
if(item>max)
item=mini; //查看数值有效范围
if(item<mini)
item=max;
writeClkBye(0x8e,0x00);
item=(item/10)*16+item%10;
writeClkBye(address-1,item); //将调整好的item值写入DS1302
UpDate();
}
/*键盘扫描*/
void keyscan ()
{
if (k1==0) // 设置时间
{
DelayMs(10); //按键消抖
if(k1==0&&w==0) //当是正常状态时就进入调时状态
{
w=1; //进入调时
SetTime(next); //调整
}
if(k1==0&&w==1) //当是调时状态 本键用于调整下一项
{
next++;
if(next>=7) {next= 0;}
SetTime(next); //调整
}
while(k1==0); //等待键松开
}
if(k4==0) // 当在调时状态时就退出调时
{
DelayMs(10); //按键消抖
if(k4==0&&w==1)
{
w=0;next=0;
Write_Cmd(0x01); //清除LCD的显示内容
}
while(k4==0); //等待键松开
}
if (k2==0) //加调整
{
DelayMs(10); //按键消抖
if(k2==0&&w==1)
{
SetTime(next); //调整
}
while(k2==0); //等待键松开
}
if (k3==0) //减调整
{
DelayMs(10); //按键消抖
if(k3==0&&w==1)
{
SetTime(next); //调整
}
while(k3==0); //等待键松开
}
}
/***开机画面***/
void welcome()
{
LCDTestWord(0,0x81);
LCDSendWord("万年历设计");
LCDTestWord(0,0x91);
LCDSendWord("指导:…");
LCDTestWord(0,0x89);
LCDSendWord("制作:…");
LCDTestWord(0,0x99);
LCDSendWord("阳光总在风雨后");
}
bit GetMoonDay(uchar LunarMonth,uint TableAddr)
{
uchar temp;
switch (LunarMonth) //LunarMonth指向农历月份
{
case 1:
{
temp=YearCode[TableAddr]&0x08; //1月,对应年份表里第一字节的BIT3位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 2:
{
temp=YearCode[TableAddr]&0x04; //2月,对应年份表里第一字节的BIT2位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 3:
{
temp=YearCode[TableAddr]&0x02; //3月,对应第一字节的BIT1位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 4:
{
temp=YearCode[TableAddr]&0x01; //1月,对应第一字节的BIT0位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 5:
{
temp=YearCode[TableAddr+1]&0x80; //5月,对应第二字节的BIT7位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 6:
{
temp=YearCode[TableAddr+1]&0x40; //6月,对应第二字节的BIT6位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 7:
{
temp=YearCode[TableAddr+1]&0x20; //7月,对应第二字节的BIT5位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 8:
{
temp=YearCode[TableAddr+1]&0x10; //8月,对应第二字节的BIT4位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 9:
{
temp=YearCode[TableAddr+1]&0x08; //9月,对应第二字节的BIT3位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 10:
{
temp=YearCode[TableAddr+1]&0x04; //10月,对应第二字节的BIT2位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 11:
{
temp=YearCode[TableAddr+1]&0x02; //11月,对应第二字节的BIT1位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 12:
{
temp=YearCode[TableAddr+1]&0x01; //12月,对应第二字节的BIT0位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
case 13:
{
temp=YearCode[TableAddr+2]&0x80; //13月,对应第三字节的BIT7位
if (temp==0) return(0); //为0,月小
else return(1); //为1,月大
}
}
}
void Conversion(uchar year,uchar month,uchar day)
{
uchar temp1,temp2,temp3,MonthP;//temp3,temp4分别表示春节距元旦的天数
uint temp4,TableAddr; //公历日离元旦的天数
bit flag2,flag_y;
temp1=year/16; //BCD->hex 先把数据转换为十六进制 高位
temp2=year%16; //低位
year=temp1*10+temp2; //把 年 数据 转换成16进制
temp1=month/16; //月份 高位
temp2=month%16; //月份 低位
month=temp1*10+temp2; //把 月 数据 转换成16进制
temp1=day/16; //日期 高位
temp2=day%16; //日期 低位
day=temp1*10+temp2; //把 日 数据 转换成16进制
TableAddr=year*0x03; //定位数据表地址
LCDTestWord(0,0x80);
LCDSendWord("20");
temp1=YearCode[TableAddr+2]&0x60; //取当年春节所在的公历月份 年份表中第三字节BIT6-5表示春节的公历月份
temp1=_cror_(temp1,5); //循环右移5位,得到 春节所在的公历月份
temp2=YearCode[TableAddr+2]&0x1f; //取当年春节所在的公历日 年份表中第三字节BIT4-0表示当年春节所在的公历日
if(temp1==0x01) // 计算当年春年离当年元旦的天数,春节只会在公历1月或2月
temp3=temp2-1; //假如春节在公历1月,则元旦离春节的天数为 temp2-1 天
else
temp3=temp2+0x1f-1; //假如春节在公历2月,则无旦离春节的天数为 temp2+0x1f-1 天
if (month<10)
temp4=DayCode1[month-1]+day-1; //0到8月某日距元旦的天数
else
temp4=DayCode2[month-10]+day-1; //9月开始的某一天距元旦的天数
if ((month>0x02)&&(year%0x04==0)) //如果公历月大于2月并且该年的2月为闰月,天数加1
temp4+=1;
//计算机出公历日距元旦的天数和春节距元旦的天数,则是为了比较公历日是在春节前还是春节后
//如果temp3>temp4 则 公历日在春节之前
if (temp4>=temp3) //公历日在春节后或就是春节当日使用下面代码进行运算
{
temp4-=temp3; //公历日离春节的天数 因为公历日在春节后 所以为temp4-temp3
month=0x01;
MonthP=0x01; //LunarMonth为月份指向,公历日在春节前或就是春节当日LunarMonth指向首月
flag2=GetMoonDay(MonthP,TableAddr); //检查该农历月为大小还是小月,大月返回1,小月返回0
flag_y=0;
if(flag2==0) //GetMoonDay()函数返回的是0
{temp1=0x1d;} //小月29天
else //GetMoonDay()函数返回的是1
{temp1=0x1e;} //大月30天
temp2=YearCode[TableAddr]&0xf0; //年份数据表中第1字节BIT7-4为闰月,为0则这年无闰月,如为1,表示有闰月
temp2=_cror_(temp2,4); //从数据表中取该年的闰月月份,如为0,则该年无闰月 BIT3-0表示阴历1到4月的大小 1为大 0 为小
while(temp4>=temp1)
{
temp4-=temp1;
MonthP+=1;
if(month==temp2)
{
flag_y=~flag_y;
if(flag_y==0)month+=1;
}
else month+=1;
flag2=GetMoonDay(MonthP,TableAddr);
if(flag2==0)temp1=0x1d;
else temp1=0x1e;
}
day=temp4+1;
}
else
{ //公历日在春节前使用下面代码进行运算
temp3-=temp4; //公历日离春节的天数 因为公历日在春节前 所以为temp3-temp4
if (year==0x00){year=0x63;}
else year-=1;
TableAddr-=0x03;
month=0x0c;
temp2=YearCode[TableAddr]&0xf0; //格式第一字节BIT7-4位表示闰月月份,为0,则无闰月,BIT3-0对应阴历第1-4月的大小,
temp2=_cror_(temp2,4);
if (temp2==0)MonthP=0x0c;
else MonthP=0x0d; //
/* MonthP为月份指向,如果当年有闰月,一年有十三个月,月指向13,无闰月指向12*/
flag_y=0;
flag2=GetMoonDay(MonthP,TableAddr);
if(flag2==0)temp1=0x1d;
else temp1=0x1e;
while(temp3>temp1)
{
temp3-=temp1;
MonthP-=1;
if(flag_y==0)month-=1;
if(month==temp2)flag_y=~flag_y;
flag2=GetMoonDay(MonthP,TableAddr);
if(flag2==0)temp1=0x1d;
else temp1=0x1e;
}
day=temp1-temp3+1;
}
c_moon=1;
temp1=year/10;
temp1=_crol_(temp1,4);
temp2=year%10;
LunarYear=temp1|temp2;
temp1=month/10;
temp1=_crol_(temp1,4);
temp2=month%10;
LunarMonth=temp1|temp2;
temp1=day/10;
temp1=_crol_(temp1,4);
temp2=day%10;
LunarDay=temp1|temp2;
}
void Displaynongli()
{
uchar LunarYearD,ReYear;
Conversion(year,month,day);
LCDTestWord(0,0x88); //显示在LCD的0X94位置上
LCDSendWord("农历");
LCDTestWord(1,LunarMonth/16+0x30); //农历月十位
LCDTestWord(1,LunarMonth%16+0x30); //农历月个位
LCDSendWord("月");
LCDTestWord(1,LunarDay/16+0x30); //农历日十位
LCDTestWord(1,LunarDay%16+0x30); //农历日个位
LunarYearD=(LunarYear/16)*10+LunarYear%16; //农历年转换成10进制数
ReYear=LunarYearD%12; //农历年模12,取余运算
switch(ReYear)
{
case 0: LCDTestWord(0,0x8e);LCDSendWord("龙年");break; //余0即整除 农历 龙年
case 1: LCDTestWord(0,0x8e);LCDSendWord("蛇年");break; //蛇年
case 2: LCDTestWord(0,0x8e);LCDSendWord("马年");break;
case 3: LCDTestWord(0,0x8e);LCDSendWord("羊年");break;
case 4: LCDTestWord(0,0x8e);LCDSendWord("猴年");break;
case 5: LCDTestWord(0,0x8e);LCDSendWord("鸡年");break;
case 6: LCDTestWord(0,0x8e);LCDSendWord("狗年");break;
case 7: LCDTestWord(0,0x8e);LCDSendWord("猪年");break;
case 8: LCDTestWord(0,0x8e);LCDSendWord("鼠年");break;
case 9: LCDTestWord(0,0x8e);LCDSendWord("牛年");break;
case 10:LCDTestWord(0,0x8e);LCDSendWord("虎年");break;
case 11:LCDTestWord(0,0x8e);LCDSendWord("兔年");break;
}
}
/***主程序***/
main()
{
k1=1;
k2=1;
k3=1;
k4=1;
Init_ST7920();
welcome();
delay(3000);
init1302();
Init_ST7920();
while(1)
{
DisplayYear(); //显示年
DisplayMonth(); //显示月
DisplayDay(); //显示日
DisplayWeek(); //显示星期
DisplayHour(); //显示时
DisplayMin(); //显示分
DisplaySec(); //显示秒
Displaynongli(); //显示农历
keyscan(); //键盘扫描
}
}
|