原创 AVR学习日志(五十五)—LCD_CLOCK电子钟

2010-12-19 20:10 2595 10 10 分类: MCU/ 嵌入式


       年终了,做了个电子钟,用的是实时时钟芯片DS1302,显示用的是LM1602。电路图如下面所示:


 8fcc12f9-27a5-49be-9117-092e20711a83.jpg


图1-1 仿真电路图


        LM1602在前面懒猫已了解过了,这里就不在做详细的说明,简单的介绍一下DS1302这个芯片,它是美国DALLAS公司推出的一种高性能、低功耗的实时时钟芯片,附加31字节静态RAM,采用SPI三线接口与MCU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号和RAM数据。实时时钟可提供秒,分,时,日,周,月与年,一个月小于31天时可以自动调整,且具有闰年补偿功能。工作电压宽达2.3-5.5V。采用又电源供电(主电源和备用电源),可以设置备用电源充电方式,提供了对后背电源进行涓流充电的功能。


        简单说一下软件,软件包括三部分:MAIN.c,LCD1602.h与DS1302.h。main.c中是关于时间显示的一些函数,LCD1602.h中是一些LCD1602.h的一些底层驱动函数,DS1302.h中是一些关于DS1302的驱动函数。程序不复杂没有什么算法,只是一些底层驱动而已,下面贴上来:


一、MAIN.c


/***********************************************************************


//文件名称: MAIN.c


//功    能:学习使用实时时钟芯片DS1302


//作    者:懒猫爱飞


//创建日期:2010.12.16


//备    注:MCU ATmega8515   编译环境:winavr20100110


***********************************************************************/


#include <avr/io.h>


#include <util/delay.h>


#include <inttypes.h>


#include <string.h>


 


#include "DS1302.h"


#include "LCD1602.h"


 


uint8_t YMD_DIS_BUF[] = {0,0,0,0,0,0,0}; //存放年月日,周


uint8_t HMS_DIS_BUF[] = {0,0,0,0,0,0};   //存放时分秒


 


uint8_t LCD_DATE_BUF[]={"DATE 00-00-00   "};


uint8_t LCD_TIME_BUF[]={"TIME 00:00:00^_^"};


 


/***********************************************************************


//函数名称:void port_init(void)


//功    能:端口初始化


//入口参数:无


//出口参数:无


***********************************************************************/


void port_init(void)


{


  DDRA  = 0xff;               //A口输出                                  


  PORTA = 0x00;


 


  DDRB  = 0xff;               //B口输出


  PORTB = 0x00;


 


  DDRC  = 0xff;               //C口输出


  PORTC = 0x80;


 


  DDRD  = 0x00;               //D口


  PORTD = 0x00;


}


 


/***********************************************************************


//函数名称:void refresh_date_time(void)


//功    能:刷新日期与时间


//入口参数:无


//出口参数:无


***********************************************************************/


void refresh_date_time(void)


{


  YMD_DIS_BUF[0] = DataTime[6] / 10;  //年


  YMD_DIS_BUF[1] = DataTime[6] % 10; 


 


  YMD_DIS_BUF[2] = DataTime[4] / 10;  //月


  YMD_DIS_BUF[3] = DataTime[4] % 10; 


 


  YMD_DIS_BUF[4] = DataTime[3] / 10;  //日


  YMD_DIS_BUF[5] = DataTime[3] % 10; 


 


    YMD_DIS_BUF[6] = DataTime[5]-1;      // 周


 


  HMS_DIS_BUF[0] = DataTime[2] / 10;  //时


  HMS_DIS_BUF[1] = DataTime[2] % 10; 


 


  HMS_DIS_BUF[2] = DataTime[1] / 10;  //分


  HMS_DIS_BUF[3] = DataTime[1] % 10; 


 


  HMS_DIS_BUF[4] = DataTime[0] / 10;  //秒


  HMS_DIS_BUF[5] = DataTime[0] % 10;  //秒


 


}


 


/***********************************************************************


//函数名称:void refresh_display(void)


//功    能:刷新LCD显示


//入口参数:无


//出口参数:无


//描    述:其实就是在LCD相应的位置把新数据得新显示


***********************************************************************/


void refresh_display(void)


