2.8. RTC
2.8.1. 概述
AC792N 芯片内置一个 RTC(实时时钟)模块,可提供系统时间计时、闹钟唤醒以及 PR 口唤醒等功能。
主要特性:
支持多种时钟源选择:外部 32K 晶振、内部 LRC
支持系统时间读写(年/月/日/时/分/秒)
支持闹钟功能
支持 PR 口(PR0~PR1)唤醒,可配置唤醒边沿和滤波时间
支持 PR 口 GPIO 基本操作(输入/输出/上下拉/强驱/数字输入)
振荡器驱动电流可配
例程路径:
apps/common/example/peripheral/rtc/rtc_test.c,可使用hello_demo工程测试,使用时在app_config.h中使能相应例程的宏开关USE_RTC_TEST_DEMO。
2.8.2. RTC 参数配置
2.8.2.1. 配置结构体
RTC 的平台配置通过 rtc_platform_data 结构体完成,各字段含义如下:
字段 |
类型 |
说明 |
|---|---|---|
default_sys_time |
struct sys_time |
默认系统时间。当 RTC 未被设置过时间时,将此值作为初始时间 |
alarm_wakeup_cb_in_irq |
void (*)(void) |
闹钟中断回调函数,在中断上下文中调用 |
pr_wakeup_cb_in_irq |
void (*)(int) |
PR 口唤醒中断回调函数,参数为唤醒的 PR 口编号 |
clk_sel |
rtc_clk_sel_t |
RTC 时钟源选择 |
x32xs |
x32xs_t |
32K 振荡器驱动电流档位 |
sys_time 结构体定义:
struct sys_time {
u16 year; // 年
u8 month; // 月
u8 day; // 日
u8 hour; // 时
u8 min; // 分
u8 sec; // 秒
};
Board 层配置示例:
#if TCFG_RTC_ENABLE
// 闹钟唤醒回调(在中断上下文中执行)
static void alarm_wakeup_cb_in_irq(void)
{
// TODO,不要加打印
}
// PR口唤醒回调(在中断上下文中执行)
static void pr_wakeup_cb_in_irq(int port)
{
// TODO,不要加打印
}
// RTC平台数据初始化
RTC_PLATFORM_DATA_BEGIN(rtc_data)
.default_sys_time = {
.year = 2024,
.month = 1,
.day = 1,
.hour = 0,
.min = 0,
.sec = 0,
},
.alarm_wakeup_cb_in_irq = alarm_wakeup_cb_in_irq,
.pr_wakeup_cb_in_irq = pr_wakeup_cb_in_irq,
.clk_sel = TCFG_RTC_CLK_SEL,
.x32xs = TCFG_RTC_X32XS_LEVEL,
RTC_PLATFORM_DATA_END()
#endif // TCFG_RTC_ENABLE
2.8.2.2. 应用配置宏
在 app_config.h 中通过以下宏配置 RTC 使能和参数:
//*********************************************************************************//
// RTC配置 //
//*********************************************************************************//
#ifdef USE_RTC_TEST_DEMO
#define TCFG_RTC_ENABLE 1 // RTC功能使能
#define TCFG_RTC_CLK_SEL CLK_SEL_32K // 时钟源:外部32K晶振
#define TCFG_RTC_X32XS_LEVEL X32XS_LEVEL_1 // 晶振驱动电流档位
#endif // USE_RTC_TEST_DEMO
配置说明:
宏 |
可选值 |
说明 |
|---|---|---|
TCFG_RTC_ENABLE |
0 / 1 |
RTC 功能使能开关,置 1 开启 |
TCFG_RTC_CLK_SEL |
CLK_SEL_32K / CLK_SEL_LRC |
RTC 时钟源选择,推荐使用
|
TCFG_RTC_X32XS_LEVEL |
X32XS_LEVEL_0 ~ X32XS_LEVEL_3 |
外部 32K 振荡器驱动电流 |
2.8.2.3. 注册设备
在板级设备表中将 RTC 设备注册到系统:
// 设备表注册
REGISTER_DEVICES(device_table) = {
// ...
#if TCFG_RTC_ENABLE
{ "rtc", &rtc_dev_ops, (void *)&rtc_data },
#endif
// ...
};
2.8.3. RTC 驱动使用
2.8.3.1. 板级初始化
在使用 RTC 之前,需要在板级代码中完成以下初始化步骤。RTC 初始化依赖 ADC 模块,必须确保 ADC 先于 RTC 初始化。
初始化顺序(按启动流程):
步骤1: power_early_flowing() 中设置平台数据并初始化电源时钟:
void power_early_flowing(void)
{
PORT_TABLE(g);
// ... 其他初始化 ...
power_set_mode(TCFG_POWER_MODE);
#if TCFG_RTC_ENABLE
set_rtc_platform_data(&rtc_data);
#endif
}
步骤2: board_power_init() 中通过 PCONTROL_RTC_CLK 命令告知
power RTC的时钟源,防止低功耗流程影响RTC晶振配置:
static void board_power_init(void)
{
#if TCFG_RTC_ENABLE
power_control(PCONTROL_RTC_CLK, TCFG_RTC_CLK_SEL);
#endif
// ... 其他电源控制 ...
}
步骤3: board_init() 中初始化 ADC 和 RTC:
void board_init(void)
{
// ...
#if TCFG_ADKEY_ENABLE || TCFG_RTC_ENABLE
adc_init(); // ADC必须先于RTC初始化
#endif
#if TCFG_RTC_ENABLE
rtc_early_init(); // RTC开机初始化
#endif
// ...
}
2.8.3.2. 打开设备
RTC 设备名为 "rtc",通过标准驱动接口 dev_open() 打开:
void *rtc_hdl;
rtc_hdl = dev_open("rtc", NULL);
if (!rtc_hdl) {
printf("err: rtc open fail!!!\n");
return;
}
2.8.3.3. 获取/设置系统时间
通过 dev_ioctl() 读写系统时间:
struct sys_time time;
// 获取当前系统时间
dev_ioctl(rtc_hdl, IOCTL_GET_SYS_TIME, (u32)&time);
printf("%d-%02d-%02d %02d:%02d:%02d\n",
time.year, time.month, time.day,
time.hour, time.min, time.sec);
// 设置系统时间
time.sec = 0;
dev_ioctl(rtc_hdl, IOCTL_SET_SYS_TIME, (u32)&time);
printf("set_sys_time: %d-%02d-%02d %02d:%02d:%02d\n",
time.year, time.month, time.day,
time.hour, time.min, time.sec);
2.8.3.4. 闹钟功能
RTC 支持两种闹钟模式:
模式1:绝对时间闹钟(指定具体时间点)
struct sys_time alarm_time;
alarm_time.sec = 59; // 设置闹钟时间为当前分钟的第59秒
dev_ioctl(rtc_hdl, IOCTL_SET_ALARM, (u32)&alarm_time);
// 使能闹钟
dev_ioctl(rtc_hdl, IOCTL_SET_ALARM_ENABLE, 1);
模式2:倒计时闹钟(指定延迟秒数)
dev_ioctl(rtc_hdl, IOCTL_SET_COUNTDOWN_ALARM, 30); // 30秒后闹钟触发
// 使能闹钟
dev_ioctl(rtc_hdl, IOCTL_SET_ALARM_ENABLE, 1);
注意: 闹钟触发后,
rtc_platform_data中配置的alarm_wakeup_cb_in_irq回调函数将在中断上下文中被调用。请在回调中仅做必要的标志位设置,避免阻塞操作。
获取当前闹钟配置:
struct sys_time alarm_info;
dev_ioctl(rtc_hdl, IOCTL_GET_ALARM, (u32)&alarm_info);
PR口作IO的基本操作:
// 设置PR口方向
rtc_port_pr_dir(IO_PORT_PR_01, 1); // 1: 输入, 0: 输出
// 读取PR口输入电平
int level = rtc_port_pr_read(IO_PORT_PR_01); // 0: 低电平, 1: 高电平
// 设置PR口输出电平
rtc_port_pr_out(IO_PORT_PR_01, 1); // 1: 高电平, 0: 低电平
// 设置PR口强驱
rtc_port_pr_hd(IO_PORT_PR_01, 1); // 1: 打开, 0: 关闭
// 设置上拉/下拉
rtc_port_pr_pu(IO_PORT_PR_01, 1); // 1: 打开上拉
rtc_port_pr_pd(IO_PORT_PR_01, 0); // 0: 关闭下拉
// 设置数字输入功能
rtc_port_pr_die(IO_PORT_PR_01, 1); // 1: 打开, 0: 关闭
2.8.3.5. 软关机保存时间
RTC时钟源选择LRC时,软关机前需调用 poweroff_save_rtc_time() 保存 RTC
校准数据:
u8 power_soff_callback(void)
{
#if TCFG_RTC_ENABLE
poweroff_save_rtc_time(); // 软关机前保存RTC校准数据
#endif
// ...
}
2.8.3.6. PR 口唤醒功能
RTC 模块提供 PR 口(PR0~PR1)的 IO
操作和唤醒功能。详见《开关机-唤醒开机》章节
2.8.4. RTC IOCTL 命令
IOCTL 命令 |
功能说明 |
参数类型 |
|---|---|---|
IOCTL_GET_SYS_TIME |
获取当前系统时间 |
struct sys_time * |
IOCTL_SET_SYS_TIME |
设置系统时间 |
struct sys_time * |
IOCTL_GET_ALARM |
获取当前闹钟配置 |
struct sys_time * |
IOCTL_SET_ALARM |
设置绝对时间闹钟 |
struct sys_time * |
IOCTL_SET_ALARM_ENABLE |
闹钟使能/关闭 |
bool (1=使能, 0=关闭) |
IOCTL_SET_COUNTDOWN_ALARM |
设置倒计时闹钟 |
u32 (倒计时秒数) |
2.8.5. 常见问题说明
1、RTC 初始化失败
答:RTC 初始化依赖 ADC 模块,若 ADC 未初始化或初始化顺序错误会导致 RTC
异常。 确保 adc_init() 在 rtc_early_init() 之前调用。
2、系统复位后时间丢失
答:系统复位重启,RTCVDD在断电复位后会重新从 default_sys_time
开始计时。该行为为硬件特性。若需要掉电保持时间,需外接备用电池供电。
3、RTC模块报错"RTC_UNACCESSIBLE!!! Please ensure the power supply of RTCVDD"
答:RTCVDD工作电压太低,无法获取RTC值。RTCVDD电压要求大于等于2.0V。
4、RTC模块报错"RTC_ACCESSIBLE_TIME_UNRELIABLE!!!"
答:RTC模块出现复位,获取到的RTC时间不可靠。
2.8.6. API 参考
ioctl cmd for rtc
-
RTC_MAGIC
-
IOCTL_GET_SYS_TIME
-
IOCTL_SET_SYS_TIME
-
IOCTL_GET_ALARM
-
IOCTL_SET_ALARM
-
IOCTL_SET_ALARM_ENABLE
RTC时钟源选择
PR口唤醒边沿
PR口唤醒滤波时间
振荡器电流选项
rtc_platform_data
-
void set_rtc_platform_data(const struct rtc_platform_data *priv)
-
int rtc_early_init(void)
rtc开机初始化函数,必须先初始化完adc
-
void poweroff_save_rtc_time(void)
软关机前保存rtc校准数据
-
int rtc_port_pr_dir(int port, u8 dir)
设置PR口的方向
- Parameters
gpio – 参考宏IO_PORT_PR_0X
dir – 1: 输入 0: 输出
- Returns
0: 成功 非0: 失败
-
int rtc_port_pr_read(int port)
获取PR口输入电平
- Parameters
gpio – 参考宏IO_PORT_PR_0X
- Returns
0: 低电平 1: 高电平
-
int rtc_port_pr_out(int port, u8 on)
设置PR口的输出电平
- Parameters
gpio – 参考宏IO_PORT_PR_0X
on – 1: 高电平 0: 低电平
- Returns
0: 成功 非0: 失败
-
int rtc_port_pr_hd(int port, u8 on)
设置PR口的强驱
- Parameters
gpio – 参考宏IO_PORT_PR_0X
on – 1: 打开 0: 关闭
- Returns
0: 成功 非0: 失败
-
int rtc_port_pr_pu(int port, u8 on)
设置PR口的上拉电阻
- Parameters
gpio – 参考宏IO_PORT_PR_0X
on – 1: 打开 0: 关闭
- Returns
0: 成功 非0: 失败
-
int rtc_port_pr_pd(int port, u8 on)
设置PR口的下拉电阻
- Parameters
gpio – 参考宏IO_PORT_PR_0X
on – 1: 打开 0: 关闭
- Returns
0: 成功 非0: 失败
-
int rtc_port_pr_die(int port, u8 on)
设置PR口的数字输入功能
- Parameters
gpio – 参考宏IO_PORT_PR_0X
on – 1: 打开 0: 关闭
- Returns
0: 成功 非0: 失败
-
int rtc_port_pr_wkup_cfg(int port, pr_io_wkup_edge_t wkup_edge, pr_io_wkup_flt_t flt_cfg)
设置PR口的唤醒功能
- Parameters
gpio – 参考宏IO_PORT_PR_0X
wkup_edge – 唤醒边沿
flt_cfg – 唤醒滤波时间
- Returns
0: 成功 非0: 失败
-
int rtc_port_pr_wkup_disable(int port)
关闭PR口的唤醒功能
- Parameters
gpio – 参考宏IO_PORT_PR_0X
- Returns
0: 成功 非0: 失败
-
struct rtc_platform_data