51单片机 万年历

这个小项目自己做了5天,小有成就感。。。感谢前辈们多多提意见。。

12864液晶、DS12C887时钟芯片、DS18B20温度传感器。。。

#define uchar unsigned char
#define uint unsigned int

uint temp;
float f_temp;

sbit Ds=P2^2;
sbit Dula=P2^6;
sbit Wela=P2^7;

sbit LcdCS=P3^5;        //寄存器选择输入 通过rs确定是写数据还是写命令
sbit LcdSID=P3^6;        //液晶读/写控制 因为不从液晶读取任何数据,所以rw一直为0
sbit LcdSCLK=P3^4;        //液晶使能控制 给en一个高脉冲将数据送入液晶控制器
sbit LcdPSB=P3^7;        //串并方式控制

sbit DSCS=P1^4;            //片选信号,低电平有效
sbit DSAS=P1^5;            //地址选通输入端 AS的上升沿将AD0~AD7上出现的地址信息锁存到DS12C887上,下降沿清除地址信息
sbit DSRW=P1^6;            //
sbit DSDS=P1^7;            //

uchar code table1[]={" CHPAVC 天若海愚"};
uchar code table3[]={"星期一"};
uchar code table4[]={"星期二"};
uchar code table5[]={"星期三"};
uchar code table6[]={"星期四"};
uchar code table7[]={"星期五"};
uchar code table8[]={"星期六"};
uchar code table9[]={"星期日"};
uchar code table10[]={"°C"};

uchar buff[4];    

char year,month,day,week,miao,fen,shi;
uchar nyrsfm[17];        //Write_nyrsfm函数中,方法二nyrsfm只需申明成nyrsfm[16]

 

/***************************************************************
文件名称:    wannianli
作者    :    天若海愚
版本号    :    V1.0
说明    :    
创建时间:    2013年9月17日13:30:38
修改记录:    无
备注    :    在12864液晶屏上显示从DS12C887采集到的年、月、日、时、分数据,并且利用DS18B20采集温度显示在12864上
***************************************************************/

#include<reg52.h>
#include<define.h>
#include<intrins.h>
#include<stdio.h>

/***********************************
函数名称:    Delay()
函数功能:    延时
入口参数:    z
备注    :
***********************************/
void Delay(uint z)
{
    uint x,y;
    for(x=z;x>0;x--)
        for(y=110;y>0;y--);
}

/**********************************
函数名称:    Ds_reset()
函数功能:    DS18B20复位,初始化函数
入口参数:    无
备注    :
**********************************/
void Ds_reset()
{
    uint i;
    Ds=1;        //书P348,时序图,先将数据线置高电平1
    _nop_();    //延时,尽可能短一点
    Ds=0;
    i=103;
    while(i>0)
        i--;    //当总线停留在低电平超过480us,总线上所以器件都将被复位,这里
                //延时约680us总线停留在低电平超过480μs,总线上的所有器件都将被复位
    _nop_();    //延时,尽可能短一点
    
    Ds=1;
    i=5;
    while(i>0)
        i--;    //释放总线后,如果初始化成功则在15~60us内产生一个由DDS18B20返回的低电平0,据
                //该状态可以确定它的存在,但是不能无限制地等
}

/**********************************
函数名称:    Ds_read_bit()
函数功能:    读一位数据函数
入口参数:    无
备注    :
**********************************/
bit Ds_read_bit()
{
    uint i;
    bit dat;
    
    Ds=0;        //单片机(微处理器)将总线拉低
    _nop_();    //读时隙起始于微处理器将总线拉低至少1us
    Ds=1;        //拉低总线后接着释放总线,让从机18b20能够接管总线,输出有效数据
    _nop_();
    _nop_();    //小延时一下,读取18b20上的数据 ,因为从ds18b20上输出的数据,在读"时间隙"下降沿出现15us内有效
    dat=Ds;           //主机读从机18b20输出的数据,这些数据在读时隙的下降沿出现15us内有效 
    i=10;
    while(i>0)
        i--;       //所有读"时间隙"必须60~120us,这里77us
    return(dat);
}


/**********************************
函数名称:    Ds_read_byte()
函数功能:    读一个字节数据函数
入口参数:    无
备注    :
**********************************/
uchar Ds_read_byte()
{
    uchar i,j,dat;
    dat=0;        //初值不能忘
    for(i=1;i<=8;i++)    //此句曾写成for(i=0;i<=8;i++),导致程序显示结果出错,细节。。。
    {
        j=Ds_read_bit();
        dat=(j<<7)|(dat>>1);    //读出的数据最低位在最前面,这样刚好一个字节在dat里
    }
    return(dat);
}