{


    //秒更新


  Write_LCD_Command(0x80+0x40+11);


  Write_LCD_Data(HMS_DIS_BUF[4]+0x30);


  Write_LCD_Command(0x80+0x40+12);


  Write_LCD_Data(HMS_DIS_BUF[5]+0x30);


   


  //分更新


  Write_LCD_Command(0x80+0x40+8);


  Write_LCD_Data(HMS_DIS_BUF[2]+0x30);


  Write_LCD_Command(0x80+0x40+9);


  Write_LCD_Data(HMS_DIS_BUF[3]+0x30);


 


  //小时更新


  Write_LCD_Command(0x80+0x40+5);


  Write_LCD_Data(HMS_DIS_BUF[0]+0x30);


  Write_LCD_Command(0x80+0x40+6);


  Write_LCD_Data(HMS_DIS_BUF[1]+0x30);


 


    //日更新


  Write_LCD_Command(0x80+0x00+11);


  Write_LCD_Data( YMD_DIS_BUF[4]+0x30);


  Write_LCD_Command(0x80+0x00+12);


  Write_LCD_Data( YMD_DIS_BUF[5]+0x30);


   


  //月更新


  Write_LCD_Command(0x80+0x00+8);


  Write_LCD_Data(YMD_DIS_BUF[2]+0x30);


  Write_LCD_Command(0x80+0x00+9);


  Write_LCD_Data(YMD_DIS_BUF[3]+0x30);


 


  //年更新


  Write_LCD_Command(0x80+0x00+5);


  Write_LCD_Data(YMD_DIS_BUF[0]+0x30);


  Write_LCD_Command(0x80+0x00+6);


  Write_LCD_Data(YMD_DIS_BUF[1]+0x30);


 


    //周更新


  Display_LCD_String(0x80+0x00+13,week[YMD_DIS_BUF[6]]);


}


 


/***********************************************************************


//函数名称:int main(void)


//功    能:主函数


//入口参数:无


//出口参数:无


//描    述:无


***********************************************************************/


int main(void)


{


    port_init();


    Init_LCD();


  Display_LCD_String(0x00,LCD_DATE_BUF);


  Display_LCD_String(0x40,LCD_TIME_BUF);


 


  while(1)


  {


       get_date_time();        // 从DS1302中读取日期与时间


       refresh_date_time();    //更新数据


 


        refresh_display();


 


        _delay_ms(200);


 


  }


 


  return 0;


}


二、DS1302.h


/***********************************************************************


//文件名称:DS1302.h


//功    能:关于DS1302的一些函数


//作    者:懒猫爱飞


//创建日期:2010.12.16


//备    注:MCU ATmega8515   编译环境:winavr20100110


***********************************************************************/


#ifndef __DS1302_H__


#define __DS1302_H__


 


#include <avr/io.h>


#include <string.h>


#include <util/delay.h>


 


#define uchar unsigned char


#define uint  unsigned int


 


//0,1,2,3,4,5,6分别代表对应周日,周一到周六


uint8_t *week[] = {"SUN","MON","TUS","WEN","THU","FRI","SAT"};


//存放所读取的时间


uint8_t DataTime[7] = {0,0,0,0,0,0,0};    //初始化


 


 


 


//宏定义循环左移


#define crol(a,n) a = (a<<(n))|(a<<(1-(n)))


 


//DS1302引脚端口宏定义


//PB0连接DS1302的data in 引脚,所以要读取数据与写数据


//判断PB0的高低电平来读取DS1302内部的数据


#define DATA_IN (PINB&(1<<0))


 


//DS1302接口数据方向


#define DDR_IO_RD()  DDRB &= ~_BV(PB0)   // 输入,从DS1302中读数据


#define DDR_IO_WR()  DDRB |= _BV(PB0)    // 输出,向DS1302中写数据


 


//向DS1302内部写数据时有用


#define WR_IO_H()    PORTB |=  _BV(PB0)     //DS1302数据端口I/O


#define WR_IO_L()    PORTB &= ~_BV(PB0)


 


#define SCLK_H()     PORTB |=  _BV(PB1)     //DS1302时钟端口


#define SCLK_L()     PORTB &= ~_BV(PB1)


 


#define RST_H()      PORTB |=  _BV(PB2)     //DS1302复位端口


#define RST_L()      PORTB &= ~_BV(PB2)


 


 


/***********************************************************************


//函数名称:void Write_A_Byte_TO_DS1302(uint8_t dat)


//功    能:向DS1302中写一个数据


//入口参数:dat


//出口参数:无


***********************************************************************/


void Write_A_Byte_TO_DS1302(uint8_t dat)


