调了半天终于弄出个程序好用的,可以自动计算2000年~2099年中任意一天是星期几。
经过MEGA128上的测试,目前没发现问题,现在发上来,请各位大虾帮忙测试一下。
另外我现在的程序只能做2000年~2099年中的星期计算,现在想做个任意年份的星期计算,一直没找到合适的算法,(有一个叫“蔡勒公式”的,但我按他写的程序就是不好用,可能是我太苯了)还望各位大虾不吝赐教。
虽然是在MEGA64上调的但适用于所有AVR芯片,改下管脚设置就行了。
程序不是我原创,我只是把我找到的DS1302程序和星期计算程序做了下修改使他们能适应我的系统能一起工作而已,在此向写这些程序的原作者表示敬意。
http://blog.csdn.net/zhenhua10 添加日期时间修正程序
#define nop() asm("nop;" ::);
struct timechar
{
uchar nowyear;
uchar nowmonth;
uchar nowday;
uchar nowhour;
uchar nowminute;
uchar nowsecond;
uchar nowweek;
};
struct timechar nowtime;///这里存时间
#define ds1302_rst 7 /*!PORTB7 DS1302实时时钟复位线引脚 */
#define ds1302_io 6 /*!PORTB6 DS1302实时时钟数据线引脚 */
#define ds1302_sclk 5 /*!PORTB5 DS1302实时时钟时钟线引脚 */
#define set_ds1302_rst_ddr() DDRB|=1<<ds1302_rst
#define set_ds1302_rst() PORTB|=1<<ds1302_rst
#define clr_ds1302_rst() PORTB&=~(1<<ds1302_rst)
#define set_ds1302_io_ddr() DDRB|=1<<ds1302_io
#define clr_ds1302_io_ddr() DDRB&=~(1<<ds1302_io)
#define set_ds1302_io() PORTB|=1<<ds1302_io
#define clr_ds1302_io() PORTB&=~(1<<ds1302_io)
#define in_ds1302_io() PINB&(1<<ds1302_io)
#define set_ds1302_sclk_ddr() DDRB|=1<<ds1302_sclk
#define set_ds1302_sclk() PORTB|=1<<ds1302_sclk
#define clr_ds1302_sclk() PORTB&=~(1<<ds1302_sclk)
#define ds1302_sec_reg 0x80
#define ds1302_min_reg 0x82
#define ds1302_hr_reg 0x84
#define ds1302_date_reg 0x86
#define ds1302_month_reg 0x88
#define ds1302_day_reg 0x8a
#define ds1302_year_reg 0x8c
#define ds1302_control_reg 0x8e
#define ds1302_charger_reg 0x90
#define ds1302_clkburst_reg 0xbe
uchar BcdToBin(uchar val)
{
val = (val >> 4) *10+(val &0x0f); ///将BCD码转换为10进制数
return val; ///返回10进制数
}
uchar BinToBcd(uchar val)
{
return ((val / 10) *16+val % 10);
}
void ds1302_write(uchar reg, uchar data)
{
uchar i = 0;
set_ds1302_io_ddr();
nop();
nop();
clr_ds1302_rst();
nop();
nop();
clr_ds1302_sclk();
nop();
nop();
set_ds1302_rst();
nop();
nop();
for (i = 8; i > 0; i--)
{
if (reg &0x01)
{
set_ds1302_io();
}
else
{
clr_ds1302_io();
}
nop();
nop();
set_ds1302_sclk();
nop();
nop();
clr_ds1302_sclk();
nop();
nop();
reg >>= 1;
}
for (i = 8; i > 0; i--)
{
if (data &0x01)
{
set_ds1302_io();
}
else
{
clr_ds1302_io();
}
nop();
nop();
set_ds1302_sclk();
nop();
nop();
clr_ds1302_sclk();
nop();
nop();
data >>= 1;
}
clr_ds1302_rst();
nop();
nop();
clr_ds1302_io_ddr();
nop();
nop();
}
uchar ds1302_read(uchar reg)
{
uchar data = 0;
uchar i = 0;
reg += 1; ///读标志
set_ds1302_io_ddr();
nop();
nop();
clr_ds1302_rst();
nop();
nop();
clr_ds1302_sclk();
nop();
nop();
set_ds1302_rst();
nop();
nop();
for (i = 8; i > 0; i--)
{
if (reg &0x01)
{
set_ds1302_io();
}
else
{
clr_ds1302_io();
}
nop();
nop();
set_ds1302_sclk();
nop();
nop();
clr_ds1302_sclk();
nop();
nop();
reg >>= 1;
}
clr_ds1302_io_ddr();
nop();
nop();
for (i = 8; i > 0; i--)
{
data >>= 1;
if (in_ds1302_io())
{
data |= 0x80;
}
nop();
nop();
set_ds1302_sclk();
nop();
nop();
clr_ds1302_sclk();
nop();
nop();
}
clr_ds1302_rst();
nop();
nop();
return (data);
}
uchar check_ds1302(void)
{
ds1302_write(ds1302_control_reg, 0x80);
if (ds1302_read(ds1302_control_reg) == 0x80)
{
return 1;
}
return 0;
}
void ds1302_read_time(void)
{
uchar tmphour;
nowtime.nowyear = BcdToBin(ds1302_read(ds1302_year_reg)); ///年
nowtime.nowmonth = BcdToBin(ds1302_read(ds1302_month_reg)); ///月
nowtime.nowday = BcdToBin(ds1302_read(ds1302_date_reg)); ///日
nowtime.nowweek = BcdToBin(ds1302_read(ds1302_day_reg)); ///周
tmphour = ds1302_read(ds1302_hr_reg); ///时
nowtime.nowhour = (tmphour &0b00100000) / 32 * 20+(tmphour &0x10) / 16 * 10+(tmphour &0x0F);
nowtime.nowminute = BcdToBin(ds1302_read(ds1302_min_reg)); ///分
nowtime.nowsecond = BcdToBin(ds1302_read(ds1302_sec_reg)); ///秒
}
/*!------------------------------------------------------------------------------
2000年~2099年星期算法
-------------------------------------------------------------------------------*/
unsigned char WeekDay20(unsigned char y, unsigned char m, unsigned char d)
{
unsigned char A;
if (m==1||m==2)
{
m+=12;
y--;
}
A= (d+2*m+3* (m+1) /5+y+y/4-y/100+y/400+1) %7;
return A;
}
//===============================================
//名称:Date_Correct()
//描述: 日期时间修正子程序
//参数:
//作者:zhenhua http://blog.csdn.net/zhenhua10
//===============================================
void Date_Correct(struct timechar * bTime)
{
if(bTime->nowyear>99)
bTime->nowyear=99;
if(bTime->nowmonth>12)
bTime->nowmonth =12;
if(bTime->nowmonth==2)
{
if(bTime->nowyear%4==0&&bTime->nowday>29)
bTime->nowday = 29;//闰年
else if(bTime->nowday>28)
bTime->nowday = 28;
}
if(bTime->nowday>31&&(bTime->nowmonth==1||bTime->nowmonth==3||bTime->nowmonth==5||bTime->nowmonth==7||
bTime->nowmonth==8||bTime->nowmonth==10||bTime->nowmonth==12))
bTime->nowday = 31;
if(bTime->nowday>30&&(bTime->nowmonth==4||bTime->nowmonth==5||bTime->nowmonth==6||bTime->nowmonth==9||
bTime->nowmonth==11))
bTime->nowday = 30;
if(bTime->nowmonth==0)
bTime->nowmonth=1;
if(bTime->nowday==0)
bTime->nowday=1;
if(bTime->nowhour>=24)
bTime->nowhour=0;
if(bTime->nowminute>=60)
bTime->nowminute=0;
if(bTime->nowsecond>=60)
bTime->nowsecond=0;
}
void ds1302_write_time(void)
{
uchar bcdyear;
uchar bcdmonth;
uchar bcdday;
uchar bcdhour;
uchar bcdminute;
uchar bcdsecond;
uchar bcdweek;
Date_Correct(&nowtime);//日期时间修正
bcdyear = BinToBcd(nowtime.nowyear);
bcdmonth = BinToBcd(nowtime.nowmonth);
bcdday = BinToBcd(nowtime.nowday);
bcdhour = BinToBcd(nowtime.nowhour);
bcdminute = BinToBcd(nowtime.nowminute);
bcdsecond = BinToBcd(nowtime.nowsecond);
nowtime.nowweek=WeekDay20(nowtime.nowyear,nowtime.nowmonth,nowtime.nowday);
bcdweek = BinToBcd(nowtime.nowweek);
ds1302_write(ds1302_control_reg, 0x00); ///关闭写保护
ds1302_write(ds1302_sec_reg, 0x80); ///暂停
ds1302_write(ds1302_day_reg, bcdweek); ///周
ds1302_write(ds1302_charger_reg, 0xa9); ///涓流充电
ds1302_write(ds1302_year_reg, bcdyear); ///年
ds1302_write(ds1302_month_reg, bcdmonth); ///月
ds1302_write(ds1302_date_reg, bcdday); ///日
ds1302_write(ds1302_hr_reg, bcdhour); ///时
ds1302_write(ds1302_min_reg, bcdminute); ///分
ds1302_write(ds1302_sec_reg, bcdsecond); ///秒
ds1302_write(ds1302_control_reg, 0x80); ///打开写保护
}
以下代码为转载:
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
主程序文件rtc.c
/********************************
AVR单片机I/O口操作DS1302接口程序
文件名:rtc.c
编译:WinAVR-20070525
硬件:ATMEGA169PV 时钟:7372800 Hz
SCLK <---- PE5
CE <---- PE3
IO <---> PE4
此程序在硬件上调试通过!
芯艺设计室 2004-2007 版权所有
转载请保留本注释在内的全部内容
WEB: http://www.chipart.cn
Email: changfutong@sina.com
*******************************/
#include<avr/io.h>
#include<util/delay.h>
//#define BURST_MODE
#define DS1302_DDR DDRE
#define DS1302_PORT PORTE
#define DS1302_PIN PINE
#define DS1302_CK PE5
#define DS1302_IO PE4
#define DS1302_CE PE3
#define delay_bus(x) _delay_loop_1(x*2)
static inline void ds1302_select(void)
{
DS1302_PORT|=_BV(DS1302_CE);
delay_bus(2);
}
static inline void ds1302_unselect(void)
{
DS1302_PORT&=~_BV(DS1302_CE);
delay_bus(2);
}
//实现读一字节时序
static uint8_t ds1302_read_byte(void)
{
uint8_t i,ret=0;
for(i=0;i<8;i++)
{
ret>>=1;
if(DS1302_PIN & _BV(DS1302_IO))
ret|=0x80;
//给一脉冲
DS1302_PORT|=_BV(DS1302_CK);
delay_bus(1);
DS1302_PORT&=~_BV(DS1302_CK);
delay_bus(1);
}
return ret;
}
//实现写一字节时序
static void ds1302_write_byte(uint8_t dat)
{
uint8_t i;
//IO口设置为输出
DS1302_DDR|=_BV(DS1302_IO);
DS1302_PORT&=~_BV(DS1302_IO);
for(i=0;i<8;i++)
{
//设置好数据口
if(dat& 0x01)
DS1302_PORT|=_BV(DS1302_IO);
else
DS1302_PORT&=~_BV(DS1302_IO);
delay_bus(1);
//给一脉冲
DS1302_PORT|=_BV(DS1302_CK);
delay_bus(1);
DS1302_PORT&=~_BV(DS1302_CK);
dat>>=1;
}
//IO口恢复到输入状态
DS1302_PORT&=~_BV(DS1302_IO);
DS1302_DDR&=~_BV(DS1302_IO);
delay_bus(1);
}
//读寄存器 addr: 0~8
uint8_t rtc_read_reg(uint8_t addr)
{
uint8_t ret;
addr<<=1;
addr|=0x81;
ds1302_select();
ds1302_write_byte(addr);
ret=ds1302_read_byte();
ds1302_unselect();
return ret;
}
//写寄存器
void rtc_write_reg(uint8_t addr,uint8_t dat)
{
addr<<=1;
addr|=0x80;
ds1302_select();
ds1302_write_byte(addr);
ds1302_write_byte(dat);
ds1302_unselect();
}
//读RAM
//在多字节(BURST)模式下读取 x 长度的数据到buf指向的存储区
//在单字节模式下从地址为x(0~31)的RAM中读一字节存储到buf处
void rtc_read_ram(uint8_t x,uint8_t *buf)
{
#ifdef BURST_MODE
uint8_t i;
ds1302_select();
ds1302_write_byte(0xff); //read burts ram
for(i=0;i<x;i++)
buf[i]=ds1302_read_byte();
ds1302_unselect();
#else
x<<=1;
x|=0xc1;
ds1302_select();
ds1302_write_byte(x);
*buf=ds1302_read_byte();
ds1302_unselect();
#endif
}
//写RAM
//在多字节(BURST)模式下写 x 长度的数据到DS1302内部RAM,从0地址开始写
//在单字节模式下在地址为x(0~31)的RAM片写入一字节数据
void rtc_write_ram(uint8_t x,uint8_t *buf)
{
#ifdef BURST_MODE
uint8_t i;
#endif
rtc_write_reg(7,0);//写使能
#ifdef BURST_MODE
ds1302_select();
ds1302_write_byte(0xfe); //write burts ram
for(i=0;i<x;i++)
ds1302_write_byte(buf[i]);
ds1302_unselect();
#else
x<<=1;
x|=0xc0;
ds1302_select();
ds1302_write_byte(x);
ds1302_write_byte(*buf);
ds1302_unselect();
#endif
rtc_write_reg(7,0x80);//写禁止
}
//读取时钟 将读取的时钟以小时,分钟,秒的顺序存入time指向的缓冲区
//读出的数据为二进制格式,而非BCD码
void rtc_get_time(uint8_t *time)
{
uint8_t s,m=0,h;
h=rtc_read_reg(2);
if(h & 0x80)//12小时制
{
if(h& _BV(4))
m=10;
else
m=0;
if(h&_BV(5))
m+=12;
h=m+(h&0x0f);
}
else //24小时制
{
h&=0x3f;
h= (h>>4)*10 + (h & 0x0f);
}
s=rtc_read_reg(0) & 0x7f;
m=rtc_read_reg(1) & 0x7f;
time[2]= (s>>4)*10 + (s & 0x0f);
time[1]= (m>>4)*10 + (m & 0x0f);
time[0]= h;
}
//设置时钟,time中的时钟格式为:时,分,秒二进制式
void rtc_set_time(uint8_t *time)
{
uint8_t h,m,s;
h=time[0]%24;
m=time[1]%60;
s=time[2]%60;
//二进制转BCD
h=((h/10)<<4) + (h%10);
m=((m/10)<<4) + (m%10);
s=((s/10)<<4) + (s%10);
rtc_write_reg(7,0);//write enable
rtc_write_reg(0,s);
rtc_write_reg(1,m);
rtc_write_reg(2,h);
rtc_write_reg(7,0x80);//write disable
}
//读日历 将读取的日历以年,月,日的顺序存入date指向的缓冲区
//读出的数据为二进制格式,而非BCD码
void rtc_get_date(uint8_t *date)
{
uint8_t y,m,d;
y=rtc_read_reg(6);
m=rtc_read_reg(4)& 0x1f;
d=rtc_read_reg(3)& 0x3f;
//BCD转二进制
date[0]=(y>>4)*10 + (y&0x0f);
date[1]=(m>>4)*10 + (m&0x0f);
date[2]=(d>>4)*10 + (d&0x0f);
}
//设置日历
void rtc_set_date(uint8_t *date)
{
uint8_t y,m,d;
//二进制转BCD码
y=((date[0]/10)<<4) + (date[0]%10);
m=((date[1]/10)<<4) + (date[1]%10);
d=((date[2]/10)<<4) + (date[2]%10);
//写入DS1302
rtc_write_reg(7,0);
rtc_write_reg(6,y);
rtc_write_reg(4,m);
rtc_write_reg(3,d);
rtc_write_reg(7,0x80);
}
//读周
void rtc_get_day(uint8_t *day)
{
*day=rtc_read_reg(5) & 0x07;
}
//设置周
void rtc_set_day(uint8_t *day)
{
rtc_write_reg(7,0);
rtc_write_reg(5,(*day)&0x07);
rtc_write_reg(7,0x80);
}
//初始化接口和各寄存器
void rtc_init(void)
{
//CE,CK口设置为输出,IO口设置为输入
DS1302_DDR|=_BV(DS1302_CE)|_BV(DS1302_CK);
DS1302_PORT&=~(_BV(DS1302_CE)|_BV(DS1302_CK)|_BV(DS1302_IO));
DS1302_DDR&=~_BV(DS1302_IO);
if(rtc_read_reg(0) & 0x80)//如果处于暂停状态
{
rtc_write_reg(7,0);//写允许
rtc_write_reg(0,0);//运行
rtc_write_reg(7,0x80);//写保护
}
//... ... 这里你可以检查涓流充电配置并设置寄存器
}
包含头文件 rtc.h
//rtc.h
#ifndef RTC_H
#define RTC_H
uint8_t rtc_read_reg(uint8_t addr);
void rtc_write_reg(uint8_t addr,uint8_t dat);
void rtc_read_ram(uint8_t x,uint8_t *buf);
void rtc_write_ram(uint8_t x,uint8_t *buf);
void rtc_get_time(uint8_t *time);
void rtc_set_time(uint8_t *time);
void rtc_get_date(uint8_t *date);
void rtc_set_date(uint8_t *date);
void rtc_get_day(uint8_t *day);
void rtc_set_day(uint8_t *day);
void rtc_init(void);
//注:此套接口函数中表示寄存器或RAM的地址为偏移地址而不是数据手册中的命令字
//如秒寄存器的地址为0 而不是0x80或0x81
#endif
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、