*附件:WanNianLi.zip如何实现一个电子万年历
万年历是从1990年1月1日开始的所有年份的年、月、日、时、分、秒信息。
这个和我们台式电脑上面的日历非常类似,打开电脑右下角的日历

可以看到今天是2025年1月17日
具体时间在右下角

本次测评我就会使用瑞萨RA4E2的任意一个普通的定时器来实现万年历显示 年、月、日、时、分、秒等具体时间功能!不需要额外的外设操作,只需要一个定时器和一个串口,串口用来打印具体的万年历信息,因为我手上目前没有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整除。

具体代码如下:
u8 LeapYear(u16 year)
{
short y = year;
u16 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

具体代码如下:
u8 BigMonth(u8 month)
{
if(month==1 || month==3 || month==5 || month==7 ||
month==8 || month==10 || month==12)
return 1;
return 0;
}
判断某年某月有多少天,具体功能如下:
先设定一个125ms的定时器计时中断,每计时125ms,定时器就会溢出,这样Timer_1s_Count就进行累加,累计8次刚好就是1s定时,然后清零,这时调用万年历年月日时分秒设置函数
void TimeVariable_Processing(void)

按照每分钟60秒,每小时60分钟,每天24小时进行累加进位操作。
然后判断是否是闰年,除去大月,计算2月天数到底是28天还是29天


void TimeVariable_Processing(void)
{
u8 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(u16 Year,u8 Month,u8 Day,u8 Hour,u8 Min,u8 Second)
{
datetime.year = Year;
datetime.month = Month;
datetime.day = Day;
datetime.hour = Hour;
datetime.min = Min;
datetime.second = Second;
}
就是根据当前windows10电脑自带的网络时间,写入这个函数,然后调用定时器里面的TimeVariable_Processing函数进行时间信息的核心算法处理,实现精确的定时操作!最后将及时得到的时间再与windows10桌面的时间信息进行对比,这样就实现了万年历的所有功能。
1.软件的主要流程图如下:

2.定时器中断子程序流程如下:


- 每隔 125ms 进入一次 Time Base 0 中断服务子程序,搭配 RAM 进行计数 8 次,达到计时1s 的功能。
万年历时间变量更新子程序流程如下:


4.当判断万年历的日是否大于当月的天数时,需要注意 1、3、5、7、8、10、12 月天数有 31
天,闰年 2 月天数有 29 天,平年 2 月天数有 28 天,其余月份天数 30 天。
5.年份能被 4 整除、但不能被 100 整除,或能被 400 整除的年份为闰年。
主要用到以下变量


好了,万年历的整个软件流程和核心算法我已经讲解完毕了,现在就来开始进入正题!
首先打开
新建一个工程,取名WanNianLi

选择MCU型号RA4E2





查看原理图可知
串口通道为UART9,IO口引脚分别为P109,P110
返回编辑器RAsmart

配置好usart9通道

中断函数改名为 user_uart_callback
然后生成代码。进入文件夹,
打开KEIL工程,进行串口重定义

添加标准库头文件
#include <stdio.h>

修改代码
fsp_err_t err = FSP_SUCCESS;
volatile bool uart_send_complete_flag = false;
void user_uart_callback (uart_callback_args_t * p_args)
{
if(p_args->event == UART_EVENT_TX_COMPLETE)
{
uart_send_complete_flag = true;
}
if(p_args->event == UART_EVENT_RX_CHAR)
{
R_SCI_UART_Write(&g_uart9_ctrl, (uint8_t *)&(p_args->data), 1);
}
}
#if defined GNUC && !defined clang
int _write(int fd, char *pBuffer, int size);
int _write(int fd, char *pBuffer, int size)
{
(void)fd;
R_SCI_UART_Write(&g_uart9_ctrl, (uint8_t *)pBuffer, (uint32_t)size);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
return size;
}
#else
int fputc(int ch, FILE *f)
{
(void)f;
R_SCI_UART_Write(&g_uart9_ctrl, (uint8_t *)&ch, 1);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
return ch;
}
#endif


err = R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);
assert(FSP_SUCCESS == err);

while (1)循环里面添加测试串口代码
while (1)
{
printf("欢迎来到瑞萨电子\r\n");
}

烧录RA4E2单片机,打开串口助手

串口调试正常。
接下来就开始实现万年历的核心算法了
为了方便起见,我们直接使用cortex-M的systick定时器吧,所有的ARM内核都带有这个定时器

我已经封装好了systick定时器
在hal_systick.c hal_systick.h文件夹里面
在KEIL里添加这两个文件

这是路劲

这是源文件
添加头文件
#include "hal_data.h"
#include <stdio.h>
#include <stdbool.h>
#include "hal_systick.h"



#define TICKS_PER_SECOND 125
代表8ms中断一次





主函数中加入核心代码
err = R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);
assert(FSP_SUCCESS == err);
hal_systick_init();
/**** Set the time variable of the calendar ****/
Calendar_TimeVariable_Set(2025,1,17,14,43,59);
while (1)
{
printf("现在时间为%d年%d月%d日%d时%d分%d秒\\n",datetime.year,datetime.month,datetime.day,datetime.hour,datetime.min,datetime.second);
}
烧录后串口打印
查看现在时间

打开电脑右下边查看现在时间

可以看到两者时间一致,我的万年历就完成了,这是我原创的,希望加入精华帖子,评为优秀
下面附件是我的万年历烧录hex文件,大家可以直接用jlink烧录到板子测试
*附件:WanNianLi.zip