上期我已经讲解了实现地奇星的RA6E2串口输入打印测试,这期来开始整点高端的东西,我想实现一个万年历,就是2025年12月8日,17:33:40秒,星期一,想实现这样的效果,还要使用串口来发送指令进行校准时间,我发现论坛目前没有人实现过串口校时的应用,所以我来实现这个测试,比较有技术含量的东西!!!!!!
可以精确到秒级别,无论任何万年历这种电子产品,每当它掉电停机重启后,如果没有开机联网它的时间就会停留在上次停机时的转态,而不是最新的时间,这时候就需要开机重新进行校时了,比如当前时间是2025年12月8日,17:38:00秒,我就在串口助手里输入如下指令:
set_date:1,2025/12/8,17:38:00
这是我自定义的指令,大家可以自己模仿去实现
然后就能精确的通过串口助手发送指令进行校时了,原理就是这么回事。
开机还是先上个美女镇楼吧!为无聊的生活添加点乐趣

最后效果如下:

主要代码如下:
#include "usart0.h"
#include <ctype.h>
#include "Systick.h"
uint8_t U1_RxBuff[RXBUFFLENGTH];
uint16_t U1_Rxlen = 0;
uint16_t U1_RxlencntPre = 0;
static volatile bool uart_send_complete_flag = false;
void UART0_Init(void)
{
fsp_err_t err = FSP_SUCCESS;
err = R_SCI_UART_Open(&g_uart0_ctrl,&g_uart0_cfg);
assert(err == FSP_SUCCESS);
}
void uart0_Send_Byte(uint8_t ch)
{
/* 发送一个字节数据到UART */
R_SCI_UART_Write(g_uart0.p_ctrl, (uint8_t *)&ch, 1);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
}
void uart0_Send_Bytes(uint8_t *data, uint32_t len)
{
/* 发送一个字节数据到UART */
R_SCI_UART_Write(g_uart0.p_ctrl, data, len);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
}
void uart0_Send_String_Length(uint8_t *str,uint32_t strlen)
{
unsigned int k=0;
do
{
uart0_Send_Byte (* (str + k));
k++;
} while(k < strlen);
}
void uart9_Send_String(uint8_t *str)
{
unsigned int k=0;
do
{
uart0_Send_Byte (* (str + k));
k++;
} while(*(str + k)!='\0');
}
void user_uart_clear(void)
{
memset(U1_RxBuff, 0, sizeof(U1_RxBuff));
U1_Rxlen = 0;
}
uint8_t user_uart_wait_receive(void)
{
if(U1_Rxlen == 0)
return REV_WAIT;
if(U1_Rxlen == U1_RxlencntPre)
{
U1_Rxlen = 0;
return REV_OK;
}
U1_RxlencntPre = U1_Rxlen;
return REV_WAIT;
}
// Single byte for the interrupt handler
volatile uint8_t g_rx_byte;
// Command buffer and index
char g_command_buffer[MAX_COMMAND_LENGTH + 1];
volatile uint8_t g_command_ready_flag = 0;
static void parse_command(void) {
int day, month;
int year;
int hour, min, sec;
int week;
if (strcmp(g_command_buffer, "sleep") == 0) {
printf("SERIAL COMMAND: Entering sleep immediately.\\r\\n");
return;
}
{
if ((day >= 1 && day <= 31) && (month >= 1 && month <= 12) &&
(year >= 00 && year <= 2099) && (week >= 1 && week <= 7) &&
(hour >= 0 && hour <= 23) && (min >= 0 && min <= 59) && (sec >= 0 && sec <= 59))
{
RTC_Time_Set(year,month,day,hour,min,sec,week);
printf("\\r\\n通过串口校准时间:\\r\\n");
printf("New Date and Time set: %02d,%02d/%02d/%02d %02d:%02d:%02d\\n\\r", week,year, month, day, hour, min, sec);
}
else
{
printf("\\n\\r");
printf("Invalid date or time format.\\n\\r");
}
return;
}
else
{
printf("SERIAL ERROR: Unknown command '%s'.\\r\\n", g_command_buffer);
printf("\\n\\r");
printf("Invalid input format. Expected: set_date:WW,DD/MM/YY,HH:MM:SS\\n\\r");
return;
}
}
/**
-
[url=home.php?mod=space&uid=2666770]@Brief[/url] Checks the flag and executes the command parser if input is ready.
*/
void handle_serial_input(void) {
if (g_command_ready_flag) {
parse_command();
g_command_ready_flag = 0; // Clear flag for next command
}
}
volatile uint8_t BitFlag = 0;
volatile uint32_t counter_tick = 0;
unsigned int t_flag = 0;
unsigned int g_year = 2019;
unsigned char g_month = 7;
unsigned char g_day = 5;
unsigned char g_day_old;
unsigned char g_weekly = 1;
unsigned char hour = 17;
unsigned char min = 59;
unsigned char sec = 50;
void RTC_Time_Set(uint16_t Year,uint8_t Month,uint8_t Day,uint8_t Hour,uint8_t Min,uint8_t Second,uint8_t weekly)
{
g_year = Year;
g_month = Month;
g_day = Day;
g_weekly = weekly;
hour = Hour;
min = Min;
sec = Second;
}
void SoftwareRTC(void)
{
if(BitFlag == 0)
return;
sec++;
if(sec == 60)
{
sec = 0;
min++;
}
if(min == 60)
{
min=0;
hour++;
}
if(hour == 24)
{
hour = 0;
g_day++;
g_weekly++;
}
//printf("hr.:%d :min: %d: sec: %d\n\r",hour,min,sec);
printf("现在时间是:");
printf("%4d年%02d月%02d日%02d时%02d分%02d秒,星期%d\r\n",g_year,g_month,g_day,hour,min,sec,g_weekly);
BitFlag = 0;
}
int isLeapYear(int year)
{
return (year % 400 == 0) || ((year % 4 ==0) && (year % 100 !=0));
}
void SoftwareYMD(void)
{
int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
days[1] += isLeapYear(g_year);
if(g_day == g_day_old)
return;
g_day_old=g_day;
if(g_day > days[g_month-1])
{
g_day=1;
g_month++;
}
if(g_weekly > 7) /* 7 is sum day */
g_weekly=1;
if(g_month>12)
{
g_month=1;
g_year++;
}
//printf("Y:%d, M:%d, D:%d, Weekly:%d\\n\\r",g_year,g_month,g_day,g_weekly);
}
void uart0_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)
{
static uint8_t buffer_index = 0;
g_rx_byte = (uint8_t)p_args->data;
if (g_rx_byte == '\\r' || g_rx_byte == '\\n') {
if (buffer_index > 0) {
g_command_buffer[buffer_index] = '\\0';
g_command_ready_flag = 1;
}
buffer_index = 0;
}
else if (buffer_index < MAX_COMMAND_LENGTH) {
g_command_buffer[buffer_index++] = tolower(g_rx_byte);
} else {
buffer_index = 0;
}
}
}
void SysTick_Handler(void)
{
g_tick_count++;
counter_tick++;
if(counter_tick == 1000)
{
counter_tick = 0;
BitFlag = 1;
}
/* USER CODE END SysTick_IRQn 1 */
}
#if 1
/* 重定向 printf 输出 */
#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_uart0_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_uart0_ctrl, (uint8_t *)&ch, 1);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
return ch;
}
#endif
#endif
#ifndef __usart0_H
#define __usart0_H
#include "hal_data.h"
#include <stdio.h>
#define RXBUFFLENGTH 5000
#define REV_OK 0 //接收完成标志
#define REV_WAIT 1 //接收未完成标志
extern uint8_t U1_RxBuff[RXBUFFLENGTH];
extern uint16_t U1_Rxlen;
extern uint16_t U1_RxlencntPre;
#define MAX_COMMAND_LENGTH 4096
#define RX_BUFFER_SIZE 64
extern volatile uint8_t g_rx_byte;
extern char g_command_buffer[MAX_COMMAND_LENGTH + 1];
extern volatile uint8_t g_command_ready_flag;
extern volatile uint8_t BitFlag;
extern volatile uint32_t counter_tick;
void RTC_Time_Set(uint16_t Year,uint8_t Month,uint8_t Day,uint8_t Hour,uint8_t Min,uint8_t Second,uint8_t weekly);
void SoftwareRTC(void);
int isLeapYear(int year);
void SoftwareYMD(void);
void UART0_Init(void);
void uart0_Send_Byte(uint8_t ch);
void uart0_Send_Bytes(uint8_t *data, uint32_t len);
void uart0_Send_String_Length(uint8_t *str,uint32_t strlen);
void uart0_Send_String(uint8_t *str);
void user_uart_clear(void);
uint8_t user_uart_wait_receive(void);
void handle_serial_input(void);
#endif
主函数如下:
#include "hal_data.h"
#include "main.h"
FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
/*******************************************************************************************************************//**
-
main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
-
is called by main() when no RTOS is used.
**********************************************************************************************************************/
void hal_entry(void)
{
/* TODO: add your own code here */
UART0_Init();
hal_systick_init();
printf("\r\n-----软件万年历-----\r\n\r\n");
RTC_Time_Set(2025,12,8,17,20,20,1);//设置默认时间
while(1)
{
handle_serial_input();
SoftwareRTC();
SoftwareYMD();
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
/*******************************************************************************************************************//**
-
This function is called at various points during the startup process. This implementation uses the event that is
-
called right before main() to set up the pins.
-
@param[in] event Where at in the start up process the code is currently at
**********************************************************************************************************************/
void R_BSP_WarmStart (bsp_warm_start_event_t event)
{
if (BSP_WARM_START_RESET == event)
{
#if BSP_FEATURE_FLASH_LP_VERSION != 0
R_FACI_LP->DFLCTL = 1U;
#endif
}
if (BSP_WARM_START_POST_C == event)
{
R_IOPORT_Open(&IOPORT_CFG_CTRL, &IOPORT_CFG_NAME);
#if BSP_CFG_SDRAM_ENABLED
R_BSP_SdramInit(true);
#endif
}
}
#if BSP_TZ_SECURE_BUILD
FSP_CPP_HEADER
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ();
/* Trustzone Secure Projects require at least one nonsecure callable function in order to build (Remove this if it is not required to build). */
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ()
{
}
FSP_CPP_FOOTER
#endif



可以做到分毫不差,关键是自己操作问题,设置好时间即可
详情看视频
新增校时步骤:
1。打开串口助手

现在时间是:2025年12月08日17时23分53秒,星期1
而网络时间是:18:43

因为我刚刚断电了,现在时间慢了,不准确了,需要使用串口校准时间
在串口助手里输入
set_date:1,2025/12/8,18:44:22

可以看到现在时间和网络时间同步了,校时完成


精确到秒级的串口校时万年历。