//例K2.2 //该程序采用总线方式访问1602A,在其屏幕上显示动态变化的日期/星期/时钟数据 //1602A的硬件连接方式如图13.3所示。3个控制信号的连接如下: //RS=1/0--选择数据口/命令状态口(连接P1.7)。 //RW=1/0--选择读操作/写操作(连接P1.5)。 //E=1/0--选中1602A/否(连接74HC00构成的译码电路)。 //时间数据同时在1602A/数码管/发光二极管等3个装置上显示。 //1602A显示两行数据: //第1行为日期和星期(3字母英文缩写),格式"yyyy-mm-dd www";第2行为时钟,格式为"hh:mm:ss" //6个数码管同时显示时钟,格式为"hh.mm.ss" //16个LED显示分/秒数据(压缩BCD码),格式为"mmmm mmmm ssss ssss" //注意程序中对1302中时间数据的访问使用了两种方法: //方法1--只读秒数据,判断是否需要刷新显示。 //方法2--用突发方式读取全部7B的时间数据,7B从高址到低址排序依次是秒/分/时/日/月/星期/年。 //注意函数lcd1602_wr_str()中用通用指针ptr作函数参数,这样它既可访问ROM中的字符,又可访问RAM中的字符。 //进行1602A的总线访问和I/O访问,仅低层函数不同,其高层函数相同。 //Program Size: data=37.1 xdata=0 code=863 #include #include #include #define uchar unsigned char #define uint unsigned int #define LCD1602 XBYTE[0xc000] //1602所有读写(数据/命令/状态)操作的口地址 #define RST_1302 A15=0;A14=A13=1; //复位1302 #define NRST_1302 A15=A14=A13=0; //取消1302复位 #define RS1602 P17 //连接1602的RS脚 #define RW1602 P15 //连接1602的RW脚 sbit P17=P1^7; //选择:H=选数据口,L=选命令/状态口 sbit P15=P1^5; //选择:H=读操作,L=写操作 sbit A13=P2^5; sbit A14=P2^6; sbit A15=P2^7; sbit DIO_1302=P1^1; //接DS1302的IO引脚 sbit CLK_1302=P1^2; //接DS1302的CLK引脚 sbit DAT_164=P3^4; //74HC164显示接口DAT sbit CLK_164=P3^5; //74HC164显示接口CLK char data lcd_buf[10]; //1602显示缓冲 char data disbuf[6]; //数码管显示缓冲 uchar code week[]="SunMonTueWedTuhFriSat"; //星期数据的3字符英文缩写 uchar code seg[]={ //七段码表 0x3f,0x06,0x5b,0x4f,0x66, //0~4 0x6d,0x7d,0x07,0x7f,0x6f, //5~9 0x77,0x7c,0x39,0x5e,0x79, //A~E 0x71,0x40,0x46,0x00,0x76, //F,-,-1,全灭,H 0xbf,0x86,0xdb,0xcf,0xe6, //0.~4. 0xed,0xfd,0x87,0xff,0xef}; //5.~9. uchar bdata dat; //为方便从字节数据中提取位数据 sbit dat7=dat^7; //要提取的位数据 //--------------------------------------------- void delay_10us(uchar n) //10微秒级延时 { do { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); }while(--n); } void delay_ms(uint n) //毫秒级延时 { do { delay_10us(131); }while(--n); } //---------------------------------------------- void sendchar(uchar ch) //向数码管传送1位数码 { uchar i; for(i=0;i<8;i++) { DAT_164=ch&0x01; CLK_164=0; CLK_164=1; ch>>=1; } } void display() //数码管显示6位数码 { uchar i; for(i=0;i<6;i++) sendchar(seg[disbuf[i]]); } //-------------------------------------------- void wr_byte1302(uchar dat) //写1字节到1302 { uchar i; for(i=8;i>0;i--) { DIO_1302=dat&0x01; CLK_1302=1; CLK_1302=0; dat>>=1; } } uchar rd_byte1302() //从1302读1字节 { uchar i; for(i=8;i>0;i--) { dat>>=1; dat7=DIO_1302; CLK_1302=1; CLK_1302=0; } return(dat); } void wr_addr_byte1302(uchar addr, uchar dat) //向1302指定地址写单字节数据 { RST_1302 //复位1302 CLK_1302=0; NRST_1302 wr_byte1302(addr); //写地址命令 wr_byte1302(dat); //写数据 CLK_1302=1; RST_1302 //复位1302 } uchar rd_addr_byte1302(uchar addr) //从指定地址读单字节数据 { unsigned char dat; RST_1302 //复位1302 CLK_1302=0; NRST_1302 wr_byte1302(addr); //写地址命令 dat=rd_byte1302(); //读1B数据 CLK_1302=1; RST_1302 //复位1302 return(dat); } void set_date1302(uchar bcdy,uchar bcdm,uchar bcdd) //设置年月日 { wr_addr_byte1302(0x8e,0x00); //去写保护 wr_addr_byte1302(0x8c,bcdy); //设置年 wr_addr_byte1302(0x88,bcdm); //设置月 wr_addr_byte1302(0x86,bcdd); //设置日 wr_addr_byte1302(0x8e,0x80); //加写保护 } void set_clk1302(uchar bcdh,uchar bcdm,uchar bcds) //设置时分秒 { wr_addr_byte1302(0x8e,0x00); //去写保护 wr_addr_byte1302(0x84,bcdh); //设置时 wr_addr_byte1302(0x82,bcdm); //设置分 wr_addr_byte1302(0x80,bcds); //设置秒 wr_addr_byte1302(0x8e,0x80); //加写保护 } void set_week1302(uchar bcdw) //设置星期 { wr_addr_byte1302(0x8e,0x00); //去写保护 wr_addr_byte1302(0x8a,bcdw); //设置星期 wr_addr_byte1302(0x8e,0x80); //加写保护 } void rd_time_burst1302(uchar *time) //突发方式读1302时间数据,time为存数据的指针 { uchar i; RST_1302 //复位1302 CLK_1302=0; NRST_1302 wr_byte1302(0xbf); //0xbf为突发读时钟命令 for(i=7;i>0;i--) { *time=rd_byte1302(); //读1字节数据 time++; } RST_1302 //复位1302 } void rd_sec1302(uchar *clk) //从1302读取秒数据 { clk[0]=rd_addr_byte1302(0x81); //读秒数据 } //-------------------------------------------- bit lcd1602_test() //读1602的状态,判断是否可对它进行读写操作 { RS1602=0; //选状态口 RW1602=1; //读操作 _nop_(); return LCD1602&0x80; //返回1602状态:0=就绪,1=不就绪 } void lcd1602_wr_cmd(uchar cmd,bit test) //写命令字到1602,test表示是否测试就绪 { if(test) lcd1602_test(); //若test=1,则须等LCM就绪后再进行操作 RS1602=0; //选命令口 RW1602=0; //写操作 _nop_(); LCD1602=cmd; //输出命令并使能1602 _nop_(); } void lcd1602_xy(char x,char y) { uchar ramaddr=y*0x40+x; //计算与x/y对应的RAM地址 lcd1602_wr_cmd(ramaddr|0x80,1); //显示定位(定位当前光标) } void lcd1602_wr_dat(char dat) //向当前RAM写数据,dat为显示字符的ASCII码 { while(lcd1602_test()); //等待LCM就绪再进行写操作 RS1602=1; //选数据口 RW1602=0; //写操作 _nop_(); LCD1602=dat; //输出数据(要显示字符的ASCII码) _nop_(); } void lcd1602_init() //按最常用的方式对1602进行初始化 { delay_ms(200); //延时等系统充分复位 lcd1602_wr_cmd(0x38,0); //初始化:显示模式16*2,5*7点阵,8位数据口 delay_ms(5); //厂方要求连续写3次初始化命令,写前不检测1602状态 lcd1602_wr_cmd(0x38,0); delay_ms(5); lcd1602_wr_cmd(0x38,0); lcd1602_wr_cmd(0x38,1); //显示模式:关显示,光标不闪烁,不显示 lcd1602_wr_cmd(0x01,1); //清屏(实际是清LCM中的RAM) lcd1602_wr_cmd(0x06,1); //光标移动模式:每次自动加1(最常用的模式) lcd1602_wr_cmd(0x0c,1); //开显示 } void lcd1602_wr_str(uchar *ptr,uchar n) //从当前位置起向1602写n个字符,ptr为通用字符指针 { uchar i; for(i=0;i>4&0x07); //时数据分解到两个显示缓冲区 lcd_buf[1]='0'+(disbuf[1]=tmp[2]&0x0f); lcd_buf[3]='0'+(disbuf[2]=tmp[1]>>4&0x07); //分数据分解到两个显示缓冲区 lcd_buf[4]='0'+(disbuf[3]=tmp[1]&0x0f); lcd_buf[6]='0'+(disbuf[4]=tmp[0]>>4&0x07); //秒数据分解到两个显示缓冲区 lcd_buf[7]='0'+(disbuf[5]=tmp[0]&0x0f); lcd_buf[2]=lcd_buf[5]=':'; //1602用':'分隔时/分/秒数据 for(i=1;i<6;i+=2) disbuf[i]+=20; //数码管用小数点分隔时/分/秒数据 display(); //数码管显示 lcd1602_xy(4,1); //1602显示定位 lcd1602_wr_str(lcd_buf,8); //在1602第2行上显示时钟 lcd_buf[2]='0'+(tmp[6]>>4&0x0f); //年数据分解到显示缓冲区 lcd_buf[3]='0'+(tmp[6]&0x0f); lcd_buf[5]='0'+(tmp[4]>>4&0x01); //月数据分解到显示缓冲区 lcd_buf[6]='0'+(tmp[4]&0x0f); lcd_buf[8]='0'+(tmp[3]>>4&0x02); //日数据分解到显示缓冲区 lcd_buf[9]='0'+(tmp[3]&0x0f); lcd_buf[0]='2';lcd_buf[1]='0'; //千年显示 lcd_buf[4]=lcd_buf[7]='-'; //1602用'-'分隔年/月/日数据 lcd1602_xy(1,0); //1602显示定位 lcd1602_wr_str(lcd_buf,10); //在1602第1行上显示日期 lcd1602_xy(12,0); //1602显示定位 lcd1602_wr_str(week+(tmp[5]&0x07)*3,3); //显示星期几 } delay_ms(100); //延时再读1302 } }