/**********************************
函数名称:    Ds_write_byte()
函数功能:    向DS18B20写一个字节函数
入口参数:    
备注    :
**********************************/
void Ds_write_byte(uchar dat)
{
    uchar i;
    uint j;
    bit testb;
    for(i=1;i<=8;i++)
    {
        testb=dat&0x01;
        dat=dat>>1;
        if(testb)
        {
            Ds=0;
            _nop_();
            _nop_();      //看时序图,至少延时1us,才产生写"时间隙"  
            Ds=1;         //写时间隙开始后的15μs内允许数据线拉到高电平
            j=8;
            while(j>0)
                j--;         //所有写时间隙必须最少持续60us
        }
        else
        {
            Ds=0;
            j=8;
            while(j>0)
                j--;         //主机要生成一个写0 时间隙,必须把数据线拉到低电平并保持至少60μs,
            Ds=1;
            _nop_();
            _nop_();        
        }
    }
}

/**********************************
函数名称:    Ds_change()
函数功能:    18b20开始获取温度并转换
入口参数:
备注    :
**********************************/
void Ds_change()
{
    Ds_reset();        //初始化
    Delay(1);
    Ds_write_byte(0xcc);    //跳过ROM.直接向18b20发温度 转换指令,适用于一个从机工作
    Ds_write_byte(0x44);    //写温度转换指令
}

/**********************************
函数名称:    Get_temp()
函数功能:    读取寄存器中存储的温度数
入口参数:    无
备注    :
**********************************/
uint Get_temp()
{
    uchar a,b;
    Ds_reset();
    Delay(1);
    Ds_write_byte(0xcc);        //写跳过读ROM指令
    Ds_write_byte(0xbe);        //读暂存器。读内部RAM中9字节的温度数据
    a=Ds_read_byte();     //读低8位
    b=Ds_read_byte();     //读高8位
    temp=b;
    temp<<=8;        //两个字节组合为一个字
    temp=temp|a;
    f_temp=temp*0.0625;     //得到真实十进制温度值,因为DS18B20可以精确到0.0625 度,所以读回数据的最低位代表的是0.0625 度。
    temp=f_temp*10+0.5;     //乘以10不是小数点后面只取1位,加0.5是四舍五入
    f_temp=f_temp+0.05;
    
    return temp;        //temp是整型
}

/**********************************
函数名称:    init_com()
函数功能:    串口初始化
入口参数:    无
备注    :
**********************************/
void init_com()
{
    TMOD=0x20;        //定时器1,方式2
    PCON=0x00;
    SCON=0x50;        //等价于REN=1;SM0=0;SM1=1;
    TH1=0xFD;
    TL1=0xFD;        //设置波特率 9600
    TR1=1;            //启动定时器1
}

/**********************************
函数名称:    comm()
函数功能:    串口发送数据
入口参数:    
备注    :
**********************************/
void comm(uchar *parr)
{
    do
    {
        SBUF=*parr++;
        while(!TI);        //发送完毕,TI由硬件置1
        TI=0;
    }while(*parr);        //保持循环直到字符为'\0'
}

/***********************************
函数名称:    Send_byte()
函数功能:    发送一个字节
入口参数:    bbyte
备注    :
***********************************/
void Send_byte(uchar bbyte)
{
    uchar i;
    for(i=0;i<8;i++)
    {
        LcdSID=bbyte&0x80;        //取出最高位
        LcdSCLK=1;
        LcdSCLK=0;        //sclk由低电平变为高电平的瞬间,液晶控制器将sid上数据读入或输出
        bbyte<<=1;        //左移1位
    }
}

/***********************************
函数名称:    Write_char()
函数功能:    写数据或写命令
入口参数:    start,ddata
备注    :
***********************************/
void Write_char(bit start,uchar ddata)
{
    uchar start_data,Hdata,Ldata;
    if(start==0)
    {
        start_data=0xf8;        //写指令
    }
    else
    {
        start_data=0xfa;        //写数据
    }
    
    Hdata=ddata&0xf0;        //取高4位
    Ldata=(ddata<<4)&0xf0;    //取低4位
    Send_byte(start_data);    //发送起始信号
    Delay(5);        //延时是必须的
    Send_byte(Hdata);        //发送高4位
    Delay(1);        //延时是必须的
    Send_byte(Ldata);
    Delay(1);
}

