?? time1380.c
字號:
//time1380.c
//HT1380操作時序,以及時間設置和LCD時間顯示(顯示小時和分鐘)
#include "lcd.h"//參見LCD章節子程序
sbit rst80=P0^2;//HT1380 REST 管腳
sbit VSCL=P0^0;//HT1380 時鐘管腳
sbit VSDA=P0^1;//HT1380 時鐘數據線
bdata uchar bitdata;//巧用bdata 空間的位操作性能
sbit bd7=bitdata^7;
sbit bd0=bitdata^0;
uchar send;
void sw80(uchar cmd);//對HT1380串行寫操作函數
struct timef{
uchar hour;
uchar min;
}time1;//時間數據結構
xdata uchar timestr[6];
void showtime();
void initime(){//HT1380初始化操作
rst80=0;
VSCL=0;//對HT1380操作無效之前,確保時鐘線為低電平
_nop_();
_nop_();
rst80=1;
sw80(0x8e);//選中Write Protect寄存器寫操作
sw80(0x00);//將WP清零,使得寄存器寫操作使能
rst80=0;//此次操作結束,該語句不能省略
_nop_();
_nop_();
rst80=1;
sw80(0x80);//選中second寄存器寫操作
sw80(0x00);//將CH清零,使時鐘運行
rst80=0;//此次操作結束
_nop_();
}
void sw80(uchar cmd){//對HT1380串行寫操作函數
uchar i;
bitdata=cmd;
VSCL=0;//先發送最低位,寫操作是在時鐘上升沿有效
//所以在時鐘線為0的前提下,先寫數據,再將時鐘線置1,構造一個上升沿
//隨后再將時鐘線置低電平
for(i=0;i<8;i++){
if(bd0){
VSDA=1;
_nop_();
VSCL=1;
_nop_();
VSCL=0;
}
else{
VSDA=0;
_nop_();
VSCL=1;
_nop_();
VSCL=0;
}
bitdata>>=1;//巧用移位操作和BDATA區能位尋址,使得程序效率大大提高
}
VSDA=1;//單片機口對讀數據須先置1后讀取,此操作對下一次讀數據打基礎
//此代碼不能省略,否則當VSDA=0,則讀數據時可能將高電平數據讀成0,因為
//數據線上的高電平會被單片機口下拉為0
}
uchar sr80(void){//對HT1380讀操作
uchar i;
bd7=VSDA;//寫Command寄存器后,由SW80()函數可知,寫完數據后,時鐘線由高電平
//變為低電平,產生一個下降沿,所以此時HT1380已經將第一位數據輸出
//筆者曾花了3天時間才查出這個操作BUG,所以對有上述SW80()寫操作時序習慣的
//讀者而言,可能是個很好的提示,希望能對其他類似的串行操作調試提供經驗幫助
bitdata>>=1;
for(i=0;i<6;i++){//接收時是先接收到最低位,所以應先把最低位存在最高位,經過
//右移移位操作,當全部數據接收到時,能保證最低位出在存儲空間的最低位
VSCL=1;
_nop_();
_nop_();
VSCL=0;
bd7=VSDA;
bitdata>>=1;
}
VSCL=1;
_nop_();
_nop_();
VSCL=0;
bd7=VSDA;
VSCL=0;
return bitdata;
}
void settime(){//由于篇幅問題,具體由讀者去完善
}
void setmin(uchar min){//分鐘寄存器設置,min為BCD碼
rst80=0;
_nop_();
rst80=1;
sw80(0x82);//選中minutes寄存器,寫操作
sw80(min);
rst80=0;
_nop_();
}
void sethour(uchar hour){//小時寄存器設置
rst80=0;
_nop_();
rst80=1;
sw80(0x84);//選中hours寄存器,寫操作
sw80(hour);
rst80=0;
_nop_();
}
void readtime(){
rst80=0;
_nop_();
VSCL=0;
_nop_();
rst80=1;
sw80(0x83);//選中minutes寄存器,讀操作
time1.min=sr80();//獲取當前時間分鐘值
rst80=0;
_nop_();
rst80=0;
_nop_();
VSCL=0;
_nop_();
rst80=1;
sw80(0x85);//選中hours寄存器,讀操作
time1.hour=sr80();//獲取當前時間小時值
rst80=0;
_nop_();
rst80=0;
_nop_();
VSCL=0;
_nop_();
rst80=1;
sw80(0x81);//選中senconds寄存器,讀操作
send=sr80();//讀取當前秒鐘值
rst80=0;
_nop_();
}
void showtime(){
//顯示效果,比如23點14分//
//若秒值為奇數,顯示如下:
// 23 14//
//若秒值為偶數,顯示如下:
// 23:14//
//從而,隨著秒值的改變,顯示第三位有閃動效果
uchar tt;
tt=time1.hour&0x0f;//得到小時數據的個位數值
timestr[1]=tt+'0';//將其轉化成ASC碼字符,且小時個位數顯示在第二位
tt=time1.hour&0xf0;//由于是24小時制,小時十位數也可直接獲取
if(tt!=0){//小時十位數不為0
tt/=16;//由于處于高4為,采用除以16,將數據移到低4位
timestr[0]=tt+'0';//小時十位數顯示在第一位
}
else{//小時十位數為0時的處理
if(timestr[1]=='0')//習慣上零點時顯示兩個0
timestr[0]='0';
else
timestr[0]=' ';
}
if(send&0x01)//第三位與秒值的顯示關系
timestr[2]=' ';
else
timestr[2]=':';
tt=time1.min&0xf0;
tt/=16;
timestr[3]=tt+'0';//第四位顯示分鐘的十位數
tt=time1.min&0x0f;
timestr[4]=tt+'0';//第五位顯示分鐘的個位數
timestr[5]='\0';
showline(timestr,0);
}
void disptime(){//由有興趣的讀者去完善
}
void formatedata(){//由有興趣的讀者去完善
}
void config (void) {
//看門狗禁止
WDTCN = 0x07;
WDTCN = 0xDE;
WDTCN = 0xAD;
SFRPAGE = 0x0F;
XBR0 = 0x00;
XBR1 = 0x00;
XBR2 = 0x40; //交叉開關使能,使得P0-P3口能輸出
XBR3 = 0x00;
SFRPAGE = 0x0F;
P0MDOUT = 0x00; //端口配置,P0-P3,P6-P7口為開漏輸出
P1MDOUT = 0x00;
P2MDOUT = 0x00;
P3MDOUT = 0x00;
P4MDOUT = 0x00; //P4口為開漏
P5MDOUT = 0x07;
P6MDOUT = 0x00;
P7MDOUT = 0x00;
P1MDIN = 0xFF; //所有端口為數字輸入,沒有模擬輸入端口
P2MDIN = 0xFF;
P3MDIN = 0xFF;
SFRPAGE = 0x0F;
CLKSEL = 0x00;
OSCXCN = 0x00;
OSCICN = 0x84;
//采用內部晶振,為24.5MHZ8分頻
}
void main(){
uint knum;
uchar mnum;
uint hnum;
uchar tk;
config();//初始化MCU
delay1ms(50);
dispini();//初始化LCD
EA=0;
initime();//初始化HT1380
delay1ms(1000);
knum=0;
hnum=0;
for(;;){
readtime();
showtime();
if(scankey()==2){//長按鍵延時的一種處理方法
//在時間設定時,希望長按鍵才生效,以免誤操作
//此為小時設置按鍵值
knum++;
}
if(scankey()!=2){
knum=0;
}
if(knum>30){//若knum>30,則認為長按鍵有效
for(;;){
if(scankey()==2){
knum=300;
time1.hour++;
tk=time1.hour&0x0f;
if(tk>9){//對小時值個位從9變為10時的進位處理
tk=time1.hour&0xf0;
tk+=0x10;
time1.hour=tk;//此時個位值為0
}
tk=time1.hour&0x0f;
if(tk>3){//因為是24小時制,所以在23點時,個位增1就是0點
tk=time1.hour&0xf0;
if(tk>0x10)
time1.hour=0;//由23點增1變到0點的處理
}
showtime();
}
while(scankey()==2);//若手一直將按鍵按住,則視為按鍵等待
//這種處理方法不是很好,最好做成超時退出,由有興趣的
//讀者來完善,若是實時操作系統,這個問題很好解決
//否則,處理起來考慮的問題還是很多
if(scankey()!=2){//對設置時鐘的間隔處理,若按鍵有一段時間沒有
//按下,則knum值自減
delay1ms(10);
knum--;
}
if(knum<3){//當knum小于3,則將小時設置寫入HT1380,退出小時設置
sethour(time1.hour);
break;
}
}
}
if(scankey()==3){//分鐘設置處理方法與小時雷同
hnum++;
}
if(scankey()!=3){
hnum=0;
}
if(hnum>30){
for(;;){
if(scankey()==3){
hnum=300;
time1.min++;
tk=time1.min&0x0f;
if(tk>9){
tk=time1.min&0xf0;
tk+=0x10;
if(tk>0x50)
tk=0;
time1.min=tk;
}
showtime();
}
while(scankey()==3);
if(scankey()!=3){
delay1ms(10);
hnum--;
}
if(hnum<3){
setmin(time1.min);
break;
}
}
}
}
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -