一个万年历程序,自己写的,当然有参考一下别人的程序,lcd显示,用的是ds1302芯片调时,剩下就是一些按键之类的,现在问题是其他都正常,就是lcd显示正常计时的时候lcd上一些位置显示的是问号和数字间接闪烁,数字应该都读进去了,也可以正常读出,但是就是显示不正常。其他功能均正常。 电路绝对没有问题,拿别人的万年历试验过完全可以用。 都调了两天了,人都快疯了!可能我比较菜,但是我可以想到的办法都试过了,就是找不到问题所在,哪位好心人帮我看一下吧!或者跟我说个大概方向也成。 PS:示波器之类我没有,也找不到,所以没有办法用那个。 程序如下: //调用函数库 #include<reg51.h> #include<intrins.h>
//宏定义 #define uchar unsigned char #define uint unsigned int
//位定义 sbit ACC0 = ACC^0; sbit ACC7 = ACC^7;
sbit T_CLK=P2^2; //与硬件相关的连线 clk为DS1302的时钟信号线 sbit T_IO=P2^3; //DAT为DS1302的I/O数据线 sbit T_RST=P2^4; //RST为DS1302的RST信号线
sbit lcd_rs=P2^6; sbit lcd_rw=P2^7; sbit lcd_en=P2^5;
sbit DS=P2^1; //定义温度传感器接口
sbit sw_add=P1^0; //定义增加键 sbit sw_move=P1^1; //定义光标左移动键 sbit sw_changeweb=P1^3; //定义翻页键 sbit sw_back=P3^0; //定义回到计时按键
sbit ring_bell=P1^2; //蜂鸣器接口
uchar a[7]={0x00,0x15,0x14,0x03,0x06,0x02,0x09}; //初始化日期 uchar week[21]={0x4d,0x6f,0x6e,0x54,0x75,0x65,0x57,0x65,0x64,0x54,0x68,0x75, 0x46,0x72,0x69,0x53,0x61,0x74,0x53,0x75,0x6e};
uchar welcome_screen1[10]=" Welcome"; //开机画面 uchar welcome_screen2[14]="by wei and zao";
uchar numbuf1[4]; //定义一个数组用于存储温度各个位 uchar sec[7]; //用于存储1302中读出的时间 uchar week_buf; //用于存储周 uchar clockring[2]; //用于闹钟存时 uchar ringdebug; //闹钟闹铃判断 uchar ringbiao; //用于显示有闹钟 uchar ringbegin; //闹钟开启判断 //////////////////////////////////////////////////////// // // // DS1820b温度传感器模块 // // // // // //////////////////////////////////////////////////////// void delay(uint count) //delay 延时子程序 { uint i; while(count) { i=200; while(i>0) i--; count--; } }
void rstds1820b(void) //发送初始化命令子程序 { uint i; DS=0; i=103; while(i>0)i--; DS=1; i=4; while(i>0)i--; }
bit readbit(void) //read a bit 读一位 { uint i; bit dat; DS=0;i++; //i++ for delay DS=1;i++;i++; dat=DS; i=8;while(i>0)i--; return (dat); }
uchar readbyte(void) //read a byte date 读一个字节 { uchar i,j,dat; dat=0; for(i=1;i<=8;i++) { j=readbit(); dat=(j<<7)|(dat>>1); //读出的数据最低位在最前面,这样刚好一个字节在DAT里 } return(dat); }
void writebyte(uchar dat) //write a byte to ds18b20 给温度传感器写一个字节 { uint i; uchar j; bit testb; for(j=1;j<=8;j++) { testb=dat&0x01; dat=dat>>1; if(testb) //write 1 { DS=0; i++;i++; DS=1; i=8;while(i>0)i--; } else { DS=0; //write 0 i=8;while(i>0)i--; DS=1; i++;i++; }
} }
void temchange(void) //DS18B20 begin change 发送温度转换命令 { rstds1820b(); delay(1); writebyte(0xcc); // address all drivers on bus writebyte(0x44); // initiates a single temperature conversion }
uint gettem() //得到温度值 { float tt; uchar a,b; uint temp; //定义一个变量用来表示温度 rstds1820b(); delay(1); writebyte(0xcc); writebyte(0xbe); a=readbyte(); b=readbyte(); temp=b; temp<<=8; //将实型数据变成整型数据 temp=temp|a; tt=temp*0.0625; temp=tt*10+0.5; return temp; }
void getnum() //得到温度的十、个、小数位 { uchar A; temchange(); numbuf1[0]=gettem()/100+0x30; A=gettem()%100; numbuf1[1]=A/10+0x30; numbuf1[2]=0x2e; numbuf1[3]=A%10+0x30; }
//////////////////////////////////////////////////////// // // // lcd1602显示模块 // // // // // ////////////////////////////////////////////////////////
//延时函数 void delay1(uint ms) //对于AT89S51来说大概延时为每单位4.5ms { int i; while(ms--) { for(i=0;i<250;i++) { _nop_(); _nop_(); _nop_(); _nop_(); } } }
void delay2(uint us) //每单位大约延时50us { while(us--) { _nop_(); } }
//判断是否准备好 uchar lcd_ready() { uchar result; lcd_rs=0; lcd_rw=1; lcd_en=1; delay2(4); result=P0&0x80; //读寄存器BF位看是否忙 lcd_en=0; return result; }
//写指令代码 void lcd_inputcode(uchar cmd) { while(lcd_ready()); lcd_en=0; lcd_rs=0; lcd_rw=0; delay2(2); P0=cmd; delay2(4); lcd_en=1; //EN下降沿写指令代码 delay2(4); lcd_en=0; }
//写数据代码 void lcd_inputdata(uchar dat) { while(lcd_ready()); lcd_en=0; lcd_rs=1; lcd_rw=0; P0=dat; delay2(4); lcd_en=1; delay2(4); lcd_en=0; }
//设定显示位置 void lcd_pos(uchar XPOS,uchar YPOS) { XPOS&=0x0f; YPOS&=0x03; if(YPOS==0x00) lcd_inputcode(XPOS|0x80); else if(YPOS==0x01) lcd_inputcode((XPOS+0x40)|0x80); }
//在指定位置显示一个字符 void displayonechar(uchar X, uchar Y, uchar dat) { lcd_pos(X,Y); //根据参数定位 lcd_inputdata(dat); //写入字符数据 }
//显示字符串 void displaychar(uchar dat[],line) { uchar i; while(dat!='\0') { displayonechar(i,line,dat); i++; } i=0; } //初始化程序 void lcd_init() { delay1(20); lcd_inputcode(0x38); //工作方式设置 delay1(5); lcd_inputcode(0x38); delay1(5);
lcd_inputcode(0x0c); //显示光标及闪烁设置 delay1(5); lcd_inputcode(0x06); delay1(5); lcd_inputcode(0x01); //清DDRAM和AC值 delay1(5); }
//清屏程序 void lcd_clr() { lcd_inputcode(0x01); delay1(5); }
//闪动程序 void flash() { delay1(150); lcd_inputcode(0x08); delay1(150); lcd_inputcode(0x0c); delay1(150); lcd_inputcode(0x08); delay1(150); lcd_inputcode(0x0c); delay1(150); }
//温度获取 void get1820() { uchar i; getnum(); i=0; while(i!=4) { displayonechar(9+i,1,numbuf1); i++; } displayonechar(13,1,0xdf); displayonechar(14,1,0x43); i=0; }
//开机显示画面 void dispaly_begin() { uchar i,j; lcd_clr(); lcd_inputcode(0x80|0x11); //DDRAM地址设置两行显示 for(i=0;i<10;i++) lcd_inputdata(welcome_screen1); lcd_inputcode(0x80|0x51); for(i=0;i<14;i++) lcd_inputdata(welcome_screen2); for(j=0;j<16;j++) { lcd_inputcode(0x18); delay1(50); } flash(); } //////////////////////////////////////////////////////// // // // 时钟芯片1302显示模块 // // // // // //////////////////////////////////////////////////////// void DS1302InputByte(uchar d) //实时时钟写入一字节(内部函数) { unsigned char i; ACC = d; for(i=8; i>0; i--) { T_IO = ACC0; //相当于汇编中的 RRC T_CLK = 1; T_CLK = 0; ACC = ACC >> 1; } }
uchar DS1302OutputByte(void) //实时时钟读取一字节(内部函数) { uchar i; for(i=8; i>0; i--) { ACC = ACC >>1; //相当于汇编中的 RRC ACC7 = T_IO; T_CLK = 1; T_CLK = 0; } return(ACC); }
//先写地址,后写命令数据 void W1302(uchar ucAddr,uchar ucDa) //ucAddr: DS1302地址, ucData: 要写的数据 { T_RST = 0; T_CLK = 0; T_RST = 1; DS1302InputByte(ucAddr); // 地址,命令 DS1302InputByte(ucDa); // 写1Byte数据 T_CLK = 1; T_RST = 0; }
//先写地址,后后读命令数据 uchar R1302(uchar ucAddr) //读取DS1302某地址的数据 { uchar ucData; T_RST = 0; T_CLK = 0; T_RST = 1; DS1302InputByte(ucAddr); // 地址,命令 ucData = DS1302OutputByte(); // 读1Byte数据 T_CLK = 1; T_RST = 0; return(ucData); }
//读全部1302模块 void read_1302() { uchar i; uchar uccode; uccode=0x8d; for(i=0;i<7;i++) { sec=R1302(uccode); uccode=uccode-2; } }
//写单个位1302模块 void write1302_bit(uchar ucAddr,uchar num) { W1302(0x8e,0x00); W1302(ucAddr,num); W1302(0x8e,0x80); }
//显示单个时间,例如年,月等子程序 display1302_bit(uchar i,uchar address,uchar line) { displayonechar(address,line,((sec>>4)&0x0f)+0x30); displayonechar(address+1,line,(sec&0x0f)+0x30); }
//初始时间 void write1302() { uchar i1; uchar ucAddr = 0x80; W1302(0x8e,0x00); // 控制命令,WP=0,写操作 for(i1 =0; i1<7; i1++) { W1302(ucAddr,a[i1]); // 秒 分 时 日 月 星期 年 ucAddr +=2; } W1302(0x8e,0x80); // 控制命令,WP=1,写保护 }
//1302显示模块 void get1302() { read_1302(); displayonechar(0x01,0,0x32); displayonechar(0x02,0,0x30); display1302_bit(0,0x03,0); //显示年 displayonechar(0x05,0,0x2d); //分隔符 display1302_bit(2,0x06,0); //显示月 displayonechar(0x08,0,0x2d); //分隔符 display1302_bit(3,0x09,0); //显示日 week_buf=((sec[1]&0x0f)-1)*3; displayonechar(0x0c,0,week[week_buf]); //显示周 displayonechar(0x0d,0,week[week_buf+1]); displayonechar(0x0e,0,week[week_buf+2]);
display1302_bit(4,0x00,1); //显示时 displayonechar(0x02,1,0x3a); //分隔符 display1302_bit(5,0x03,1); //显示分 displayonechar(0x05,1,0x3a); //分隔符 display1302_bit(6,0x06,1); //显示秒
if(ringbiao==1) //判断时钟是否开启 { displayonechar(0x08,1,0xef); } }
void ds1302_init() { write1302_bit(0x90,0xa9); //开涓流充电 W1302(0x8e,0x80); //写保护 }
//////////////////////////////////////////////////////// // // // 按键调时模块 // // // // // //////////////////////////////////////////////////////// //延时模块,一个单位延时大概为1ms void delayms(uint z) { uint x,y; for(x=z;x>0;x--) for(y=122;y>0;y--); }
//每一位调时模块 void changetime_bit0(uchar i,uchar code_change,uchar address_change,uchar line) { sec=sec+1; write1302_bit(code_change,sec); if((sec&0x0f)>9)sec=sec&0xf0; displayonechar(address_change,line,sec%16+0x30); lcd_inputcode(0x80+0x40*line+address_change);
}
void changetime_bit1(uchar i,uchar j,uchar code_change,uchar address_change,uchar line) { sec=sec+0x10; write1302_bit(code_change,sec); if((sec>>4)>j)sec=sec&0x0f; displayonechar(address_change,line,sec/16+0x30);; lcd_inputcode(0x80+0x40*line+address_change); }
//调时模块 void changetime() //调时钟时间 { uchar i; //保证第一次在个位分 if(sw_add==0) { delayms(15); if(sw_add==0) //加按键判定 { while(!sw_add); //松手判定 delayms(15); while(!sw_add); //松手判定 if(i==0) //个位秒的修改 changetime_bit0(6,0x80,0x07,1); else if(i==1) //十位秒的修改 changetime_bit1(6,5,0x80,0x06,1); else if(i==2) //个位分的修改 changetime_bit0(5,0x82,0x04,1); else if(i==3) //十位分的修改 changetime_bit1(5,5,0x82,0x03,1); else if(i==4) //个位小时修改 changetime_bit0(4,0x84,0x01,1); else if(i==5) //十位小时修改 changetime_bit1(4,2,0x84,0x00,1); else if(i==6) //周修改 { sec[3]=sec[3]+1; write1302_bit(0x8a,sec[3]); if(sec[3]>7)sec[3]=0x01; week_buf=(sec[3]%16-1)*3; displayonechar(0x0c,0,week[week_buf]); //显示周 displayonechar(0x0d,0,week[week_buf+1]); displayonechar(0x0e,0,week[week_buf+2]); lcd_inputcode(0x80+0x0c); } else if(i==7) //日个位修改 changetime_bit0(2,0x86,0x0a,0); else if(i==8) //日十位修改 changetime_bit1(2,3,0x86,0x09,0); else if(i==9) //月个位修改 changetime_bit0(1,0x88,0x07,0); else if(i==10) //月十位修改 changetime_bit1(1,1,0x88,0x06,0); else if(i==11) //年个位修改 changetime_bit0(0,0x8c,0x04,0); else //年十位修改 changetime_bit1(0,9,0x8c,0x03,0); } } if(sw_move==0) //移动按键判定 { delayms(20); if(sw_move==0) { while(!sw_move); //松手判定 delayms(20); while(!sw_move); //松手判定 if(i<12) i++; else i=0; } if(i==0) lcd_inputcode(0x80+0x40+7); else if(i==1) lcd_inputcode(0x80+0x40+6); else if(i==2) lcd_inputcode(0x80+0x40+4); else if(i==3) lcd_inputcode(0x80+0x40+3); else if(i==4) lcd_inputcode(0x80+0x40+1); else if(i==5) lcd_inputcode(0x80+0x40); else if(i==6) lcd_inputcode(0x80+0x0c); else if(i==7) lcd_inputcode(0x80+0x0a); else if(i==8) lcd_inputcode(0x80+0x09); else if(i==9) lcd_inputcode(0x80+0x07); else if(i==10) lcd_inputcode(0x80+0x06); else if(i==11) lcd_inputcode(0x80+0x04); else lcd_inputcode(0x80+0x03); } }
void changering_bit0(uchar i,uchar address_ring) { clockring=clockring+1; if((clockring&0x0f)>9)clockring=clockring&0xf0; displayonechar(address_ring,1,clockring%16+0x30); lcd_inputcode(0x80+0x40+address_ring); }
void changering_bit1(uchar i,uchar j,uchar address_ring) { clockring=clockring+0x10; if((clockring>>4)>j)clockring=clockring&0x0f; displayonechar(address_ring,1,clockring/16+0x30); lcd_inputcode(0x80+0x40+address_ring); }
void changering() //调闹钟时间 { uchar i; if(sw_add==0) { delayms(15); if(sw_add==0) //按键判定 { while(!sw_add); //松手判定 delayms(15); while(!sw_add); //松手判定 ringbiao=1; ringbegin=1; if(i==0) //个位分的修改 changering_bit0(0,0x0a); else if(i==1) //十位分的修改 changering_bit1(0,5,0x09); else if(i==2) //个位小时修改 changering_bit0(1,0x07); else if(i==3) //十位小时修改 changering_bit1(1,2,0x06); } } if(sw_move==0) //按键判定 { delayms(20); if(sw_move==0) { while(!sw_move); //松手判定 delayms(20); while(!sw_move); //松手判定 if(i<3) i++; else i=0; } if(i==0) lcd_inputcode(0x80+0x40+0x0a); //光标移动 else if(i==1) lcd_inputcode(0x80+0x40+9); else if(i==2) lcd_inputcode(0x80+0x40+7); else if(i==3) lcd_inputcode(0x80+0x40+6); }
}
void change1302_bit(uchar address_1302,uchar line,uchar i) { displayonechar(address_1302,line,sec/16+0x30); displayonechar(address_1302+1,line,sec%16+0x30); }
void display1302() //对当前sec数值进行提取显示 { lcd_clr(); displayonechar(0x01,0,0x32); displayonechar(0x02,0,0x30); change1302_bit(0x03,0,0); //显示年
displayonechar(0x05,0,0x2d); //分隔符 change1302_bit(0x06,0,1); //显示月 displayonechar(0x08,0,0x2d); //分隔符 change1302_bit(0x09,0,2); week_buf=(sec[3]%16-1)*3; displayonechar(0x0c,0,week[week_buf]); //显示周 displayonechar(0x0d,0,week[week_buf+1]); displayonechar(0x0e,0,week[week_buf+2]);
change1302_bit(0x00,1,4); //显示时
displayonechar(0x02,1,0x3a); //分隔符 change1302_bit(0x03,1,5); //显示分
displayonechar(0x05,1,0x3a); //分隔符 change1302_bit(0x06,1,6); //显示秒 }
//不同屏幕的显示函数 void screen0() { lcd_clr(); display1302(); get1820(); } void screen1() { lcd_clr(); displaychar(" made in fzu ",0); } void screen2() { lcd_clr(); ringdebug=0; displaychar(" bell ring ",0); displayonechar(0x06,1,clockring[1]/16+0x30); //显示时 displayonechar(0x07,1,clockring[1]%16+0x30);
displayonechar(0x08,1,0x3a); //分隔符 displayonechar(0x09,1,clockring[0]/16+0x30); //显示分 displayonechar(0x0a,1,clockring[0]%16+0x30); }
//闹时界面 void displaybellring() { lcd_clr(); ringbiao=0; displaychar(" it's the time ",0); displaychar("press sw to stop",1); } //换屏幕显示 void changescreen() { uchar i; if(sw_changeweb==0) { delayms(5); if(sw_changeweb==0) { if(i<2) i++; else i=0; } if(i==1) screen1(); else if(i==2) screen2(); else screen0(); } while(sw_changeweb!=1); if(i==0) changetime(); if(i==2) changering(); }
//对时钟进行调时 void changeclock() interrupt 2 { lcd_inputcode(0x0f); //开光标 lcd_inputcode(0x80|0x4f); //将光标移位至最后一位 while(sw_back!=0) { changescreen(); } lcd_inputcode(0x0c); //关光标 }
//闹钟判断 void bellring() { if(clockring[0]==sec[5]&&clockring[1]==sec[4]&&ringdebug==0&&ringbegin==1) { lcd_clr(); displaybellring(); ring_bell=0; while(sw_back!=0); ring_bell=1; lcd_clr(); ringdebug++; } }
//按键初始化模块 void sw_init() { IT1=0; //使得外部中断的触发方式为低电平触发; EX1=1; //允许外部的中断0的中断请求 PX1=1; //设置外部中断为高优先级 //开中断终端设置 EA=1; //开中断总允许位 }
void main() { lcd_init(); lcd_clr(); sw_init(); dispaly_begin(); ds1302_init(); write1302(); lcd_clr(); while(1) { get1820(); get1302(); bellring(); delayms(500); } //flash(); }
|
|