/***********************************
函数名称:    Lcd_pos()
函数功能:    设置显示位置
入口参数:    X,Y
备注    :
***********************************/
void Lcd_pos(uchar X,uchar Y)
{
    uchar pos;
    if(X==0)
    {
        X=0x80;
    }
    else if(X==1)
    {
        X=0x90;
    }
    else if(X==2)
    {
        X=0x88;
    }
    else if(X==3)
    {
        X=0x98;
    }
    pos=X+Y;
    Write_char(0,pos);
}

/***********************************
函数名称:    Lcd_init()
函数功能:    液晶初始化
入口参数:    无
备注    :
***********************************/
void Lcd_init()
{
    Delay(5);        //启动等待,等LCD进入工作状态
    LcdPSB=0;        //串口驱动
    Write_char(0,0x30);        //基本指令操作
    Delay(5);
    Write_char(0,0x0c);        //显示开,关光标,反白关
    Delay(5);
    Write_char(0,0x01);        //清屏,将DDRAM的地址计数器归零
    Delay(5);
}


/**********************************
函数名称:    Display_temp()
函数功能:    在12864上显示温度
入口参数:
备注    :    小数点后保留一位
**********************************/
void Display_temp()
{
    uchar i;
    Lcd_pos(2,4);
    Write_char(1,buff[0]);
    Write_char(1,buff[1]);
    Write_char(1,buff[2]);
    Write_char(1,buff[3]);        

    for(i=0;i<3;i++)
    {
        Write_char(1,table10[i]);    //显示符号°C 
        Delay(5);
    }
}

/*********以下是操作DS12C87时钟芯片***********/
/***********************************
函数名称:    Write_ds()
函数功能:    写12C8887函数
入口参数:    add,date
备注    :
***********************************/
void Write_ds(uchar add,uchar date)
{
    DSCS=0;
    DSAS=1;
    DSDS=1;
    DSRW=1;
    P0=add;
    DSAS=0;     //dsas的下降沿将AD0~~AD7上的地址信息锁存到DS12C887上
    DSRW=0;
    P0=date;
    DSAS=1;
    DSRW=1;        //INTER模式,dsrw的上升沿锁存数据
    DSCS=1;
}

/***********************************
函数名称:    Read_ds()
函数功能:    读12C8887函数
入口参数:    add
备注    :
***********************************/
uchar Read_ds(uchar add)
{
    uchar ds_date;
    DSAS=1;
    DSDS=1;
    DSRW=1;
    DSCS=0;
    P0=add;        //先写地址
    DSAS=0;
    DSDS=0;
    P0=0xff;
    ds_date=P0;        //再读数据
    DSDS=1;
    DSCS=1;
    DSAS=1;
    return ds_date;
}

/**********************************
函数名称:    Write_nyrsfm()
函数功能:    将年、月、日、时、分显示在12864液晶上
入口参数:
备注    :    不能通过下面的/*   中的函数来实现
**********************************/
void Write_nyrsfm(char year,char month,char day,char shi,char fen)
{
    //方法一:
    
    /*uchar i;
    Lcd_pos(1,0);
    
    nyrsfm[0]=0x32;
    nyrsfm[1]=0x30;
    nyrsfm[2]=year/10+0x30;
    nyrsfm[3]=year%10+0x30;
    nyrsfm[4]='-';
    nyrsfm[5]=month/10+0x30;
    nyrsfm[6]=month%10+0x30;
    nyrsfm[7]='-';
    nyrsfm[8]=day/10+0x30;
    nyrsfm[9]=day%10+0x30;
    nyrsfm[10]=' ';
    nyrsfm[11]=shi/10+0x30;
    nyrsfm[12]=shi%10+0x30;    
    nyrsfm[13]=':';
    nyrsfm[14]=fen/10+0x30;
    nyrsfm[15]=fen%10+0x30;
    nyrsfm[16]='\0';
    
    //while(nyrsfm[i]!='\0')        //while()循环不显示,是错误的
    //{
    //    Write_char(1,nyrsfm[i]);        
    //    i++;
    //}
    for(i=0;i<17;i++)
    {
        Write_char(1,nyrsfm[i]);
        Delay(5);
    }*/


    //方法二:
    
    Lcd_pos(1,0);
    
    nyrsfm[0]=0x32;
    Write_char(1,0x32);
    
    nyrsfm[1]=0x30;
    Write_char(1,0x30);
    
    nyrsfm[2]=year/10+0x30;
    Write_char(1,nyrsfm[2]);
    
    nyrsfm[3]=year%10+0x30;
    Write_char(1,nyrsfm[3]);
    
    nyrsfm[4]='-';
    Write_char(1,nyrsfm[4]);
    
    nyrsfm[5]=month/10+0x30;
    Write_char(1,nyrsfm[5]);

    nyrsfm[6]=month%10+0x30;
    Write_char(1,nyrsfm[6]);
    
    nyrsfm[7]='-';
    Write_char(1,nyrsfm[7]);
    
    nyrsfm[8]=day/10+0x30;
    Write_char(1,nyrsfm[8]);
    
    nyrsfm[9]=day%10+0x30;
    Write_char(1,nyrsfm[9]);
    
    nyrsfm[10]=' ';
    Write_char(1,nyrsfm[10]);
    
    nyrsfm[11]=shi/10+0x30;
    Write_char(1,nyrsfm[11]);
    
    nyrsfm[12]=shi%10+0x30;
    Write_char(1,nyrsfm[12]);
    
    nyrsfm[13]=':';
    Write_char(1,nyrsfm[13]);
    
    nyrsfm[14]=fen/10+0x30;
    Write_char(1,nyrsfm[14]);
    
    nyrsfm[15]=fen%10+0x30;
    Write_char(1,nyrsfm[15]);
}

