打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
51单片机液晶显示(含lcd12864和lcd1602)时钟_liutao2588的空间_...
【转】 51单片机液晶显示(含lcd12864和lcd1602)时钟
2010-06-05 01:05
转载自 nathen_zhang
最终编辑 nathen_zhang

/*
51mcu液晶时钟(ZYB-MCU51-LCD-A0)
通过一个跳线进行1602和12864之单进行切换
本单片机具有完整的时间初始化和时间日期调校功能。
设计制作:Nathen.zhang@gmail.com

lcd12864部分还未完成,仅能显示欢迎信息。
*/

#include<reg52.h>
#include <math.h>
#include <intrins.h>   //使用 _nop_()将会用到此头文件
#define uchar unsigned char
#define uint unsigned int
#define uchar unsigned char
#define uint unsigned int

//跳线设置    1-2 则key=1(lcd12864)   2-3则key=0(lcd1602);
sbit key=P0^4;


//ds1302寄存器宏定义
#define WRITE_SECOND 0x80
#define WRITE_MINUTE 0x82
#define WRITE_HOUR   0x84
#define WRITE_DATE 0x86
#define WRITE_MONTH 0x88
#define WRITE_DAY   0x8A
#define WRITE_YEAR   0x8C
#define READ_SECOND   0x81
#define READ_MINUTE 0x83
#define READ_HOUR    0x85
#define READ_DATE   0x87
#define READ_MONTH 0x89
#define READ_DAY    0x8B
#define READ_YEAR   0x8D
#define WRITE_PROTECT 0x8E
#define uchar unsigned char
#define L1 0x80    // 液晶显示第一行起始地址 然后再加上所有位序号(从0开始)
#define L2 0xc0    //液晶显示第二行起始地址 0x80+0x40
#define DATA P3
#define pos_year   L1+0
#define pos_month L1+3
#define pos_date   L1+6
#define pos_day    L1+9
#define pos_hour   L2+0
#define pos_minute L2+3
#define pos_sec L2+6

//位寻址寄存器定义
sbit ACC_7 = ACC^7;

//管脚定义
sbit sclk=P0^2; // DS1302时钟信号 7脚
sbit clk_io=P0^1;    // DS1302数据信号   6脚
sbit clk_rst=P0^0;   // DS1302片选   5脚

sbit RS1602=P1^5;
sbit RW1602=P1^6;
sbit E1602=P1^7;


//功能按键定义
sbit func_key=P2^2;
sbit inc_key=P2^1;
sbit dec_key=P2^0;

//地址、数据发送子程序
void delay(uchar);
void write1602_comd(uchar);
void write1602_data(uchar);
void init1602_lcd();
void keyscan();
void Init_DS1302();   
void read_real_time();
uchar i,j,flag=0;
uchar code table1[]=" - - / ";
uchar code table2[]=" : : ";

//LCD12864相关函数
//数据线使用P3
#define DATA    P3  
//控制线使用P1
#define CONTROL P1
#define RS12864   0     //并行的指令数据选择信号
#define RW12864   1     //并行的读写信号     
#define E12864    2     //并行的使能信号  
#define PSB12864 3     //并/串行接口选择        12864 LCD分为字符型和点阵型,字符型即内带中文字库的有PSB串并选择引脚。
#define RST12864 5     //复位 低电平有效     

#define SETB(x,y) (x|=(1<<y))
#define CLRB(x,y) (x&=(~(1<<y)))
#define CHKB(x,y) (x&(1<<y))
void lcd12864_init(void);
void clr_lcd12864(void);
void send12864_com(unsigned char cmd);
void send12864_data(unsigned char dat);
void chek12864_busy(void);
void set12864_xy(unsigned char xpos,unsigned char ypos);
void print12864(unsigned char x,unsigned char y,char* str);
void print12864str(unsigned char xpos,unsigned char ypos,unsigned char str[],unsigned char k);
unsigned char code buf[4] ={0xbb,0xb6,0xd3,0xad};//欢迎


