| 
 
| 本帖最后由 yangyurong 于 2025-1-17 17:31 编辑 
 万年历是从1990年1月1日开始的所有年份的年、月、日、时、分、秒信息。这个和我们台式电脑上面的日历非常类似,打开电脑右下角的日历
 可以看到今天是2025年1月17日
 
 具体时间在右下角
 
  本次测评我就会使用G32A1465的任意一个普通的定时器来实现万年历显示 年、月、日、时、分、秒等具体时间功能!不需要额外的外设操作,只需要一个定时器和一个串口,串口用来打印具体的万年历信息,因为我手上目前没有LED显示模块和LCD面板,所以只能使用串口来打印时间。不过这没有关系,只要时间精确,最后打印出来的时间信息可以和wind10电脑右下角的时间进行对比查看时间的准确性。
 万年历说明
 输出指定月的月份,需要了解每个月份的相关条件:
 1.该月有多少天
 2.该月的1号是星期几(求距离1990.1.1的总天数)
 3.如何将当月日期一一对应星期
 解决了这三个问题,就可以打印出指定年份的月的日历了。
 
 一、求某年某月里该月份里面有多少天
 因为每个月份可能天数是不相同的,这里有涉及到闰年的问题。
 整体来说1,3,5,7,8,10,12,这几个月***是31天。称为大月。
 大月就是该月有31天,小月就是有30天,基本是不需要年份就可以判断该月的天数的,唯独一个二月,二月就需要年份的判断。二月是一个小月,按前面来说应该是30天的,但是就是它不同,2月在闰年的时候是29天,在平年的时候是28天。这样的话就需要判断该年是闰年还是平年。就可以知道指定月里有多少天数了。
 1.判断某年是闰年和平年
 如果某年是闰年的话,该年一共有366天,因为2月是29天。平年的话,就是365天。
 闰年是啥:
 
 1.该年份能被4整除且不能被100整除。或  2.该年份能被400整除。
 uint8_t LeapYear(uint16_t year)
 {
 uint16_t y = year;
 uint16_t remainder;
 
 remainder = y%4;
 if(!remainder)
 {
 remainder = y%100;
 if(remainder) return 1;
 else
 {
 remainder = y%400;
 if(remainder) return 0;
 }
 return 1;
 }
 return 0;
 }
 2.求出某年某月有多少天
 首先对大月进行判断
 
 1,3,5,7,8,10,12是大月,***是31天
 uint8_t BigMonth(uint8_t month)
 {
 if(month==1 || month==3 || month==5 || month==7 ||
 month==8 || month==10 || month==12)
 return 1;
 return 0;
 }
 uint8_t LeapYear(uint16_t year)
 {
 uint16_t y = year;
 uint16_t remainder;
 
 remainder = y%4;
 if(!remainder)
 {
 remainder = y%100;
 if(remainder) return 1;
 else
 {
 remainder = y%400;
 if(remainder) return 0;
 }
 return 1;
 }
 return 0;
 }
 判断某年某月有多少天,具体功能如下:
 
 先设定一个1ms的定时器计时中断,每计时1ms,定时器就会溢出,这样Timer_1s_Count就进行累加,累计1000次刚好就是1s定时,然后清零,这时调用万年历年月日时分秒设置函数
 void TimeVariable_Processing(void)
 {
 uint8_t day;
 day = datetime.day;
 
 datetime.second += 1;
 if(datetime.second>=60)
 {
 datetime.second -= 60;
 datetime.min++;
 if(datetime.min>=60)
 {
 datetime.min -= 60;
 datetime.hour++;
 if(datetime.hour>=24)
 {
 datetime.hour -= 24;
 day = (++datetime.day);
 if(BigMonth(datetime.month))
 {
 if(day>=32)
 {
 datetime.day-=31;
 day=0xff;
 }
 }
 else
 {
 if(datetime.month==2)
 {
 if(LeapYear(datetime.year))
 {
 if(day>=30)
 {
 datetime.day-=29;
 day=0xff;
 }
 }
 else if(day>=29)
 {
 datetime.day-=28;
 day=0xff;
 }
 }
 else if(day>=31)
 {
 datetime.day-=30;
 day=0xff;
 }
 }
 if(day==0xff)
 {
 datetime.month++;
 if(datetime.month>=13)
 {
 datetime.month -= 12;
 datetime.year++;
 }
 }
 else datetime.day=day;
 }
 }
 }
 
 }
 为了计算出准确的时间信息,我还为此专门设计了一个设定年月日时分秒的函数,通过它可以校验初始时间
 void Calendar_TimeVariable_Set(uint16_t Year,uint8_t Month,uint8_t Day,uint8_t Hour,uint8_t Min,uint8_t Second)
 {
 datetime.year = Year;
 datetime.month = Month;
 datetime.day = Day;
 datetime.hour = Hour;
 datetime.min = Min;
 datetime.second = Second;
 }
 就是根据当前windows10电脑自带的网络时间,写入这个函数,然后调用定时器里面的TimeVariable_Processing函数进行时间信息的核心算法处理,实现精确的定时操作!最后将及时得到的时间再与windows10桌面的时间信息进行对比,这样就实现了万年历的所有功能。
 4.当判断万年历的日是否大于当月的天数时,需要注意 1、3、5、7、8、10、12 月天数有 31
 天,闰年 2 月天数有 29 天,平年 2 月天数有 28 天,其余月份天数 30 天。
 
 5.年份能被 4 整除、但不能被 100 整除,或能被 400 整除的年份为闰年。好了,花不多说,我们直接上干货!!!!!!!!!!!!!!!!!!
 打开demo文件夹里的一个定时器工程即可,我们选择G32A1xxx_SDK_V1.1\Examples\G32A1465\CFGTMR\CFGTMR_PeriodicInterrupt
 CFGTMR_PeriodicInterrupt这个定时器周期性中断工程
 打开keil
 main文件修改代码如下
 /* Includes */
 #include "user_config.h"
 #include "board.h"
 #include <stdio.h>
 
 
 
 
 
 #define TICKS_PER_SECOND 1000
 
 
 typedef struct{
 uint16_t        year;
 uint8_t                month;
 uint8_t                day;
 uint8_t                hour;
 uint8_t                min;
 uint8_t                second;
 } TimeStamp;
 
 
 volatile uint16_t Timer_1s_Count;
 
 
 void Calendar_TimeVariable_Set(uint16_t Year,uint8_t Month,uint8_t Day,uint8_t Hour,uint8_t Min,uint8_t Second);
 uint8_t BigMonth(uint8_t month);
 uint8_t LeapYear(uint16_t year);
 void TimeVariable_Processing(void);
 
 
 TimeStamp datetime;
 
 
 void Calendar_TimeVariable_Set(uint16_t Year,uint8_t Month,uint8_t Day,uint8_t Hour,uint8_t Min,uint8_t Second)
 {
 datetime.year = Year;
 datetime.month = Month;
 datetime.day = Day;
 datetime.hour = Hour;
 datetime.min = Min;
 datetime.second = Second;
 }
 
 
 uint8_t BigMonth(uint8_t month)
 {
 if(month==1 || month==3 || month==5 || month==7 ||
 month==8 || month==10 || month==12)
 return 1;
 return 0;
 }
 
 
 uint8_t LeapYear(uint16_t year)
 {
 uint16_t y = year;
 uint16_t remainder;
 
 remainder = y%4;
 if(!remainder)
 {
 remainder = y%100;
 if(remainder) return 1;
 else
 {
 remainder = y%400;
 if(remainder) return 0;
 }
 return 1;
 }
 return 0;
 }
 
 
 void TimeVariable_Processing(void)
 {
 uint8_t day;
 day = datetime.day;
 
 datetime.second += 1;
 if(datetime.second>=60)
 {
 datetime.second -= 60;
 datetime.min++;
 if(datetime.min>=60)
 {
 datetime.min -= 60;
 datetime.hour++;
 if(datetime.hour>=24)
 {
 datetime.hour -= 24;
 day = (++datetime.day);
 if(BigMonth(datetime.month))
 {
 if(day>=32)
 {
 datetime.day-=31;
 day=0xff;
 }
 }
 else
 {
 if(datetime.month==2)
 {
 if(LeapYear(datetime.year))
 {
 if(day>=30)
 {
 datetime.day-=29;
 day=0xff;
 }
 }
 else if(day>=29)
 {
 datetime.day-=28;
 day=0xff;
 }
 }
 else if(day>=31)
 {
 datetime.day-=30;
 day=0xff;
 }
 }
 if(day==0xff)
 {
 datetime.month++;
 if(datetime.month>=13)
 {
 datetime.month -= 12;
 datetime.year++;
 }
 }
 else datetime.day=day;
 }
 }
 }
 
 }
 
 int main(void)
 {
 CFGTMR_STATE_T cfgtmr0State;
 
 /* Initialize clock */
 CLOCK_SYS_Init(&g_clockConfig);
 
 /* Initialize pins */
 PINS_Init(NUM_OF_CONFIGURED_PINS0, g_pinsConfig);
 
 /* Initialize LEDs */
 LED_Init();
 LED_On(LED_GREEN);
 
 /* Initialize Buttons */
 BTN_Init();
 
 /* Initialize UART */
 COM_Init();
 
 /* Initialize CFGTMR */
 CFGTMR_Init(CFGTMR0_INSTANCE, &g_cfgtmr0InitConfig, &cfgtmr0State);
 
 /* Initialize counter */
 CFGTMR_InitCounter(CFGTMR0_INSTANCE, &g_cfgtmr0TimerParamConfig);
 
 /* Start Counter */
 CFGTMR_StartCounter(CFGTMR0_INSTANCE);
 
 
 /**** 设置万年历开始时间 ****/
 Calendar_TimeVariable_Set(2025,1,17,24,30,00);
 
 
 while (1)
 {
 printf("现在时间为%d年%d月%d日%d时%d分%d秒\n",datetime.year,datetime.month,datetime.day,datetime.hour,datetime.min,datetime.second);
 
 }
 }
 
 /*!
 * @brief   CFGTMR0 Counter overflow and Reload IRQ handler.
 *
 * @param   None
 *
 * @retval  None
 */
 void CFGTMR0_Ovf_Reload_IRQHandler(void)
 {
 static uint32_t cnt = 0;
 
 if (CFGTMR_ReadStatusFlags(CFGTMR0_INSTANCE) & CFGTMR_TIME_OVERFLOW_FLAG)
 {
 if (++cnt == 1000U)
 {
 TimeVariable_Processing();
 LED_Toggle(LED_GREEN);//绿灯每1秒钟跳转一次
 cnt = 0;
 }
 }
 
 CFGTMR_ClearStatusFlags(CFGTMR0_INSTANCE, (uint32_t)CFGTMR_TIME_OVERFLOW_FLAG);
 }
 
 
  
  烧录后串口打印
 
 查看现在时间
 
  打开电脑右下边查看现在时间 
  可以看到两者时间一致,我的万年历就完成了,这是我原创的,
 下面附件是我的万年历烧录hex文件,大家可以直接用jlink烧录到板子测试
 
 
 
 
 | 
 
万年历串口时间   
桌面时间   
万年历定时器算法   
非常准确的万年历   |