/**********************************
函数名称:    Write_week()
函数功能:    星期显示
入口参数:    xq
备注    :
**********************************/
void Write_week(char xq)
{
    uchar i;

    Lcd_pos(2,0);
    switch(xq)
    {
        case 1:
            i=0;
            while(table3[i]!='\0')
            {
                Write_char(1,table3[i]);
                i++;
            }
            break;
        case 2:
            i=0;
            while(table4[i]!='\0')
            {
                Write_char(1,table4[i]);
                i++;
            }
            break;
        case 3:
            i=0;
            while(table5[i]!='\0')
            {
                Write_char(1,table5[i]);
                i++;
            }
            break;
        case 4:
            i=0;
            while(table6[i]!='\0')
            {
                Write_char(1,table6[i]);
                i++;
            }
            break;
        case 5:
            i=0;
            while(table7[i]!='\0')
            {
                Write_char(1,table7[i]);
                i++;
            }
            break;
        case 6:
            i=0;
            while(table8[i]!='\0')
            {
                Write_char(1,table8[i]);
                i++;
            }
            break;
        case 7:
            i=0;
            while(table9[i]!='\0')
            {
                Write_char(1,table9[i]);
                i++;
            }
            break;
    }
}

/**********************************
函数名称:    Set_time()
函数功能:    DS12C887首次上电初始化时间函数
入口参数:    无
备注    :
**********************************/
void Set_time()
{
    Write_ds(2,25);
    Write_ds(4,10);
    Write_ds(6,3);
    Write_ds(7,18);
    Write_ds(8,9);
    Write_ds(9,13);
}

/***********************************
函数名称:    init()
函数功能:    初始化
入口参数:    无
备注    :
***********************************/
void init()
{
    uchar i;
    i=0;
    Wela=0;
    Dula=0;
    EA=1;
    EX1=1;
    IT1=1;

    Set_time();        //首次上电时使用,只需执行一次
    Lcd_pos(3,0);    
    while(table1[i]!='\0')
    {
        Write_char(1,table1[i]);
        i++;
    }
}


/**********************************
函数名称:    Main()
函数功能:    
入口参数:
备注    :
**********************************/
void Main()
{
    init_com();            //串口初始化
    Lcd_init();            //LCD初始化
    init();                //不能放在Lcd_init()函数前面,,只有在液晶初始化之后才能够显示init()函数的内容
    
    while(1)
    {
        Ds_change();        //读取寄存器中存储的温度数
        Get_temp();            //获取温度并转换
        
        sprintf(buff,"%.1f",f_temp);        //将浮点型温度格式化为字符型,并保留一位,需声明#include<stdio.h>
        Delay(1000);
        comm(buff);            //串口发送数据,在串口调试助手上显示,这里主要是为了测试buff的内容而加上这句的
        Display_temp();        //在12864液晶上显示温度
        
        Delay(5);
        year=Read_ds(9);    //从DS12C887上读取数据
        month=Read_ds(8);
        day=Read_ds(7);
        week=Read_ds(6);
        shi=Read_ds(4);
        fen=Read_ds(2);

        Write_nyrsfm(year,month,day,shi,fen);        //在12864液晶显示
        Write_week(week);                            //显示星期
    }
}

 

 

posted on 2013-09-18 10:44  天若海愚  阅读(642)  评论(0编辑  收藏  举报