/********************测忙碌**********************/
//测忙碌子程序
//RS=0,RW=1,E=H,D0-D7=状态字
/************************************************/
void chek12864_busy(void)
{ unsigned char temp1;//状态信息(判断是否忙)
   CLRB(CONTROL,RS12864);    // RS = 0;
   SETB(CONTROL,RW12864);    // RW = 1;
   SETB(CONTROL,E12864);    // E = 1;
   do{temp1 = DATA;DATA=0xFF;} while(temp1&0x80);
   SETB(CONTROL,E12864);   // E = 1;
   DATA=0xFF;
}
/********************写命令**********************/
//写命令子程序
//
/************************************************/
void send12864_com(unsigned char cmd)/*写命令*/
{
    chek12864_busy();
    CLRB(CONTROL,RS12864); //RS = 0;
    CLRB(CONTROL,RW12864); //RW = 0;
    DATA = cmd;
    SETB(CONTROL,E12864); //E = 1;
    CLRB(CONTROL,E12864); //E = 0;
}
/********************写数据**********************/
//写数据子程序
//
/************************************************/
void send12864_data(unsigned char dat)
{
chek12864_busy();
SETB(CONTROL,RS12864); //RS = 1;
CLRB(CONTROL,RW12864); //RW = 0;
DATA = dat;
SETB(CONTROL,E12864); //E = 1;
CLRB(CONTROL,E12864); //E = 0;
}
/********************初始化**********************/
//复位、通讯方式选择
/************************************************/
void lcd12864_init(void)
{
    SETB(CONTROL,RST12864);   //复位RST=1
    SETB(CONTROL,PSB12864);   //通讯方式为并口PSB = 1
          //send12864_com(0x34);//34H--扩充指令操作
    send12864_com(0x30); //功能设置,一次送8位数据,基本指令集
    send12864_com(0x0C); //0000,1100 整体显示,游标off,游标位置off
    send12864_com(0x01); //0000,0001 清DDRAM
    send12864_com(0x02); //0000,0010 DDRAM地址归位
    send12864_com(0x80); //1000,0000 设定DDRAM 7位地址000,0000到地址计数器AC
}

/*******************************************************************/
//          设置显示位置    xpos(1~16),tpos(1~4)
/*******************************************************************/
void set12864_xy(unsigned char xpos,unsigned char ypos)
{
switch(ypos)
{
   case 1:
    send12864_com(0X80|xpos);break;
   case 2:
    send12864_com(0X90|xpos);break;
   case 3:
    send12864_com(0X88|xpos);break;
   case 4:
    send12864_com(0X98|xpos);break;
   default:break;
}
}
/*******************************************************************/
//          在指定位置显示字符串
/*******************************************************************/
void print12864(unsigned char x,unsigned char y,char* str)
{
    unsigned char lcd_temp;
    set12864_xy(x,y);
    lcd_temp=*str;
    while(lcd_temp != 0x00)
    {
        send12864_data(lcd_temp);
        lcd_temp=*(++str);
    }
}

/********************写字符串******************/
//写字符串子程序
//xpos1取0~7共八列,ypos1取0~3共四行。
/**********************************************/
void print12864str(unsigned char xpos,unsigned char ypos,unsigned char str[],unsigned char k)
{
unsigned char n;
     switch (ypos)
    { case 1: xpos |= 0x80;break;         //第一行
      case 2: xpos |= 0x90;break;         //第二行
      case 3: xpos |= 0x88;break;         //第三行
      case 4: xpos |= 0x98;break;         //第四行
      default: break;
     }
      send12864_com(xpos);                                    //此处的Xpos已转换为LCM的显示寄存器实际地址
      for(n=0;n < k;n++)
     {
      send12864_data(str[n]);//显示汉字时注意码值,连续两个码表示一个汉字
      }
}

