本帖最后由 51小刚 于 2011-7-23 08:58 编辑
首先先了解三个概念:rtc, gps,电波表
前两个就不用我解释了,电波表是机身由原子时钟和无线电接收系统组成,由国家授时中心发出准确时间,通过无线电接收系统接收、经CPU处理后显示时间,电波表30万年误差不会超过一秒。简单的说就是一种接受标准时间信息的电波后,可以自动校对时间的手表
两个相关的链接http://www.hotpower.org/HotAjax/HotPower_HotAjax.html和https://bbs.21ic.com/icview-248981-1-1.html
涉及六个算法
0.世纪内星期算法
3480
1.菜农星期表格
3492
2.菜农星期公式
3500
3.菜农星期第二公式
3500
4.基姆拉尔森星期公式
3516
5.蔡勒完整星期公式
3516
6.蔡勒星期公式(无越界校验)
3508 (此公式不能在计算机实战)
具体大家可看菜农大叔的帖子。以下为群课总结,希望对大家有帮助。
菜农大叔说:
俺以前的感觉,ST的RTC应该是“假的”,ST的RTC我们需要自己处理时间,年月日时分秒,甚至星期,而一般真的RTC,不需要你处理的,甚至闹铃都可以,nxp,nuvoton都是真的,当然还有很多外围模块。
这是例程通过串口输出的字符串,[6]内是星期,(1)是24小时显示模式。
俺为何又要讲星期算法呢???
在任何rtc模块中,一般都是0点翻转,你在其它时间强行写入即改写的,一般不做校验,例如,今天是星期五,你可以输入星期X,新唐的RTC也是这样的不校验,即使0点翻转,故新唐的RTC的星期,为week%7的内部算法,这是其一。有个问题,没有外接电池引脚,这是不好,ds1302,它也是0点翻转校验的,gps的出现,使rtc淡化了,几乎无用了,手机里有的也用gps校准rtc,所以,这种应用rtc就不怕掉电了,所以,这些都需要星期的算法。
难民人:上传电波表图,方便大家理解
中国的长波授时编码标准为BPC。目前该长波授时的时间编码还未正式公开,其专利由西安高华实业有限公司持有。同时该公司也是中国第一台长波授时电波钟的开发者
菜农大叔抽烟后:
我们是东8区,那么,就有2个“断点”,上午8点和下午四点,我们的8点,是伦敦的0点,此后我们是同一天,我们的0点,就是伦敦的下午4点,但是我们进入了新的一天,新的一周,新的一年,新的世纪,新的。。。。gps此时还是伦敦时间,不会翻转,
所以,我们必须把伦敦时间+8小时,对否????
故,我们必须知道星期算法,才能进行换算,故我们的0点,gps不翻转,但是我们必须软翻转,但是在上午8点,gps翻转,故我们的时间也要修正,假若8点我们再翻转就错了,再说电波表,由于陕西天文台不是24小时授时,故需要rtc的支持,但是gps也需要,应该可能在屋内或大树下,信号不好时,BPC是通过长波发送出去的。
为何要星期算法?我们就需要年月日,时分秒呀?星期无用呀?这些人都要被
下面菜农老师讲了例程:
最著名的星期公式有蔡勒和基姆拉尔森两个(俺目前知道的),俺的公式是在没参考任何公式的情况下推导出来的,https://bbs.21ic.com/icview-248985-1-1.html 这个帖子了有俺对他们的推导过程,并且对蔡勒公式进行了修正,因为那是在125年前推导出来的,没有考虑计算机的溢出问题。
void rtc_t::SetWeek(uint32_t Year, uint32_t Month, uint32_t Day)
{
uint32_t DayOfWeek;
#if WEEK_M == 1
//月表=(13*Month+8)/5
static const char WeekTable[]="\x2\x5\x0\x3\x5\x1\x4\x6\x2\x0";
#endif
if (WriteEnable())
{
if (Month < 2)
{
//去年
if (Year > 0) Year --;//2000.3~2099.12
else Year = 9999;
//今年的1月2月是去年的13月14月
// Month += 12;
//今年的1月2月是去年的5月6月
Month += 4;
}
#if WEEK_M == 0
//世纪内星期算法
//工程编译长度合计3480个字节
DayOfWeek = ((Year%100)+((Year%100)>>2)+(13*Month+8)/5+Day)%7;
#else
#if
WEEK_M == 1
//菜农星期表格=((百年%4)*5+年+年/4+月表+日)%7
//月表=(13*Month+8)/5
//工程编译长度合计3492个字节
DayOfWeek = (((Year/100)&3)*5+(Year%100)+((Year%100)>>2)+WeekTable[Month-3]+Day)%7;
#else
#if WEEK_M == 2
//菜农星期公式=((百年&3)*5+年+(年>>2)+(13*月+8)/5+日)%7
//工程编译长度合计3500个字节
DayOfWeek = (((Year/100)&3)*5+(Year%100)+((Year%100)>>2)+(13*Month+8)/5+Day)%7;
#else
#if WEEK_M == 3
//菜农星期公式2=((百年*5)%20+年+(年>>2)+(13*月+8)/5+日)%7
//工程编译长度合计3492个字节
DayOfWeek = (((Year/100)*5)%20+(Year%100)+((Year%100)>>2)+(13*Month+8)/5+Day)%7;
#else
#if WEEK_M == 4
//基姆拉尔森星期公式=(百年/4+百年*5+年+年/4+(13*月+8)/5+日)%7
//工程编译长度合计3516个字节
DayOfWeek = (Year/400+(Year/100)*5+(Year%100)+((Year%100)>>2)+(13*Month+8)/5+Day)%7;
#else
#if WEEK_M == 5
//蔡勒完整星期公式=(203+百年/4-2*百年+年+年/4+(13*月+3)/5+日+1)%7
//工程编译长度合计3516个字节
DayOfWeek = (203+Year/400-(Year/100)*2+(Year%100)+((Year%100)>>2)+(13*Month+8)/5+Day)%7;
#else //WEEK_M == 6
//蔡勒星期公式=(百年/4-2*百年+年+年/4+(13*月+3)/5+日+1)%7 (此公式不能在计算机实战)
//工程编译长度合计3508个字节
DayOfWeek = (Year/400-(Year/100)*2+(Year%100)+((Year%100)>>2)+(13*Month+8)/5+Day)%7;
#endif
#endif
#endif
#endif
#endif
#endif
#if LOOK_H == 0
RTCs.DWR.Bits.DWR = DayOfWeek;
#else
RTC.DWR().DWR = DayOfWeek;
#endif
rtc_t::DayOfWeek = DayOfWeek;
}
}
例程可以通过WEEK_M选择一个,字节最短速度最快的当然是查表法,新唐的 RTC只支持本世纪,严格说一个为2000.3.1~2099.12.31,为什么,大家自己看俺的推导过程就明白了,菜农星期公式=((百年&3)*5+年+(年>>2)+(13*月+8)/5+日)%7,既然是本世纪,百年=20,那么(百年&3)*5=0,故:
//世纪内星期算法
//工程编译长度合计3480个字节
DayOfWeek = ((Year%100)+((Year%100)>>2)+(13*Month+8)/5+Day)%7;
用过外围RTC的人,还有NXP,新唐的 RTC,时间日期都是计算好,我们用时取出即可,不需要我们再去计算了,有人说无外接电池不好,这个是的,但大量的应用是某些的辅助,如辅助gps,电波表等高精度时钟源,这节课,实际是讲RTC的应用,主要是设置问题,
void rtc_t::SetCalender()
{
if (WriteEnable())
{
SetHourMode(1);//DRVRTC_CLOCK_24
SetDate(2011, 7, 15);
SetTime(23, 59, 0);
SetWeek(2011, 7, 15);
SetTickMode(0);
}
}
注意,星期不是外部输入的,而是星期公式算不来的,这样就不容易出错,俺主张,在设置时,只输入日期和时间即可,星期还是算吧,还有,星期是“密码”,可以检验gps协议里日期的真伪,这个例程没有加闹铃中断,新唐BSP里的RTC例程有的
rtc_t中断服务例程
bool rtc_t::isr(int vector)
{
#if LOOK_H == 0
RTCs.RIIR.Bits.TI = 1;
#else
RTC.RIIR().TI = 1;
#endif
return true;
}
void rtc_t::dsr(int vector, uintptr_t count)
{
GetCalender();
sem.do_post();
}
例程采用RTC每秒中断一次,即秒脉冲发生器,void rtc_t::dsr(int vector, uintptr_t count)
{
GetCalender();
sem.do_post();
}
在dsr()读日期,时间等信息,并为任务发送信号量。
难民人<lwslws201@qq.com> 21:59:54
有秒脉冲
还不是跟STm 的RTC一样?
菜农答曰:不一样的,此中断是当前所有实时的信息,不需要你计算的,取出来就是你想要得到的,不过新唐的 RTC里的数据是BCD码,有些晕,估计是受ds1302等影响吧,感觉有些“抄板”的嫌疑~~~,只是猜测。
John Lee:
bcd码对显示比较方便。
雁塔菜农:
是的
雁塔菜农:
LED数码管最好了
雁塔菜农:
应该是这个想法,后头问问国军
雁塔菜农:
void rtc_t::GetCalender()
{
#if LOOK_H == 0
Year = 2000 + RTCs.CLR.Bits.YEAR10 * 10 + RTCs.CLR.Bits.YEAR1;
Month = RTCs.CLR.Bits.MON10 * 10 + RTCs.CLR.Bits.MON1;
Day = RTCs.CLR.Bits.DAY10 * 10 + RTCs.CLR.Bits.DAY1;
Hour = RTCs.TLR.Bits.HR10 * 10 + RTCs.TLR.Bits.HR1;
Minute = RTCs.TLR.Bits.MIN10 * 10 + RTCs.TLR.Bits.MIN1;
Second = RTCs.TLR.Bits.SEC10 * 10 + RTCs.TLR.Bits.SEC1;
DayOfWeek = RTCs.DWR.Bits.DWR;
ClockDisplay = RTCs.TSSR.Bits.HR24;
#else
#if LOOK_H == 1
auto clr = RTC.CLR();
Year = 2000 + clr.YEAR10 * 10 + clr.YEAR1;
Month = clr.MON10 * 10 + clr.MON1;
Day = clr.DAY10 * 10 + clr.DAY1;
DayOfWeek = RTC.DWR().DWR;
auto tlr = RTC.TLR();
Hour = tlr.HR10 * 10 + tlr.HR1;
Minute = tlr.MIN10 * 10 + tlr.MIN1;
Second = tlr.SEC10 * 10 + tlr.SEC1;
ClockDisplay = RTC.TSSR().HR24;
#else
RTC.CLR()
.YEAR10.lambda([&Year](uint32_t y){Year = 2000 + y * 10; return y; })
.YEAR1.lambda([&Year](uint32_t y){ Year += y; return y; })
.MON10.lambda([&Month](uint32_t m){Month = m * 10; return m; })
.MON1.lambda([&Month](uint32_t m){ Month += m; return m; })
.DAY10.lambda([&Day](uint32_t d){Day = d * 10; return d; })
.DAY1.lambda([&Day](uint32_t d){ Day += d; return d; });
DayOfWeek = RTC.DWR().DWR;
RTC.TLR()
.HR10.lambda([&Hour](uint32_t h){Hour = h * 10; return h; })
.HR1.lambda([&Hour](uint32_t h){ Hour += h; return h; })
.MIN10.lambda([&Minute](uint32_t m){Minute = m * 10; return m; })
.MIN1.lambda([&Minute](uint32_t m){ Minute += m; return m; })
.SEC10.lambda([&Second](uint32_t s){Second = s * 10; return s; })
.SEC1.lambda([&Second](uint32_t s){ Second += s; return s; });
ClockDisplay = RTC.TSSR().HR24;
#endif
#endif
}
|