基于DS1302的实用时钟程序(iccavr)
扫描二维码
随时随地手机看文章
/*******************************************
*lcd1602_8h.h*
* LCD1602操作库函数,8位数据联接关系: *
* LCD1602:DB0-DB7 M16:PB0-PB7 *
* LCD1602:RS M16:PD3 *
* LCD1602:E M16:PD6 *
* LCD1602:R/WGND *
*******************************************/
//LCD相关端口定义
#define RS1602 PD3 //RS接在PORTD.3上
#define E1602 PD6//E接在PORTD.6上
#define CLR_RS PORTD&=~(1<
#define uchar unsigned char
#define uint unsigned int
//延时函数:入口i,需要定时的时长
void delay(uint i) //在1M时钟下为i ms
{
uchar j;
for(;i;i--)
{
for(j=220;j;j--)
{;}
}
}
/*************************************
* LCD1602操作函数组 *
*************************************/
//写数据到LCD的函数:入口data,需写入的显示数据
void lcd_da(uchar data)
{
SET_RS; //RS置高,写数据
delay(1);
SET_E;
PORTB=data;//数据送端口
delay(1);
CLR_E;//E下降沿写入数据
delay(1);//延时1MS
}
//写指令到LCD的函数:入口data,需写入的控制指令
void lcd_comm(uchar data)
{
CLR_RS; //RS清0,写指令
delay(1);//延时1MS
SET_E;
PORTB=data;//指令送端口
delay(1);
CLR_E;//E下降沿写入指令
delay(1);//延时1MS
}
//将时钟数据转换后在LCD上显示
void timer_lcd(void)
{
lcd_comm(0x80); //写指令:第1行地址
lcd_da(0x20); //留3个空格,使显示数据居中
lcd_da(0x20);
lcd_da(0x20);
lcd_da(0x32); //显示世纪位:2
lcd_da(0x30); //0
lcd_da((timer[1]>>4)+0x30); //显示年
lcd_da((timer[1]&0x0f)+0x30);
lcd_da('/');
lcd_da((timer[2]>>4)+0x30); //显示月
lcd_da((timer[2]&0x0f)+0x30);
lcd_da('/');
lcd_da((timer[3]>>4)+0x30); //显示日
lcd_da((timer[3]&0x0f)+0x30);
lcd_comm(0xC0); //写指令:第2行地址
lcd_da(20); //留1个空格
lcd_da((timer[4]>>4)+0x30); //时
lcd_da((timer[4]&0x0f)+0x30);
lcd_da(':');
lcd_da((timer[5]>>4)+0x30); //分
lcd_da((timer[5]&0x0f)+0x30);
lcd_da(':');
lcd_da((timer[6]>>4)+0x30); //秒
lcd_da((timer[6]&0x0f)+0x30);
lcd_da(20); //时间与星期间留1空格
lcd_da('W'); //星期的前导字
lcd_da('e');
lcd_da('e');
lcd_da('k');
lcd_da((timer[7]&0x0f)+0x30); //星期数据
}
//调整显示函数
//入口:m为调整的数据说明,i为调整数据
void set_timer_lcd(uchar m,uchar i)
{
lcd_comm(0x80); //写指令:第1行地址
lcd_comm(0x01); //写指令:CLS
lcd_da(0x20); //留空格
lcd_da(0x20);
// lcd_da(0x20);
// lcd_da(0x20);
lcd_da('S'); //显示SET:
lcd_da('E');
lcd_da('T');
lcd_da(':');
lcd_comm(0xC0); //写指令:第2行地址
lcd_da(20); //留1个空格
switch(m)
{
case 1: //年调整
{lcd_da('Y');
lcd_da('E');
lcd_da('A');
lcd_da('R');
lcd_da('=');};break;
case 2: //月调整
{lcd_da('M');
lcd_da('O');
lcd_da('N');
lcd_da('T');
lcd_da('H');
lcd_da('=');};break;
case 3: //日调整
{lcd_da(0x20);
lcd_da('D');
lcd_da('A');
lcd_da('Y');
lcd_da('=');};break;
case 4: //小时调整
{lcd_da('H');
lcd_da('O');
lcd_da('U');
lcd_da('R');
lcd_da('=');};break;
case 5: //分调整
{lcd_da(0x20);
lcd_da('M');
lcd_da('I');
lcd_da('N');
lcd_da('=');};break;
case 7: //周调整
{lcd_da('W');
lcd_da('E');
lcd_da('E');
lcd_da('K');
lcd_da('=');};break;
default:;break;
}
lcd_da((i>>4)+0x30); //调整数据
lcd_da((i&0x0f)+0x30);
lcd_da(0x20);
lcd_da(0x20);
}
//LCD1602初始化函数
void lcd_init(void)
{
lcd_comm(0x38); //写指令:8位数据、2行显示、5*8点阵
delay(5);
lcd_comm(0x06); //写指令:自左向右显示
delay(5);
lcd_comm(0x0C); //写指令:显示开
delay(5);
lcd_comm(0x14); //写指令:移动
delay(5);
lcd_comm(0x80); //写指令:第1行地址
delay(5);
lcd_comm(0x01); //写指令:CLS
}
//ds1302_h.h
//DS1302操作库函数,在用M16学习板时可直接引用
//宏定义:
//联接DS1302的端口定义
#define ds1302_rst PC4 //定义1302的RST接在PC4
#define ds1302_io PC3 //定义1302的IO接在PC3
#define ds1302_sclk PC2//定义1302的时钟接在PC2
#define set_ds1302_rst_ddr() DDRC|=1<
#define ds1302_min_add 0x82 //分数据地址
#define ds1302_hr_add 0x84 //时数据地址
#define ds1302_date_add 0x86 //日数据地址
#define ds1302_month_add 0x88 //月数据地址
#define ds1302_day_add 0x8a //星期数据地址
#define ds1302_year_add 0x8c //年数据地址
#define ds1302_control_add 0x8e //控制数据地址
#define ds1302_charger_add 0x90
#define ds1302_clkburst_add 0xbe
//简化宏定义
#define uchar unsigned char
#define uint unsigned int
/*************************************
* DS1302操作函数组 *
*************************************/
//写入1302数据函数:
//入口:add为写入地址码,data为写入数据
//返回:无
void ds1302_write(uchar add,uchar data)
{
uchar i=0;
set_ds1302_io_ddr(); //配置IO为输出
asm("nop");
asm("nop");
clr_ds1302_rst(); //清复位,停止所有操作
asm("nop");
asm("nop");
clr_ds1302_sclk(); //清时钟,准备操作
asm("nop");
asm("nop");
set_ds1302_rst(); //置复位,开始操作
asm("nop");
asm("nop");
for(i=8;i>0;i--) //此循环写入控制码
{
if(add&0x01)
set_ds1302_io(); //当前位为1,置数据位
else
clr_ds1302_io(); //当前位为0,清数据位
asm("nop");
asm("nop");
set_ds1302_sclk(); //产生时钟脉冲,写入数据
asm("nop");
asm("nop");
clr_ds1302_sclk();
asm("nop");
asm("nop");
add>>=1; //移位,准备写入下1位
}
for(i=8;i>0;i--) //此循环写入数据码
{
if(data&0x01)
set_ds1302_io();
else
clr_ds1302_io();
asm("nop");
asm("nop");
set_ds1302_sclk();
asm("nop");
asm("nop");
clr_ds1302_sclk();
asm("nop");
asm("nop");
data>>=1;
}
clr_ds1302_rst();
asm("nop");
asm("nop");
clr_ds1302_io_ddr(); //清输出状态
asm("nop");
asm("nop");
}
//从1302中读出数据:
//入口:add为读数据所在地址
//返回:读出的数据data
uchar ds1302_read(uchar add)
{
uchar data=0;
uchar i=0;
add+=1; //读标志
set_ds1302_io_ddr(); //端口输出
asm("nop");
asm("nop");
clr_ds1302_rst(); //清复位
asm("nop");
asm("nop");
clr_ds1302_sclk(); //清时钟
asm("nop");
asm("nop");
set_ds1302_rst(); //置复位
asm("nop");
asm("nop");
for(i=8;i>0;i--) //此循环写入地址码
{
if(add&0x01)
{set_ds1302_io();}
else
{clr_ds1302_io();}
asm("nop");
asm("nop");
set_ds1302_sclk();
asm("nop");
asm("nop");
clr_ds1302_sclk();
asm("nop");
asm("nop");
add>>=1;
}
clr_ds1302_io_ddr(); //端口输入
asm("nop");
asm("nop");
for(i=8;i>0;i--) //此循环读出1302的数据
{
data>>=1;
if(in_ds1302_io())
{data|=0x80;}
asm("nop");
asm("nop");
set_ds1302_sclk();
asm("nop");
asm("nop");
clr_ds1302_sclk();
asm("nop");
asm("nop");
}
clr_ds1302_rst();
asm("nop");
asm("nop");
return(data);
}
//检查1302状态
uchar check_ds1302(void)
{
ds1302_write(ds1302_control_add,0x80);
if(ds1302_read(ds1302_control_add)==0x80) return 1;
return 0;
}
//向1302中写入时钟数据
void ds1302_write_time(void)
{
ds1302_write(ds1302_control_add,0x00); //关闭写保护
ds1302_write(ds1302_sec_add,0x80); //暂停
ds1302_write(ds1302_charger_add,0xa9); //涓流充电
ds1302_write(ds1302_year_add,timer[1]); //年
ds1302_write(ds1302_month_add,timer[2]); //月
ds1302_write(ds1302_date_add,timer[3]); //日
ds1302_write(ds1302_day_add,timer[7]); //周
ds1302_write(ds1302_hr_add,timer[4]); //时
ds1302_write(ds1302_min_add,timer[5]); //分
ds1302_write(ds1302_control_add,0x80); //打开写保护
}
//秒清0,并启动时钟
void start_ds1302(void)
{
ds1302_write(ds1302_control_add,0x00); //关闭写保护
ds1302_write(ds1302_sec_add,0x00); //开启计时,秒清0
ds1302_write(ds1302_control_add,0x80); //打开写保护
}
/****************************************/
/*ds1302_lcd_h.c */
/*DS1302 实用时钟程序LCD1602版*/
/* 实验环境:阿发的M16学习板 */
/* 时钟频率:内部 1.0000Mhz */
/* 编译系统:ICCAVR6.31A */
/* 功 能:通过按键S0-S7对时钟数据 */
/* 进行调整,在LCD1602上显示*/
/* 说 明:S0调整/正常,S1调整数据转换*/
/* S2调整减, S3调整加*/
/* S7确认调整并写入暂停走时*/
/* S8启动时钟正常走时*/
/****************************************/
#include
#include
#include
#include
//简化宏定义
#define uchar unsigned char
#define uint unsigned int
//全局变量定义
uchar timer[8]; //时钟数据
//定义按键数据
uchar key,k1;
uchar k2=0;
uchar set_flag=0; //定义按键调整标志
//延时函数:入口i,需要定时的时长
void delay_ms(uint i) //在1M时钟下为i ms
{
uchar j;
for(;i;i--)
{
for(j=220;j;j--)
{;}
}
}
//向1302中写入时钟数据
void ds1302_write_time(void)
{
ds1302_write(ds1302_control_add,0x00); //关闭写保护
ds1302_write(ds1302_sec_add,0x80); //暂停
ds1302_write(ds1302_charger_add,0xa9); //涓流充电
ds1302_write(ds1302_year_add,timer[1]); //年
ds1302_write(ds1302_month_add,timer[2]); //月
ds1302_write(ds1302_date_add,timer[3]); //日
ds1302_write(ds1302_day_add,timer[7]); //周
ds1302_write(ds1302_hr_add,timer[4]); //时
ds1302_write(ds1302_min_add,timer[5]); //分
ds1302_write(ds1302_control_add,0x80); //打开写保护
}
//从1302中读出当前时钟
void ds1302_read_time(void)
{
timer[1]=ds1302_read(ds1302_year_add); //年
timer[2]=ds1302_read(ds1302_month_add); //月
timer[3]=ds1302_read(ds1302_date_add); //日
timer[7]=ds1302_read(ds1302_day_add); //周
timer[4]=ds1302_read(ds1302_hr_add); //时
timer[5]=ds1302_read(ds1302_min_add); //分
timer[6]=(ds1302_read(ds1302_sec_add))&0x7F; //秒
}
//将时钟数据转换后在LCD上显示
void timer_lcd(void)
{
lcd_comm(0x80); //写指令:第1行地址
lcd_da(0x20); //留3个空格,使显示数据居中
lcd_da(0x20);
lcd_da(0x20);
lcd_da(0x32); //显示世纪位:2
lcd_da(0x30); //0
lcd_da((timer[1]>>4)+0x30); //显示年
lcd_da((timer[1]&0x0f)+0x30);
lcd_da('/');
lcd_da((timer[2]>>4)+0x30); //显示月
lcd_da((timer[2]&0x0f)+0x30);
lcd_da('/');
lcd_da((timer[3]>>4)+0x30); //显示日
lcd_da((timer[3]&0x0f)+0x30);
lcd_comm(0xC0); //写指令:第2行地址
lcd_da(20); //留1个空格
lcd_da((timer[4]>>4)+0x30); //时
lcd_da((timer[4]&0x0f)+0x30);
lcd_da(':');
lcd_da((timer[5]>>4)+0x30); //分
lcd_da((timer[5]&0x0f)+0x30);
lcd_da(':');
lcd_da((timer[6]>>4)+0x30); //秒
lcd_da((timer[6]&0x0f)+0x30);
lcd_da(20); //时间与星期间留1空格
lcd_da('W'); //星期的前导字
lcd_da('e');
lcd_da('e');
lcd_da('k');
lcd_da((timer[7]&0x0f)+0x30); //星期数据
}
//时钟调整加函数:入口b需要调整的时钟数据位置
void add_timer(uchar b)
{
switch(b)
{
case 1: //年数据加1调整,范围00-99
{
timer[1]+=0x01;
if((timer[1]& 0x0f)==0x0A)
{
timer[1]=((timer[1]&0xF0)+0x10);
if(timer[1]>0x99)timer[1]=0x00;
}
};break;
case 2: //月数据加1调整,范围1-12
{
timer[2]+=0x01;
if(timer[2]==0x13)timer[2]=0x01;
if((timer[2]&0x0f)==0x0A)
{
timer[2]=((timer[2]&0xF0)+0x10);
}
};break;
case 3: //日数据加1调整,范围1-30,暂未考虑31日
{
timer[3]+=0x01;
if(timer[3]>0x30)timer[3]=0x01;
if((timer[3]&0x0f)==0x0A)
{
timer[3]=((timer[3]&0xF0)+0x10);
}
};break;
case 4: //时数据加1调整,范围0-24
{
timer[4]+=0x01;
if(timer[4]==0x24)timer[4]=0x00;
if((timer[4]&0x0f)==0x0A)
{
timer[4]=((timer[4]&0xF0)+0x10);
}
};break;
case 5: //分数据加1调整,范围0-59
{
timer[5]+=0x01;
if((timer[5]&0x0f)==0x0A)
{
timer[5]=((timer[5]&0xF0)+0x10);
if(timer[5]>0x59)timer[5]=0x00;
}
};break;
case 7: //星期数据加1调整,范围1-7
{
timer[7]+=0x01;
if(timer[7]>0x07)timer[7]=0x01;
};break;
default:;break;
}
}
//时钟调整减函数:入口b需要调整的时钟数据位置
void dec_timer(uchar b)
{
switch(b)
{
case 1: //年数据减1调整,范围00-99
{
timer[1]-=0x01;
if((timer[1]&0x0f)==0x0F)
{
timer[1]=(timer[1]&0xF9);
if(timer[1]>0x99)timer[1]=0x99;
}
};break;
case 2: //月数据减1调整,范围1-12
{
timer[2]-=0x01;
if(timer[2]==0x00)timer[2]=0x12;
if((timer[2]&0x0f)==0x0F)
{
timer[2]=(timer[2]&0xF9);
}
};break;
case 3: //日数据减1调整,范围1-30,暂未考虑31日
{
timer[3]-=0x01;
if(timer[3]==0x00)timer[3]=0x30;
if((timer[3]&0x0f)==0x0F)
{
timer[3]=(timer[3]&0xF9);
}
};break;
case 4: //时数据减1调整,范围0-24
{
timer[4]-=0x01;
if(timer[4]>0x24)timer[4]=0x23;
if((timer[4]&0x0f)==0x0f)
{
timer[4]=(timer[4]&0xF9);
}
};break;
case 5: //分数据减1调整,范围0-59
{
timer[5]-=0x01;
if((timer[5]&0x0f)==0x0F)
{
timer[5]=(timer[5]&0xF9);
if(timer[5]>0x59)timer[5]=0x59;
}
};break;
case 7: //星期数据减1调整,范围1-7
{
timer[7]-=0x01;
if(timer[7]<0x01)timer[7]=0x07;
};break;
default:;break;
}
}
/***************************************
* 按键读取判断函数 *
* 函数功能:判断按键是否按下 *
* 入口参数:无 *
* 返 回 值:无键按下0,有键按下为1-8 *
* 说 明:需要在初始化函数中将接口置 *
* 为输入 *
***************************************/
uchar rd_key(void)
{
uchar a,b;
a=PINA;
if(a==0xFF)
return(0);
else
{
a=~a;
switch(a)
{
case 0x01:b=1;break;
case 0x02:b=2;break;
case 0x04:b=3;break;
case 0x08:b=4;break;
case 0x10:b=5;break;
case 0x20:b=6;break;
case 0x40:b=7;break;
case 0x80:b=8;break;
default:b=0;break;
}
return(b); //返回的1-8分别对应按键S0-S7
}
}
//定时器1:每秒从DS1302中读取4次数据,更新显示
void timer1_init(void) //定时器1初始化:250毫秒定时,预分频256
{
TCCR1B = 0x00; //停止定时器
TCNT1H = 0xFC; //初值高字节
TCNT1L = 0x2F; //定时初值低字节
TCCR1A = 0x00;
TCCR1B = 0x04; //启动定时器
}
#pragma interrupt_handler timer1_ovf_isr:9
void timer1_ovf_isr(void) //定时器1中断入口:250MS中断一次
{
TCNT1H = 0xFC; //重装初值
TCNT1L = 0x2F;
ds1302_read_time(); //读出当前时钟
timer_lcd(); //显示数据转换
}
/* 定时器0中断配置函数
预分频数: 256
定时时间: 50mSec
功 能:在进入调整状态20秒内没操作自动回到时钟状态*/
void timer0_init(void)
{
TCCR0 = 0x00; //先关定器0
TCNT0 = 0x3C; //装初值
OCR0 = 0x13; //set compare
TCCR0 = 0x04; //开定时器
}
/* 定时器0中断入口函数 */
#pragma interrupt_handler timer0_ovf_isr:10
void timer0_ovf_isr(void)
{
TCNT0 = 0x3C; //重装初值
if(k2==0)
{
set_flag=0; //清调整标志
TCCR1B = 0x04; //启动定时器1
}
else
k2--;
}
/*************************************
* 主函数 *
*************************************/
void main(void)
{
uchar a=1,b=0;
DDRA=0x00; //设置按键A口为带上拉输入:接按键
PORTA=0xFF;
DDRC=255; //定义C口为输出:接LCD背光
DDRB=255; //定义B口为输出:接LCD数据
PORTB=255;
DDRD=0xFB; //定义D口为输出:接LCD控制,PD2为输入,中断按钮
PORTD=0x04; //PD2上拉
delay_ms(500); //延时500ms,准备LCD初始化
lcd_init(); //初始化LCD
delay_ms(50);
CLI(); //先关闭所有中断
timer0_init(); //设定定时器0
timer1_init(); //设定定时器1
MCUCR = 0x00;
GICR = 0x00;
TIMSK = 0x05; //允许定时器0、定时器1中断
SEI(); //开总中断
while(1)
{
if(rd_key()!=0)
{
k1=rd_key(); //
delay_ms(20); //
if(rd_key()==k1)
{
key=rd_key();
switch(key)
{
case 1: //调整和结束调整处理
{
if(set_flag==0)
{
set_flag=1;
a=1;
k2=250;
TCCR1B = 0x00; //停止定时器1
set_timer_lcd(1,timer[1]); //年数据调整显示
}
else
{
set_flag=0;
TCCR1B = 0x04; //启动定时器
}
};break;
case 2: //调整数据切换
{
if(set_flag==1)
{
a++;
if(a==6)a=7;
if(a>=8)a=1;
set_timer_lcd(a,timer[a]);
k2=250;
}
};break;
case 3: //数据减1调整
{
if(set_flag==1)
{
dec_timer(a);
set_timer_lcd(a,timer[a]);
k2=250;
}
};break;
case 4: //数据加1调整
{
if(set_flag==1)
{
add_timer(a);
set_timer_lcd(a,timer[a]);
k2=250;
}
};break;
case 5:;break;
case 6:;break;
case 7: //写入调整后的时钟并暂停
{
if(set_flag==1)
{
ds1302_write_time(); //写入调整后的时钟并暂停
set_flag=0;
TCCR1B = 0x04; //启动定时器
b=1;
}
};break;
case 8: //启动时钟
{
if(b==1)
{
start_ds1302();
b=0;
}
};break;
default:;break;
}
while(rd_key()!=0)
{
delay_ms(20);
}
}
}
}
}