/********************清屏************************/
//清屏
/************************************************/
void clr_lcd1286412864(void)
{
   send12864_com(0x01);
}
/********以下为液晶1602子程序*********************/
void init1602_lcd()
{
write1602_comd(0x01);
write1602_comd(0x0c);
write1602_comd(0x06);
    write1602_comd(0X3C); // 功能设置 :2行显示模式,数据为8位。还有字体的设置。
write1602_comd(0x80);    //设置第一行地址指针
for(i=0;i<10;i++)   //初始化显示,用于显示格式符
    {
         write1602_data(table1[i]);
    delay(20);
   }
write1602_comd(0Xc0); // 设置第二行地址指针
for(j=0;j<8;j++)
    {
         write1602_data(table2[j]);
    delay(20);
        }
i=0;j=0;   
}

void delay(uchar z)
{
uchar i,j;
for(j=z;j>0;j--)  
for(i=125;i>0;i--);
}

void write1602_comd(uchar com)
{
RS1602=0;
DATA=com;
RW1602=0;
delay(5);
E1602=1;
delay(5);
E1602=0;
}
void write1602_data(uchar date)
{
RS1602=1;
DATA=date;
RW1602=0;
delay(5);
E1602=1;
    delay(5);
E1602=0;
}
/********以上为液晶子程序******************/

/********以下为DS1302子程序*******************************/


void writeds1602_byte(uchar dat)   
{
uchar i,dat_w;
dat_w=dat;        //数据送给变量dat_w
for(i=0;i<8;i++)
{
    clk_io=0;            //数据线先拉低
    if(dat_w&0x01)    //如果是1
    {clk_io=1;}
    else              //如果是0
    {clk_io=0;}
    dat_w=dat_w>>1; //右移一位
    sclk=0;          //先拉低时钟信号
    sclk=1;          //拉高时钟信号
}   
sclk=0;            //写一字节完毕,拉低时钟信号
}

//-------------------------------
//---- 从DS1302读一字节 (下降沿有效) --------
//---- 读出数据时从低位0位到高位7 -----------
//-------------------------------

uchar readds1602_byte()
{
uchar i,k;
k=0;
for(i=0;i<8;i++)
{
k=k>>1;
if(clk_io)
{k=k|0x80;}
sclk=1;          //先拉低时钟信号
sclk=0;
}
sclk=0;
return (k);
}

//-------------------------------
//---- 指定地址写一数据 --------
//-------------------------------

void Write1602(uchar add,uchar dat) //dat为十进制数
{
clk_rst=0;
sclk=0;
clk_rst=1;                              //拉高RST
writeds1602_byte(add);                  // 写地址
writeds1602_byte((dat/10<<4)|(dat%10)); // DS1302中的时间数据是BCD码形式
sclk=0;
clk_rst=0;
}

//-------------------------------
//---- 指定地址读出一数据 ------
//-------------------------------

uchar Read1302(uchar add)
{
uchar temp,dat1,dat2;
clk_rst=0;
sclk=0;
clk_rst=1;
writeds1602_byte(add);
temp=readds1602_byte(); //读取的数据为16进制
sclk=0;              //读取完毕,拉低SCLK
clk_rst=0;              //拉低SCLK

dat1=temp/16;        //16进制转成BCD
dat2=temp%16;
temp=dat1*10+dat2; //转换成10进制数字
return (temp);   

}

 

//初始化DS1302
void Init_DS1302(void)   
{     //设置初始化时间
       Write1602 (WRITE_PROTECT,0X00); delay(1); //禁止写保护
     Write1602 (WRITE_SECOND,0x00); delay(1);   //秒位初始化
       Write1602 (WRITE_MINUTE,0x00);delay(1); //分钟初始化
       Write1602 (WRITE_HOUR,0x0C); delay(1); //小时初始化
    Write1602 (WRITE_DAY,0x01); delay(1);   //星期初始化
       Write1602 (WRITE_DATE,0x12);delay(1); //日初始化
       Write1602 (WRITE_MONTH,0x05); delay(1); //月初始化
    Write1602 (WRITE_YEAR,0x09); delay(1); //年初始化
       Write1602 (WRITE_PROTECT,0x80); delay(1);//允许写保护
}

