1. 前言
本次实验内容是调用RTC实时时钟,实现日历、串口设定时间的功能。
2. 硬件部分
RTC
RTC 时钟模块是一个时间外设,主要用于日期时间的存储和控制,有别于一般 MCU中的 Timer,RTC 时钟有两种计时模式,日期模式和计时模式,RTC 常见的操作包括设置时间、设置定时闹铃、配置周期性中断以及启动或停止操作。
3. 软件部分
3.1 配置RTC
选择Stacks选项卡
| 序号 |
操作 |
|---|
| 1 |
点击界面下方标签栏中的Stacks标签,进入堆栈配置页面。 |
| 2 |
在HAL/Common Stacks区域,点击New Stack按钮。 |
| 3 |
在弹出菜单中,选择Timers选项。 |
| 4 |
在Timers子菜单中,选择Realtime Clock (r_rtc)。 |

| 序号 |
操作 |
|---|
| 1 |
在Settings设置区域的Module g_rtc0 Realtime Clock (r_rtc)部分,找到Clock Source,将其设置为Sub-Clock。 |
| 2 |
在Settings设置区域的Module g_rtc0 Realtime Clock (r_rtc)部分,设置Callback为rtc_callback。 |
| 3 |
在Settings设置区域的Module g_rtc0 Realtime Clock (r_rtc)部分,分别设置Alarm Interrupt Priority、Period Interrupt Priority、Carry Interrupt Priority为Priority 2。 |

配置完成后,生成项目代码。
3.2编写代码
将先前的项目模板复制一份,重命名为03_RTC
3.2.1 新建rtc.h
新建文件rtc.h,加入以下代码
#ifndef RTC_H_
#define RTC_H_
#include "hal_data.h"
extern rtc_time_t set_time;
extern volatile bool rtc_flag;
#endif
3.2.2 新建rtc.c
这段代码定义一个set_time结构体,用于在主函数中配置,同时实现了rtc_callback回调函数,用于每1秒中断一次使用
#include "hal_data.h"
#include "rtc.h"
rtc_time_t set_time = {
.tm_sec = 59,
.tm_min = 59,
.tm_hour = 23,
.tm_mday = 28,
.tm_mon = 1,
.tm_year = 125,
};
volatile bool rtc_flag = false;
void rtc_callback(rtc_callback_args_t *p_args)
{
if (p_args->event == RTC_EVENT_PERIODIC_IRQ)
rtc_flag = true;
}
3.2.3 修改hal_entry.c
在文件开头加入代码
#include "rtc.h"
rtc_time_t get_time;
在hal_entry函数中加入代码,主要功能是初始化了rtc、配置时间以及解析串口接收到的设定时间的信息
g_rtc0.p_api->open(&g_rtc0_ctrl, &g_rtc0_cfg);
g_rtc0.p_api->calendarTimeSet(&g_rtc0_ctrl, &set_time);
g_rtc0.p_api->periodicIrqRateSet(&g_rtc0_ctrl, RTC_PERIODIC_IRQ_SELECT_1_SECOND);
while (1)
{
if (rtc_flag)
{
g_rtc0.p_api->calendarTimeGet(&g_rtc0_ctrl, &get_time);
rtc_flag = 0;
printf("%d年%d月%d日 %d:%d:%d\n",
get_time.tm_year + 1900, get_time.tm_mon +1, get_time.tm_mday,
get_time.tm_hour, get_time.tm_min, get_time.tm_sec);
}
if (uart_rx_complete_flag)
{
char * time;
uart_rx_complete_flag = 0;
if(strncmp(rx_data, "time:", 5) == 0){
time = rx_data+5;
set_time.tm_year = ((time[0]-'0')*1000)+((time[1]-'0')*100)+
((time[2]-'0')*10)+(time[3]-'0')-1900;
set_time.tm_mon = ((time[4]-'0')*10)+(time[5]-'0') - 1;
set_time.tm_mday = ((time[6]-'0')*10)+(time[7]-'0');
set_time.tm_hour = ((time[8]-'0')*10)+(time[9]-'0');
set_time.tm_min = ((time[10]-'0')*10)+(time[11]-'0');
set_time.tm_sec = ((time[12]-'0')*10)+(time[13]-'0');
g_rtc0.p_api->calendarTimeSet(&g_rtc0_ctrl, &set_time);
}
}
}
3.2.4 修改debug_bsp_uart.c
改动部分比较多,为了方便起见,把该文件的内容都放在下方了。
#include "debug_bsp_uart.h"
volatile int uart_send_complete_flag = 0;
volatile int uart_rx_complete_flag = 0;
char rx_data[1024];
volatile uint16_t cnt;
uint8_t stat = 0;
void Debug_UART9_Init(void)
{
fsp_err_t err = FSP_SUCCESS;
err = R_SCI_UART_Open(&g_uart9_ctrl, &g_uart9_cfg);
assert(FSP_SUCCESS == err);
}
void debug_uart9_callback(uart_callback_args_t *p_args)
{
switch (p_args->event)
{
case UART_EVENT_RX_CHAR:
{
rx_data[cnt++] = (char)p_args->data;
if (stat == 0 && p_args->data == '\r')
{
stat = 1;
}
else if (stat == 1 && p_args->data == '\n')
{
uart_rx_complete_flag = 1;
cnt = 0;
}
else
{
stat = 0;
}
break;
}
case UART_EVENT_TX_COMPLETE:
{
uart_send_complete_flag = 1;
break;
}
default:
break;
}
}
#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 == 0)
;
uart_send_complete_flag = 0;
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 == 0)
;
uart_send_complete_flag = 0;
return ch;
}
#endif
3.2.5 修改debug_bsp_uart.h
该文件内容为
#ifndef DEBUG_BSP_UART_H_
#define DEBUG_BSP_UART_H_
#include "hal_data.h"
#include "stdio.h"
void Debug_UART9_Init(void);
extern volatile int uart_rx_complete_flag;
extern char rx_data[1024];
#endif
4. 下载测试
把编译好的程序下载到开发板并复位,打开串口助手,在发送框输入
time:20250228235959

注意:
- 如果串口助手没有自动添加\r\n,则上面的结尾需要手动加上
\r\n,否则程序无法识别该命令。
- 程序中未作纠错,请勿输入错误的时间,否则日历将出错。
工程附件
*附件:03_RTC.zip