{


  uint8_t i;


    DDR_IO_WR(); //设置数据口为输出,向DS1302中写入数据


  for(i=0;i<8;i++)


  {


        SCLK_L();


       if(dat&0x01)


       {


             WR_IO_H();


       }


       else


       {


             WR_IO_L();


       }


       SCLK_H(); //上升沿数据被写入


       dat>>=1;


  }


}


 


/***********************************************************************


//函数名称:uint8_t Get_A_Byte_FROM_DS1302()


//功    能:从DS1302中读取一个数据


//入口参数:无


//出口参数:DAT


***********************************************************************/


uint8_t Get_A_Byte_FROM_DS1302()


{


  unsigned char i;


  unsigned char DAT = 0X00;


  DDR_IO_WR();//数据口设置输入,从DS1302中读取数据


  PORTB &= ~_BV(0);


  for(i=0;i<8;i++)


  {


        SCLK_H();


       SCLK_L();


       if(DATA_IN)


       {


             DAT |= _BV(i);   //输入为高则置为高电平


       }


       else


       {


             DAT &= ~_BV(i); //输入为低则清零


       }


  }


 


  return (DAT>>4)*10+(DAT&0x0F);  //将BCD码转换为十进制数并返回


}


 


/***********************************************************************


//函数名称:uint8_t Read_Data(uint8_t addr)


//功    能:从DS1302指定地址读取一个字节


//入口参数:addr


//出口参数:dat


***********************************************************************/


uint8_t Read_Data(uint8_t addr)


{


  uint8_t dat;


  //RST_L();


  //SCLK_L();


  RST_H();


  Write_A_Byte_TO_DS1302(addr);


    //_delay_us(5);


  dat = Get_A_Byte_FROM_DS1302();


  SCLK_H();


  RST_L();


  return dat;


}


/***********************************************************************


//函数名称:void Write_Data_To_DS1302(uint8_t addr,uint8_t dat)


//功    能:向DS1302指定地址写一个字节


//入口参数:addr,dat


//出口参数:null


***********************************************************************/


void Write_Data_To_DS1302(uint8_t addr,uint8_t dat)


{


  //RST_L();


  //SCLK_L();


  //_delay_us(5);


  //RST_H();


  Write_A_Byte_TO_DS1302(addr); //写地址


  //SCLK_L();


  //_delay_us(5);


  Write_A_Byte_TO_DS1302(dat); //写数据


  //SCLK_L();


  //_delay_us(5);


  RST_L();


}


 


/***********************************************************************


//函数名称:void get_date_time(void)


//功    能:从DS1302读取日期时间


//入口参数:无


//出口参数:无


***********************************************************************/


void get_date_time(void)


{


  uint8_t i = 0;


  uint8_t addr = 0x81;    //0x81是秒地址


    for(i=0; i<7; i++)


  {


       DataTime = Read_Data(addr); //读日期时间地址依次是0x81,0x83...


       addr += 2;                        


  }


}


 


/***********************************************************************


//函数名称:void set_date_time(void)


//功    能:设定日期时间


//入口参数:无


//出口参数:无


//描    述:记得转换成BCD码再写进去


***********************************************************************/


void set_date_time(void)


{


  uint8_t i = 0;


  uint8_t addr_r = 0x80;                //起始状态为秒地址


  uint8_t dat_w;


    Write_Data_To_DS1302(0x8E,0x00);    //写控制字,取消写保护


    //将秒,分,时,日,月,周,年依次写入


  for(i=0;i<7;i++)


  {


       //秒的起始地址0x80


       //分,时,日,月,周,年的地址依次加2


       //2位的十进制要转换成BCD码后再写入


        addr_r = 0x80 + 2*i;


       dat_w = (DataTime/10<<4) | (DataTime%10);


 


        Write_Data_To_DS1302(addr_r,dat_w);


  }


 


    Write_Data_To_DS1302(0x8E,0x80);    //写控制字,写保护


}


 


#endif


三、LCD1602.h


/***********************************************************************


//文件名称:LCD1602.h


//功    能:关于LCD1602的一些函数


//作    者:懒猫爱飞


//创建日期:2010.12.16


//备    注:MCU ATmega8515   编译环境:winavr20100110


***********************************************************************/


#ifndef __LCD1602_H__


#define __LCD1602_H__


 


#include <avr/io.h>


#include <util/delay.h>


#include <string.h>


 


//LCD1602的端口宏定义


//_BV(bit) 的宏定义在io.h中,准确的说应该在sfr_defs.h这个头文件中


#define LCD_RS_R()   PORTC |=  _BV(0)