/*************** 以下为:读写年、月、日、时、分、秒的子程序*******************/
/*不包括星期在内
功能:从DS1302中读出时间和日期,并在1602液晶上显示出来
三个参数分别为:1.读时间日期存储器地址 2.写入的十位光标地址 3.个位光标地址 */
void read_write_date_time(uchar com_addr, cusr_addr)
{
uchar hour_wei,ge_wei,call_data;
   Write1602 (WRITE_PROTECT,0x00);delay(1); //禁止写保护
   call_data=Read1302(com_addr); //秒
   hour_wei=call_data/10;

   ge_wei=call_data%10;
   write1602_comd(cusr_addr);// 设置第二行地址指针
   write1602_data(hour_wei+0X30);
   write1602_data(ge_wei+0X30);
   Write1602 (WRITE_PROTECT,0x80); delay(1); //允许写保护
}

void write_lcd(uchar cusr_addr,time_data )
{
uchar hour_wei,ge_wei;
   hour_wei=time_data/10;
   ge_wei=time_data%10;
   write1602_comd(cusr_addr);// 设置第二行地址指针
   write1602_data(hour_wei+0X30);
   write1602_data(ge_wei+0X30);
}
void read_real_time()
{
uchar xq;
   read_write_date_time(READ_HOUR,L2); //小时
   read_write_date_time(READ_MINUTE,L2+3);
   read_write_date_time(READ_SECOND,L2+6);
   read_write_date_time(READ_YEAR,L1);
   read_write_date_time(READ_MONTH,L1+3);
   read_write_date_time(READ_DATE,L1+6); //日期

   xq=Read1302(READ_DAY); //星期
   write1602_comd(L1+9);
   write1602_data(xq+0X30);
   Write1602 (WRITE_PROTECT,0x80); //允许写保护
}


//键盘处理

uchar first=1,num,second,minute,hour,day,date,month,year;

