注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
四蹄踏雪II号的个人空间 https://home.eeworld.com.cn/space-uid-224269.html [收藏] [复制] [分享] [RSS]
日志

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

已有 1339 次阅读2010-12-29 16:42

(192.70 K) 该附件被下载次数 1

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

图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

#include

#include

#include

#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

#include

#include

#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[i] = 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[i]/10<<4) | (DataTime[i]%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

#include

#include

//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[i]);

}

}

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

//函数名称: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函数初始化时调用一下,也可以不用。

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

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

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章