#define LCD_RS_S()   PORTC &= ~_BV(0)


 


#define LCD_RW_R()   PORTC |=  _BV(1)


#define LCD_RW_W()   PORTC &= ~_BV(1)


 


#define LCD_EN_EN()  PORTC |=  _BV(2)


#define LCD_EN_DIS() PORTC &= ~_BV(2)


 


#define LCD_DATA_IN()    DDRA = 0x00


#define LCD_DATA_OUT()   DDRA = 0xFF


 


#define LCD_DATA_W   PORTA


#define LCD_DATA_R   PINA


 


#define LCD_Busy 0x80


 


/***********************************************************************


//函数名称:uint8_t Read_LCD_State()


//功    能:读取LCD的状态


//入口参数:无


//出口参数:state


***********************************************************************/


uint8_t Read_LCD_State()


{


  uint8_t state;


 


  LCD_RS_S();           //下面这些可以参看数据手册中的时序表


  LCD_RW_R();


  LCD_EN_EN();


 


  LCD_DATA_IN();        //设置PA口为输入口,读取LCD的状态


  state = LCD_DATA_R;


 


  LCD_EN_DIS();


 


  return state;


}


 


/***********************************************************************


//函数名称:void LCD_Busy_Wait()


//功    能:LCD忙等待状态


//入口参数:无


//出口参数:无


***********************************************************************/


void LCD_Busy_Wait()


{


 


  while((Read_LCD_State()&LCD_Busy) == LCD_Busy);


 


}


 


/***********************************************************************


//函数名称:void Write_LCD_Data(uint8_t dat)


//功    能:向LCD写数据


//入口参数:dat


//出口参数:无


***********************************************************************/


void Write_LCD_Data(uint8_t dat)


{


  LCD_Busy_Wait();


  LCD_RS_R();


  LCD_RW_W();


  LCD_EN_EN();


 


  LCD_DATA_OUT(); //    PA口设置为输出,向LCD写数据


  LCD_DATA_W = dat;


 


  LCD_EN_DIS();


}


 


/***********************************************************************


//函数名称:void Write_LCD_Command(uint8_t cmd)


//功    能:向LCD写命令


//入口参数:cmd


//出口参数:无


***********************************************************************/


void Write_LCD_Command(uint8_t cmd)


{


  LCD_Busy_Wait();


  LCD_RS_S();


  LCD_RW_W();


  LCD_EN_EN();


 


  LCD_DATA_OUT();      //PA口设置为输出,向LCD写命令


  LCD_DATA_W = cmd;


  


 


  LCD_EN_DIS();


}


 


/***********************************************************************


//函数名称:void Set_LCD_POS(uint8_t  p)


//功    能:设置LCD显示的位置


//入口参数:p


//出口参数:无


***********************************************************************/


void Set_LCD_POS(uint8_t p)


{


  Write_LCD_Command(p|0x80); 


}


 


/***********************************************************************


//函数名称:void Display_LCD_String(uint8_t p,uint8_t *s)


//功    能:向LCD中写入要显示的字符串


//入口参数:p  *s


//出口参数:无


***********************************************************************/


void Display_LCD_String(uint8_t p,uint8_t *s)


{


  uint8_t i;


  Set_LCD_POS(p);


 


  for(i=0;i<16;i++)


  {


       Write_LCD_Data(s);


  }


}


 


/***********************************************************************


//函数名称:void Init_LCD(void)


//功    能:LCD初始化


//入口参数:无


//出口参数:无


***********************************************************************/


void Init_LCD(void)


{


  Write_LCD_Command(0x38); //写命令


    _delay_ms(1);


  Write_LCD_Command(0x01); // 清屏命令


  _delay_ms(1);


  Write_LCD_Command(0x06); //字符进入模式:屏幕不动,字符后移


  _delay_ms(1);


  Write_LCD_Command(0x0c); //关光标,关闪烁,显示开


  _delay_ms(1);


}


#endif


 


 


        程序没有复杂的算法,纯粹是一些底层驱动,按键函数还没有加上,所以现在时间日期还不能调整,但是初始化日期可以修改,只要在DataTime[7]这个数组中初始化好日期时间就可以了,void set_date_time(void)这个函数是设定日期时间的,可以在main函数初始化时调用一下,也可以不用。


        好了,今天就先到这吧,懒猫该继续修行去了,最后再喊一下口号,鼓励一下自己:


 


 每天进步一点点,开心多一点^_^

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
10
关闭 站长推荐上一条 /1 下一条