void keyscan()
{
if(first==1) //读取当前值,用于调整的基准
{
second=Read1302(READ_SECOND);
minute=Read1302(READ_MINUTE);
hour=Read1302(READ_HOUR);
day=Read1302(READ_DAY);
date=Read1302(READ_DATE);
month=Read1302(READ_MONTH);
year=Read1302(READ_YEAR);
   }
   first=0;
if(func_key==0)
{
    delay(5);
    if(func_key==0)
     {
     num++;
     flag=1;
     while(!func_key);
     if(num==1)
     {
      clk_rst=0; //停止时钟转换
      write1602_comd(pos_sec); //调整秒 将光标移动到秒位置
      write1602_comd(0x0f); //光标闪烁
     }
         }
    if(num==2)
    {
     write1602_comd(pos_minute); //将光标移动到分钟
    }
    if(num==3)
    {
     write1602_comd(pos_hour); //将光标移动到小时
    }
    if(num==4)
    {
     write1602_comd(pos_day); //将光标移动到星期
    }
    if(num==5)
    {
     write1602_comd(pos_date); //将光标移动到日
    }
    if(num==6)
    {
     write1602_comd(pos_month); //将光标移动到月
    }
    if(num==7)
    {
     write1602_comd(pos_year); //将光标移动到年
    }
    if(num==8)       //保存调整后的数据
    {
     num=0;
     write1602_comd(0x0c);   //光标停止闪烁
     flag=0;
     clk_rst=1; //开始时钟转换
    
     Write1602 (WRITE_PROTECT,0x00);delay(1); //禁止写保护

     Write1602(WRITE_SECOND,second);delay(1);    //秒位初始化 ,写入DS1302
                Write1602(WRITE_MINUTE,minute); delay(1); //分钟初始化
           Write1602(WRITE_HOUR,hour); delay(1); //小时初始化
           Write1602(WRITE_DAY,day); delay(1); //星期初始化
           Write1602(WRITE_DATE,date); delay(1); //日初始化
           Write1602(WRITE_MONTH,month); delay(1); //月时初始化
           Write1602(WRITE_YEAR,year); delay(1); //年时初始化
     Write1602 (WRITE_PROTECT,0x80); delay(1); //允许写保护

     first=1;      //用于下次调整时重新读当前值

       }    
   }
   if(num!=0)
   {
    if(inc_key==0)
    {
     delay(1);
     if(inc_key==0)
     {
      while(!inc_key);
      if(num==1)       //秒
      {
       second++;
       if(second==60)
        second=0;
       write_lcd(pos_sec,second);//秒位初始化,写入液晶显示
       write1602_comd(pos_sec); //置光标
      
      }
      if(num==2)     //分
      {
       minute++;
       if(minute==60)
        minute=0;
       write_lcd(pos_minute,minute);
       write1602_comd(pos_minute); //置光标

      }
      if(num==3)     //时
      {
       hour++;
       if(hour==24)
        hour=0;
       write_lcd(pos_hour,hour);
         write1602_comd(pos_hour); //置光标

      }
      if(num==4)     //星期
      {
       day++;
       if(day==8)
        day=1;
         write1602_comd(pos_day); //置光标
                  write1602_data(day+0X30);

      }
      if(num==5)     //日
      {
       date++;
       if(date==32)
        date=1;
       write_lcd(pos_date,date);
         write1602_comd(pos_date); //置光标

      }
      if(num==6)     //月
      {
       month++;
       if(month==13)
        month=1;
       write_lcd(pos_month,month);
         write1602_comd(pos_month); //置光标

      }
      if(num==7)     //年
      {
       year++;
       if(year==99)
        hour=0;
       write_lcd(pos_year,year);
         write1602_comd(pos_year); //置光标

      }
     }
    }
    if(dec_key==0)
    {
     delay(1);
     if(dec_key==0)
     {
      while(!dec_key);
      if(num==1)       //秒
      {
       second--;
       if(second==-1)
        second=59;
       write_lcd(pos_sec,second);//秒位初始化,写入液晶显示
       write1602_comd(pos_sec); //置光标
      
      }
      if(num==2)     //分
      {
       minute--;
       if(minute==-1)
        minute=59;
       write_lcd(pos_minute,minute);
       write1602_comd(pos_minute); //置光标

      }
      if(num==3)     //时
      {
       hour--;
       if(hour==-1)
        hour=23;
       write_lcd(pos_hour,hour);
         write1602_comd(pos_hour); //置光标

      }
      if(num==4)     //星期
      {
       day--;
       if(day==0)
        day=7;
         write1602_comd(pos_day); //置光标
                  write1602_data(day+0X30);

      }
      if(num==5)     //日
      {
       date--;
       if(date==0)
        date=31;
       write_lcd(pos_date,date);
         write1602_comd(pos_date); //置光标

      }
      if(num==6)     //月
      {
       month--;
       if(month==0)
        month=12;
       write_lcd(pos_month,month);
         write1602_comd(pos_month); //置光标

      }
      if(num==7)     //年
      {
       year--;
       if(year==0)
        hour=15;
       write_lcd(pos_year,year);
         write1602_comd(pos_year); //置光标

      }
     }
    }   
   }
}

void main()
{
int j=0;
uchar temp;
uint mode=0;     // 1-2 则key=1(lcd12864)   2-3则key=0(lcd1602);

if (key==1) mode=1;

switch(mode)
{
case 1:      //液晶12864工作方式(完善中)
       lcd12864_init();    
       print12864str(1,1,buf,4);
       print12864(5,1,"光临");
       print12864(0,3," NATHEN_ZHANG");
     while(1);
       break;
case 0:     //液晶1602工作方式
    if (Read1302(READ_SECOND)>59) Init_DS1302();   //如果时间已停止则设初值,通过检测CH寄存们进行确定,这点很关键。
    init1602_lcd();
while(1)
{
     keyscan();
   delay(10);
   if(flag==0)
     read_real_time();
}       
break;
   }

                    
}


本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
51单片机 tm1620驱动3位数码管程序
基于51单片机SHT11温湿度传感器检测程序(含电路图)
1602显示程序
st7920
STM8S103F
74hc164驱动共阴数码管与共阳数码管
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服