(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函数初始化时调用一下,也可以不用。
好了,今天就先到这吧,懒猫该继续修行去了,最后再喊一下口号,鼓励一下自己:
每天进步一点点,开心多一点^_^