/*---------------------------------------------------------------------------
/*-------------------------------頭文件---------------------------------------*/
#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 );
}
}
}
/*結束----------------------------------------------------------------------*/
|