/*---------------------------------------------------------------------------
/*-------------------------------头文件---------------------------------------*/
#include <reg52.h>
#include <intrins.h>
#include <string.h>
#include <stdlib.h>
#include "LCD12864.h"
#include "DS3231.h"
#include "nongli.h"
#include "displaytime.h"
#include "dashuzi.h"
const char chHex[16] = "0123456789ABCDEF";
/********************************************************************************************************************
以下为GPS部分定义 (如果纯为显示GPS信息只需要定义变量RsBuf[80]就够了,两个结构体可以不要,边解析边显示 )
********************************************************************************************************************/
//串口中断需要的变量
uchar seg_count; /** 逗号计数器 **/
uchar byte_count; /** 位数计数器 **/
uchar mode; /** 0:结束模式,1:命令模式,2:数据模式 **/
uchar buf_full; /** 1:整句接收完成,相应数据有效。0:缓存数据无效 **/
typedef xdata struct
{
uchar TIME[10];
uchar VA[2];
uchar WD[11];
uchar WDNS[2];
uchar JD[12];
uchar JDWE[2];
uchar DATE[9];
} GPS_GPRMC;
GPS_GPRMC gps;
typedef xdata struct /** 作时区转换时需要用到年、月、日进一 **/
{
uchar Hour;
uchar Min;
uchar Sec;
uchar Day;
uchar Mon;
uchar Year;
} TIMER;
TIMER Timer;
char idata RsBuf[80]; /** 全局变量 **/
void InitBps(); /** 串口初始化 **/
void UTCToLocal(TIMER *GPS_DataTmp);
/**** 以上为GPS部分定义 ***************************************************/
/*--------------------定义按键-----------------------------------------------*/
sbit K1 = P1 ^ 6; //K1-进入设置;GPS模式下为强制校时
sbit K2 = P1 ^ 5; //K2-调时模式下为 加
sbit K3 = P1 ^ 3; //K3-调时模式下为 减;运行模式下为背景灯控制
sbit K4 = P1 ^ 4; //K4-调时模式下为 确认、返回 ;运行模式下为DS3231与GPS运行界面切换
sbit BLK = P2 ^ 3; //液晶背光控制输出,低电平有效,PNP三极管控制。
sbit Bell_Out = P1 ^ 7;
/*---------------------函数声明------------------------------*/
void DelayM(uint);
void Delay(int);
void ds_w(void);
void GetDS3231(void);
void Conver_week(uchar year, uchar month, uchar day);
/*-----------------------------定义全局变量------------------------------*/
bit q = 0, w = 0; //调时标志位
uchar yy, mo, dd, xq, hh, mm, ss, month_moon, day_moon, week, tiangan, dizhi, moontemp1, moontemp2; //定义时间映射全局变量(专用寄存器)
signed char address, item, max, mini;
/*-----------------------------延时函数 1MS/次-------------------------------*/
void DelayM(uint a)
{
uchar i;
while( --a != 0)
{
for(i = 0; i < 125; i++);
}
}
/*-----------------------------日期、时间设置函数-----------------------------*/
void tiaozheng(void)
{
yy = read_random(DS3231_YEAR);
mo = read_random(DS3231_MONTH);
dd = read_random(DS3231_DAY);
week = read_random(DS3231_WEEK);
lcm_w_test(0, 0x80);
lcm_w_word("20"); //显示内容字符20
lcm_w_test(1, yy / 10 + 0x30); //函数参数1,代表本行写数据,YY/10+0X30得出年十位数字的显示码地址,送显示
lcm_w_test(1, yy % 10 + 0x30);
lcm_w_word("年");
lcm_w_test(1, mo / 10 + 0x30);
lcm_w_test(1, mo % 10 + 0x30);
lcm_w_word("月"); //调用字符显示函数,显示文字 月
lcm_w_test(1, dd / 10 + 0x30);
lcm_w_test(1, dd % 10 + 0x30);
lcm_w_word("日"); //显示字符 日
if(read_random(DS3231_HOUR) != hh) //如果程序中的小时与1302芯片中的不同,
{
hh = read_random(DS3231_HOUR); //刷新程序中的小时数据
}
lcm_w_test(0, 0x91); //第一个参数0,表示本行写入LCM的是指令,指定显示位置88H(第三行左端)
lcm_w_test(1, (hh / 10) + 0x30); //显示十位
lcm_w_test(1, hh % 10 + 0x30); //显示个位
lcm_w_word("时");
if(read_random(DS3231_MINUTE) != mm) //如果1302芯片中的分钟数据与程序中的分钟变量不相等
{
mm = read_random(DS3231_MINUTE) ; //刷新程序中的分钟数据
}
lcm_w_test(1, (mm / 10) + 0x30); //向液晶写数据,显示分钟的十位数
lcm_w_test(1, mm % 10 + 0x30); //向液晶写数据,显示分钟的个位数
lcm_w_word("分");
if(read_random(DS3231_SECOND) != ss) //如果1302芯片中的分钟数据与程序中的秒钟变量不相等
{
ss = read_random(DS3231_SECOND); //刷新程序中的秒钟数据
}
lcm_w_test(1, (ss / 10) + 0x30); //向液晶写数据,显示分钟的十位数
lcm_w_test(1, ss % 10 + 0x30); //向液晶写数据,显示分钟的个位数
lcm_w_word("秒");
}
/**********************************************************************************************************/
//调整时间子函数,设置键、数据范围、上调加一,下调减一功能。
void Set_time(unsigned char sel) //根据选择调整的相应项目加1并写入DS1302,函数参数是按动设置键的次数
{
write_com(0x30);
write_com(0x06);
lcm_w_test(0, 0x98); //第一参数0表示本行写入指令,指定下面行的 调整 显示起始位置为9AH
lcm_w_word("★调整");//调用字符显示函数,显示 调整字样
if(sel == 5)
{
lcm_w_word("秒钟");
address = DS3231_SECOND;
max = 59;
mini = 0;
tiaozheng(); //调用日期、时间调整函数
ds_w(); //被调数据加一或减一函数
tiaozheng();
} //秒7,按动7次显示 调整秒钟
//并指定秒钟数据写入1302芯片的地址是0x82,秒钟数据的最大值是59,最小值是0
if(sel == 4)
{
lcm_w_word("分钟");
address = DS3231_MINUTE;
max = 59;
mini = 0;
tiaozheng();
ds_w();
tiaozheng();
} //分钟6,按动6次显示 调整分钟
//并指定分钟数据写入1302芯片的地址是0x82,分钟数据的最大值是59,最小值是0
if(sel == 3)
{
lcm_w_word("小时");
address = DS3231_HOUR;
max = 23;
mini = 0;
tiaozheng();
ds_w();
tiaozheng();
} //小时5,按动5次显示 调整小时
//规定小时数据写入1302芯片的位置是0x84,小时数据最大值23,最小值是0
if(sel == 2)
{
lcm_w_word("日期");
address = DS3231_DAY;
mo = read_random(DS3231_MONTH);//读月数据
yy = read_random(DS3231_YEAR);//读年数据
if(mo == 2 && yy % 4 != 0)
{
max = 28; //平年2月28天
mini = 1;
}
if(mo == 2 && yy % 4 == 0)
{
max = 29; //闰年2月29天
mini = 1;
}
if(mo == 1 || mo == 3 || mo == 5 || mo == 7 || mo == 8 || mo == 10 || mo == 12)
{
max = 31; //31天的月份
mini = 1;
}
if(mo == 4 || mo == 6 || mo == 9 || mo == 11)
{
max = 30; //30天的月份
mini = 1;
}
tiaozheng();
ds_w();
tiaozheng(); //调用日期、时间调整函数
} //日3,按动3次显示 调整日期
//规定日期数据写入1302的位置地址是0x86,日期最大值31,最小值是1
if(sel == 1)
{
lcm_w_word("月份");
address = DS3231_MONTH;
max = 12;
mini = 1;
tiaozheng();
ds_w();
tiaozheng();
} //月2,按动2次显示 调整月份
//规定月份写入1302的位置地址是0x88,月份最大值12,最小值1
if(sel == 0)
{
lcm_w_word("年份");
address = DS3231_YEAR;
max = 99;
mini = 0;
tiaozheng();
ds_w(); //被调数据加一或减一函数
tiaozheng(); //调用日期、时间调整函数
} //年1,按动1次显示 调整年份,
//规定年份写入1302的地址是0x8c,年份的最大值99,最小值0
}
/*****************************************************************************/
//被调数据加一或减一,并检查数据范围,写入1302指定地址保存
void ds_w(void)
{
item = read_random(address);
if(K2 == 0) //如果按动上调键
{
item++;//数加 1
}
if(K3 == 0) //如果按动下调键
{
item--;//数减 1
}
if(item > max) item = mini; //查看数值是否在有效范围之内
if(item < mini) item = max; //如果数值小于最小值,则自动等于最大值
ModifyTime(address, item);
}
//=================================BEEP驱动===========================================//
//需要定义Delay,输出Bell_Out引脚
/********************************************************************************************/
void beep ( unsigned char a, unsigned char b,
unsigned char c, unsigned char d)
{
for(; a > 0; a--) //第一个声音的长度
{
Bell_Out = ~Bell_Out;//取反扬声器驱动口,以产生音频
Delay(b);//音调设置延时
}
for(; c > 0; c--) //同上
{
Bell_Out = ~Bell_Out;
Delay(d);//
}
Bell_Out = 1;
}
void Beep_y(void)
{
beep(50, 100, 100, 50); //BELL
}
//--------------------------------------------扬声器--无效音
//void Beep_n(void){beep(50,80,100,100); }//BELL
void Delay(int num)//延时函数
{
while(num--) ;
}
/********************************************************************************************************************
以下为GPS部分
********************************************************************************************************************/
void InitBps()
{
//T2设置
#if (PARITYBIT == NONE_PARITY)
SCON = 0x50; //8-bit variable UART
#elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
SCON = 0xda; //9-bit variable UART, parity bit initial to 1
#elif (PARITYBIT == SPACE_PARITY)
SCON = 0xd2; //9-bit variable UART, parity bit initial to 0
#endif
TL2 = RCAP2L = (65536-(FOSC/32/BAUD)); //Set auto-reload vaule = 0xD9;
TH2 = RCAP2H = (65536-(FOSC/32/BAUD)) >> 8; // = 0xFF;
T2CON = 0x34; //TF2 EXF2 RCLK TCLK EXEN2 TR2 C/T2 CP/RL2 //串口工作模式1,接收使能
ET2 = 0;
ES = 1; //串口中断允许
PS = 1; //串口中断优先
EA = 1; //总中断
Delay(500);
}
uchar Checksum(char *s) /** gps原生校验 **/
{
uchar Sum, i;
char chck[3] = {0};
uint length=0;
length = strlen(s);
if(length < 38 || s[0] != '$' || s[length - 3] != '*') /** 非完整语句 **/
return 0;
Sum = s[1];
for(i = 2; i < length - 3 && s[i] != '*'; i++) /** 求校验和 **/
{
Sum ^= s[i];
}
if(chHex[ (unsigned int)Sum / 16]== s[length - 2] && chHex[ (unsigned int)Sum % 16] == s[length - 1] )
return 1;
else
return 0;
}
void FormatString(char *p) /** gps数据长度不定,不能用固定位截取。把分隔符 ',' 转换为'\0'自动变换为相应的信息段 **/
{
uint i, j, k;
uchar q[12];
uint len = strlen(p) ;
for(i = 0; i < len; i++)
{
if(p[i] == ',')
p[i] = '\0';
}
len-=6;
for(i = 0, j = 0, k = 0; i < len; i++)
{
q[j++] = p[i];
if(p[i] == '\0')
{
switch(k)
{
case 1:
strcpy(gps.TIME,q);
break;
case 2:
strcpy(gps.VA,q);
break;
case 3:
strcpy(gps.WD,q);
break;
case 4:
strcpy(gps.WDNS,q);
break;
case 5:
strcpy(gps.JD, q);
break;
case 6:
strcpy(gps.JDWE, q);
break;
case 9:
strcpy(gps.DATE, q);
break;
default:
break;
}
k++;
j = 0;
}
}
strcpy(q, gps.TIME);
q[2] = '\0';
Timer.Hour = atoi(q);
strcpy(q, &gps.TIME[2]);
q[2] = '\0';
Timer.Min = atoi(q);
strcpy(q, &gps.TIME[4]);
q[2] = '\0';
Timer.Sec = atoi(q);
strcpy(q, gps.DATE);
q[2] = '\0';
Timer.Day = atoi(q);
strcpy(q, &gps.DATE[2]);
q[2] = '\0';
Timer.Mon = atoi(q);
strcpy(q, &gps.DATE[4]);
q[2] = '\0';
Timer.Year = atoi(q);
UTCToLocal(&Timer);
}
void UTCToLocal(TIMER *GPS_DataTmp) /** 时区变换 **/
{
GPS_DataTmp->Hour += TIME_AREA;
if( GPS_DataTmp->Hour >= 24) //如果小时数大于24
{
GPS_DataTmp->Hour -= 24; //小时数减24
GPS_DataTmp->Day++; //日期数加1
switch(GPS_DataTmp->Day) //判断日期
{
case 29: //普通年的2月份
if((!((GPS_DataTmp->Year % 400 == 0) || ((GPS_DataTmp->Year % 4 == 0) && (GPS_DataTmp->Year % 100 != 0))) && (GPS_DataTmp->Mon == 2)))
{
GPS_DataTmp->Day = 1;
GPS_DataTmp->Mon++;
}
break;
case 30: //如果是闰年的2月
if(((GPS_DataTmp->Year % 400 == 0) || ((GPS_DataTmp->Year % 4 == 0) && (GPS_DataTmp->Year % 100 != 0))) && (GPS_DataTmp->Mon == 2))
{ /** 闰年:四年一闰,百年不润,四百年再闰 **/
GPS_DataTmp->Day = 1;
GPS_DataTmp->Mon++;
}
break;
case 31: /** 小月 **/
if((GPS_DataTmp->Mon == 4) || (GPS_DataTmp->Mon == 6) || (GPS_DataTmp->Mon == 9) || (GPS_DataTmp->Mon == 11))
{
GPS_DataTmp->Day = 1;
GPS_DataTmp->Mon++;
}
break;
case 32: /** 大月,12月需年加一 **/
GPS_DataTmp->Day = 1;
GPS_DataTmp->Mon++;
if(GPS_DataTmp->Mon >= 13)
{
GPS_DataTmp->Year++;
GPS_DataTmp->Mon = 1;
}
break;
}
}
}
void GetRs232_Data() interrupt 4 /** GPS信息采集 **/
{
uchar tmp;
if(RI)
{
tmp = SBUF;
switch(tmp)
{
case '$':
mode = 1; //接收命令模式
byte_count = 0; //接收位数清空
memset(RsBuf,0,80);
RsBuf[byte_count++] = tmp;
break;
case '\r': /** 亦可用换行符'\n',见到几乎所有的gps C51程序都是用‘*’作标志尾!! 原生的校验为何不用?? **/
if(mode == 2)
buf_full = 1;
else
buf_full = 0;
mode = 0;
RsBuf[byte_count] = '\0';
break;
default:
if(mode == 1)
{
//命令种类判断
RsBuf[byte_count] = tmp; //接收字符放入类型缓存
if(byte_count == 5) //如果类型数据接收完毕,判断类型
{
if(RsBuf[1] == 'G' && RsBuf[2] == 'P' && RsBuf[3] == 'R')
{
if(RsBuf[4] == 'M' && RsBuf[5] == 'C')
{
mode = 2; /** 获取标志头 $GPRMC 缓存可以接收后续字符 **/
}
else
{
mode = 0;
byte_count = 0;
}
}
else
{
mode = 0;
byte_count = 0;
}
}
byte_count++;
}
else if(mode == 2)
{
RsBuf[byte_count] = tmp;
byte_count++;
}
if(byte_count > 73) /** 接收数据过长,超过73Byte还没标志尾抛弃 **/
{
mode = 0;
byte_count = 0;
memset(RsBuf,0,80);
}
break;
}
if(buf_full && byte_count > 36 && Checksum(RsBuf)) /*** 标志头尾齐全、至少有时间数据、验证码无误,才能解析数据 ***/
{
FormatString(RsBuf);
buf_full = 0;
}
}
RI = 0;
}
void ShowGPS(void) /** 显示GPS时间、定位信息界面 **/
{
uchar i = 0;
if(gps.DATE[0] >= '0' && gps.DATE[0] <= '9')
{
lcm_w_test(0,0x30);
lcm_w_test(0,0x06);
write_com(0x81);
write_data(0x32); /** 补年号 20** **/
write_data(0x30);
write_data(Timer.Year / 10 + '0');
write_data(Timer.Year % 10 + '0');
write_data('-');
write_data(Timer.Mon / 10 + '0');
write_data(Timer.Mon % 10 + '0');
write_data('-');
write_data(Timer.Day / 10 + '0');
write_data(Timer.Day % 10 + '0');
write_com(0x91);
write_data(Timer.Hour / 10 + '0');
write_data(Timer.Hour % 10 + '0');
write_data(':');
write_data(Timer.Min / 10 + '0');
write_data(Timer.Min % 10 + '0');
write_data(':');
write_data(Timer.Sec / 10 + '0');
write_data(Timer.Sec % 10 + '0');
if(gps.VA[0] == 'A') /** GPS定位有效则显示定位,否则提示 定位中 **/
{
write_com(0x88);
if(gps.JDWE[0] == 'E')
lcm_w_word("东经:");
if(gps.JDWE[0] == 'W')
lcm_w_word("西经:");
for(i = 0; i < 3; i++)
write_data(gps.JD[i]);
write_data('.');
for(i = 3; i < 8; i++)
write_data(gps.JD[i]);
write_com(0x98);
if(gps.WDNS[0] == 'N')
lcm_w_word("北纬: ");
if(gps.WDNS[0] == 'S')
lcm_w_word("南纬:");
for(i = 0; i < 2; i++)
write_data(gps.WD[i]);
write_data('.');
for(i = 2; i < 7; i++)
write_data(gps.WD[i]);
}
else
{
write_com(0x88);
lcm_w_word("定位中........");
write_com(0x98);
lcm_w_word("按MOD 键强制校时");
}
}
else
{
lcm_w_test(0,0x30);
lcm_w_test(0,0x06);
write_com(0x88);
lcm_w_word(" 编程实现乐趣。");
write_com(0x98);
lcm_w_word(" 算法改变世界。");
}
}
void GPS_JS(void) /** GPS自动校时 **/
{
if(Timer.Year<14) //低于2014无效
return;
GetDS3231();
if(Timer.Year!=year)
ModifyTime(DS3231_YEAR,Timer.Year); //写入3231
if(Timer.Mon!=month)
ModifyTime(DS3231_MONTH,Timer.Mon);
if(Timer.Day!=day)
ModifyTime(DS3231_DAY,Timer.Day);
if(Timer.Hour!=hour)
ModifyTime(DS3231_HOUR,Timer.Hour);
if(Timer.Min!=min)
ModifyTime(DS3231_MINUTE,Timer.Min);
Timer.Sec++;//gps接收处理耽搁约0.5s,在此处修正1s
if(Timer.Sec!=sec)
ModifyTime(DS3231_SECOND,Timer.Sec);
Conver_week(Timer.Year,Timer.Mon,Timer.Day);//调用公历换算星期子函数
if(week==0) //DS3231 中用 1-7 代表周一至周日
week=7;
ModifyTime(DS3231_WEEK,week);
}
/********************************************************************************************************************
以上为GPS部分
********************************************************************************************************************/
void GetDS3231() //获取当前日期和时间
{
year = read_random(DS3231_YEAR); //年
month = read_random(DS3231_MONTH); //月
day = read_random(DS3231_DAY); //日
hour = read_random(DS3231_HOUR); //时
min = read_random(DS3231_MINUTE); //分
sec = read_random(DS3231_SECOND); //秒
}
/*主函数---------------------------------------------------------------------*/
void main()
{
uchar e = 0;
uint cc=0;//计时5min
uchar sc=0;//sec
bit s = 0; /** 翻页用 **/
bit js = 1; /** gps校时用,在GPS界面校时一次就行了 **/
K1 = 1;
K2 = 1;
K3 = 1;
K4 = 1;
BLK = 0;
InitBps();
mode = 0;
InitBps();
lcm_init(); //液晶初始化
welcome(); //调用欢迎信息
DelayM(1000); //延时
lcm_clr(); //清屏
Clean_12864_GDRAM(); //清屏
while(1)
{
if (w == 0) //正常走时
{
if(s)
{
if(sc!=Timer.Sec)
{
ShowGPS();
if(gps.VA[0] == 'A')
cc++;
else
cc=0 ;
sc=Timer.Sec;
}
if( cc>=300 && js && Timer.Sec<59) /** 出现定位数据5分钟以上表明时间一定有效。 在定位界面只需校时一次。<59 有含义,自己体会! **/
{
GPS_JS();
js=0;
}
/*12小时自动校时一次,应一坛友要求加的该功能。其实完全无必要,如果GPS一直挂着就直接用 GPS+12864 程序!*/
if(js==0 && Timer.Hour%12==0 && Timer.Min==0 && Timer.Sec==0)
{
GPS_JS();
}
}
else
{
GetDS3231();
if(sc!=sec) //秒有更新才刷新lcd
{
displaydate(); //显示日期
displaynl(); //显示农历
displaytime(); //显示时间
displayxq(); //显示星期
displaytemp();
sc=sec;
}
js=1;
}
/*液晶背光控制,按一下亮,再按一下灭----------------------------------------*/
if(K3 == 0)
{
DelayM(20);
q = ~q; //标志位取反
if(q)
{
BLK = BLK | 1;
}
else
{
BLK = BLK & 0;
}
while(K3 == 0);
}
}
else
{} //否则启动调时
/*----------------------------设置时间--------------------------------------*/
if (K1 == 0)
{
DelayM(20); //按键消抖
if(K1 == 0 && s && Timer.Sec<59) //如果GPS模式强制校时
{
GPS_JS();
while(! K1 );
Beep_y();
}
else
{
if(K1 == 0 && w == 1) //当是调时状态 本键用于调整下一项
{
e++;
if (e >= 7 )
{
e = 0;
}
while(! K1 ); //等待键松开
Set_time(e); //调整
}
if(K1 == 0 && w == 0) //当是正常状态时就进入调时状态
{
lcm_clr();
Clean_12864_GDRAM(); //清屏
w = 1; //进入调时
Set_time(e);
}
}
while(K1 == 0); //等待键松开
}
/*--------------------------------------------------------------------------*/
if (K4 == 0) // 当在调时状态时就退出调时
{
DelayM(20);
if(K4 == 0 && w == 0)
{
lcm_clr();
Clean_12864_GDRAM();
write_com(0x30);
write_com(0x06);
welcome();
DelayM(2000);
s=~s; /** 运行模式中 K2 作界面切换键 **/
if(s) /** 进入GPS界面即重新获取定位信息,防止GPS断开后保留的最后一次时间给校时用了 **/
gps.VA[0] = 'V';
cc=0 ;//重新进行有效计时
while(K4 == 0);
}
if(K4 == 0 && w == 1) /** 和前个if 切勿颠倒 **/
{
w = 0; //退出调时
e = 0; //"下一项"计数器清0
}
lcm_clr();
Clean_12864_GDRAM();
while(K4 == 0);
Beep_y();
}
/*加调整--------------------------------------------------------------------*/
if (K2 == 0 && w == 1)
{
DelayM(20);
if(K2 == 0 && w == 1)
{
Set_time(e);
}
while(! K2 );
}
/*减调整--------------------------------------------------------------------*/
if (K3 == 0 && w == 1)
{
DelayM(20);
if(K3 == 0 && w == 1)
{
Set_time(e);
}
while(! K3 );
}
}
}
/*结束----------------------------------------------------------------------*/
|