📄 calendar.c
字号:
#include "calendar.h"
//移植网友的代码
//超强的日历,支持农历,24节气几乎所有日历的功能
//日历时间以1970年为元年,用32bit的时间寄存器可以运行到2100年左右
//正点原子@SCUT
//V1.0
//下部分数据是农历部分要使用的
//月份数据表
u8 const day_code1[9]={0x0,0x1f,0x3b,0x5a,0x78,0x97,0xb5,0xd4,0xf3};
unsigned short const day_code2[3]={0x111,0x130,0x14e};
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表
u8 const *sky[10]= {"甲","乙","丙","丁","戊","己","庚","辛","壬","癸",};//天干
u8 const *earth[12]={"子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥",};//地支
u8 const *monthcode[12]={"一","二","三","四","五","六","七","八","九","十","冬","腊",};//农历月份
u8 const *nongliday[4]={"初","十","廿","三",};//农历日期
tm timer;//时钟结构体
//实时时钟配置
//BKP->DR1 bit3 时钟是否要重设?
void rtc_init(void)
{
//检查是不是第一次配置时钟
u8 temp;
temp=FM24C16_ReadOneByte(52);
if(temp&0X02)//系统继续计时
{
printf("TIME Init OK!\n");
//NVIC_RTCConfiguration();//RTC中断使能
//while(!(RTC->CRL&(1<<3)));//等待RTC寄存器同步
//RTC->CRH|=0X01; //允许秒中断
//while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
}else //第一次配置(未开启秒中断!!)
{
printf("TIME need INIT!\n");
//NVIC_RTCConfiguration(); //RTC中断使能
RCC->APB1ENR|=1<<28; //使能电源时钟
RCC->APB1ENR|=1<<27; //使能备份时钟
PWR->CR|=1<<8; //取消备份区写保护
RCC->BDCR|=1<<16; //备份区域软复位
RCC->BDCR&=~(1<<16); //备份区域软复位
RCC->BDCR|=1<<0; //开启外部低速振荡器
while(!(RCC->BDCR&0X02));//等待外部时钟就绪
RCC->BDCR|=1<<8; //LSI作为RTC时钟
RCC->BDCR|=1<<15;//RTC时钟使能
RTC->PRLH=0X0000;
RTC->PRLL=32770; //时钟周期设置(有待观察,看是否跑慢了?)理论值:32767
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
RTC->CRL|=1<<4; //允许配置
rtc_set(2009,4,19,23,59,55);//设置时间
RTC->CRL&=~(1<<4);//配置更新
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
temp|=0x02;//标记时钟被修改了
FM24C16_WriteOneByte(52,temp);
}
rtc_get();//更新时间
}
//RTC时钟中断
//每秒触发一次
void RTC_IRQHandler(void)
{
u16 RTCCRL;
RTCCRL=RTC->CRL;//读取FLAG
if(RTCCRL&0x0001)//秒钟中断
{
rtc_get();//更新时间
printf("sec ok\n");
}
if(RTCCRL&0x0002)//闹钟中断
{
RTC->CRL&=~(0x0002);//清闹钟中断
Alarm_Process();
//闹钟处理
}
RTC->CRL&=0X0FFA; //清除溢出,秒钟中断标志
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
}
//月份 1 2 3 4 5 6 7 8 9 10 11 12
//闰年 31 29 31 30 31 30 31 31 30 31 30 31
//非闰年 31 28 31 30 31 30 31 31 30 31 30 31
//输入:年份
//输出:该年份是不是闰年.1,是.0,不是
u8 Is_Leap_Year(u16 year)
{
if(year%4==0) //必须能被4整除
{
if(year%100==0)
{
if(year%400==0)return 1;//如果以00结尾,还要能被400整除
else return 0;
}else return 1;
}else return 0;
}
//设置时钟
//把输入的时钟转换为秒钟
//以1970年1月1日为基准
//1970~2099年为合法年份
//平年的月份日期表
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
u8 rtc_set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
u16 t;
u32 seccount=0;
if(syear<1970||syear>2099)return 0;
for(t=1970;t<syear;t++) //把所有年份的秒钟相加
{
if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
else seccount+=31536000; //平年的秒钟数
}
smon-=1;
for(t=0;t<smon;t++) //把前面月份的秒钟数相加
{
seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
}
seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加
seccount+=(u32)hour*3600;//小时秒钟数
seccount+=(u32)min*60; //分钟秒钟数
seccount+=sec;//最后的秒钟加上去
//设置时钟
RCC->APB1ENR|=1<<28;//使能电源时钟
RCC->APB1ENR|=1<<27;//使能备份时钟
PWR->CR|=1<<8; //取消备份区写保护
//上面三步是必须的!
RTC->CRL|=1<<4; //允许配置
RTC->CNTL=seccount&0xffff;
RTC->CNTH=seccount>>16;
RTC->CRL&=~(1<<4);//配置更新
while(!(RTC->CRL&(1<<5)));//等待RTC寄存器操作完成
return 1;
}
//从当前秒钟值,得到时间.
//返回值:0,失败;1,成功
u8 rtc_get(void)
{
u32 timecount=0;
u32 temp=0;
u16 temp1=0;
timecount=RTC->CNTH;//得到计数器中的值(秒钟数)
timecount<<=16;
timecount+=RTC->CNTL;
temp=timecount/86400; //得到天数(秒钟数对应的)
temp1=1970; //从1970年开始
while(temp>=365)
{
if(Is_Leap_Year(temp1))//是闰年
{
if(temp>=366)temp-=366;//闰年的秒钟数
else {temp1++;break;}
}
else temp-=365; //平年
temp1++;
}
timer.w_year=temp1;//得到年份
temp1=0;
while(temp>=28)//超过了一个月
{
if(Is_Leap_Year(timer.w_year)&&temp1==1)//当年是不是闰年/2月份
{
if(temp>=29)temp-=29;//闰年的秒钟数
else break;
}
else
{
if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
else break;
}
temp1++;
}
timer.w_month=temp1+1;//得到月份
timer.w_date=temp+1; //得到日期
temp=timecount%86400; //得到秒钟数
timer.hour=temp/3600; //小时
timer.min=(temp%3600)/60; //分钟
timer.sec=(temp%3600)%60; //秒钟
return 1;
}
///////////////////////////////////////////////////////////////////////
//支持从1900年到2099年的农历查询
//支持从2000年到2050年的节气查询
//子函数,用于读取数据表中农历月的大月或小月,如果该月为大返回1,为小返回0
u8 GetMoonDay(u8 month_p,unsigned short table_addr)
{
switch (month_p)
{
case 1:
if((year_code[table_addr]&0x08)==0) return(0);
else return(1);
case 2:
if((year_code[table_addr]&0x04)==0) return(0);
else return(1);
case 3:
if((year_code[table_addr]&0x02)==0) return(0);
else return(1);
case 4:
if((year_code[table_addr]&0x01)==0) return(0);
else return(1);
case 5:
if((year_code[table_addr+1]&0x80)==0) return(0);
else return(1);
case 6:
if((year_code[table_addr+1]&0x40)==0) return(0);
else return(1);
case 7:
if((year_code[table_addr+1]&0x20)==0) return(0);
else return(1);
case 8:
if((year_code[table_addr+1]&0x10)==0) return(0);
else return(1);
case 9:
if((year_code[table_addr+1]&0x08)==0) return(0);
else return(1);
case 10:
if((year_code[table_addr+1]&0x04)==0) return(0);
else return(1);
case 11:
if((year_code[table_addr+1]&0x02)==0) return(0);
else return(1);
case 12:
if((year_code[table_addr+1]&0x01)==0) return(0);
else return(1);
case 13:
if((year_code[table_addr+2]&0x80)==0) return(0);
else return(1);
}
return(0);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称:GetChinaCalendar
//功能描述:公农历转换(只允许1901-2099年)
// 输 入: year 公历年
// month 公历月
// day 公历日
// p 储存农历日期地址
// 输 出: 1 成功
// 0 失败
/////////////////////////////////////////////////////////////////////////////////////////////////////////
u8 GetChinaCalendar(u16 year,u8 month,u8 day,u8 *p)
{
u8 temp1,temp2,temp3,month_p,yearH,yearL;
u8 flag_y;
unsigned short temp4,table_addr;
yearH=year/100; yearL=year%100;//年份的高低两个字节
if((yearH!=19)&&(yearH!=20))return(0);//日期不在19xx ~ 20xx 范围内,则退出
// 定位数据表地址
if(yearH==20) table_addr=(yearL+100-1)*3;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -