设计说明
源码说明
源代码位于:
-
bsp/artinchip/drv/rtc/drv_rtc.c,RTC Driver 层实现
-
bsp/artinchip/hal/rtc/hal_rtc.c,RTC HAL 层实现
-
bsp/artinchip/include/hal/hal_rtc.h,RTC HAL 层接口头文件
模块架构
Linux 提供了一个 RTC 子系统,简称 RTC Core,使得在用户空间可以通过 /dev/watchdogX 来访问 RTC 控制器。为了更方便查看硬件状态和参数设置,本驱动另外扩展了几个 sysfs 节点。 整个软件框架如 图 1 所示:

RTC V1.0 控制器可以适配到通用的 RTC 时间、闹钟接口,其它非标准的特性有:
- Alarm 的中断输出:
Alarm 的中断输出是否可用完全由板级电路的设计决定,软件上只需要使能中断信号即可。
具体操作详见 配置 RTC 自定义参数。
- 时钟校准:
RTC 控制器支持 ±975 ppm 的校准范围。
用户需要在实测 32 KHz 晶振的时钟频率后,配置给 menuconfig 中的参数
AIC_RTC_CLK_RATE
,详见 配置 RTC 自定义参数。 - SYS_BAK 寄存器:
RTC 控制器提供了 128 bits 的 NVMEM 寄存器,系统复位时不会丢失数据,目前主要用于记录系统复位状态。 详见 WRI 模块的复位原因管理说明。
- 8-bit 寄存器的读写为了减少对代码的干扰,可以将 8-bit 数据的拆解、打包进行封装,示例如下:
#define RTC_WRITEL(val, reg) \ do { \ RTC_WRITE_ENABLE; \ writeb((val) & 0xFF, RTC_BASE + (reg)); \ writeb(((val) >> 8) & 0xFF, RTC_BASE + (reg) + 0x4); \ writeb(((val) >> 16) & 0xFF, RTC_BASE + (reg) + 0x8); \ writeb(((val) >> 24) & 0xFF, RTC_BASE + (reg) + 0xC); \ RTC_WRITE_DISABLE; \ } while (0) #define RTC_READL(reg) (readb(RTC_BASE + reg) \ | (readb((RTC_BASE + reg) + 0x4) << 8) \ | (readb((RTC_BASE + reg) + 0x8) << 16) \ | (readb((RTC_BASE + reg) + 0xC) << 24))
关键流程设计
初始化流程
RTC 驱动的初始化接口通过 INIT_BOARD_EXPORT(drv_rtc_init)
完成,主要是通过调用 RTC 子系统的接口
rt_hw_rtc_register() 注册一个 RTC 设备。
-
初始化模块的时钟 (clk),确保 RTC 控制器所需的时钟已经正确配置和启用。
-
注册中断。
为 RTC 控制器分配并注册中断处理程序,以便在闹钟触发时能够及时响应。
-
设置校准参数。
根据用户实测的 32 KHz 晶振频率值,计算并设置校准参数,以确保 RTC 的时间精度。
-
配置 Alarm IO 的输出信号。
如果系统配置了 Alarm IO 输出信号,则使能相应的硬件功能,否则可略过。
校准算法设计
(100 * 1024 * 1024 + 100 * calibrate) / (clock-rate / 32) = 1024 => calibrate = (clock-rate * 32 - 100 * 1024 * 1024) / 100;
其中:
-
clock-rate: 是用户实测 32K 晶振的频率值乘以 100。关于配置说明,详见 打开 RTC 驱动
-
calibrate: 最终要填入 RTC 控制器的校准值,在 hal 层代码中自动处理。
注:校准值 calibrate 分正负:- 正:表示 32K 晶振实际偏快。
- 负:表示 32K 晶振偏慢。
数据结构设计
- struct aic_rtc_dev:属于 HAL 层接口,记录 RTC
控制器的配置信息:
struct aic_rtc_dev { rtc_callback_t callback; u32 clk_rate; u32 clk_drv; u32 alarm_io; u32 cal_fast; s32 cal_val; s32 inited; };
接口设计
Driver 层接口设计
struct rt_rtc_ops { rt_err_t (*init)(void); rt_err_t (*get_secs)(time_t *sec); rt_err_t (*set_secs)(time_t *sec); rt_err_t (*get_alarm)(struct rt_rtc_wkalarm *alarm); rt_err_t (*set_alarm)(struct rt_rtc_wkalarm *alarm); rt_err_t (*get_timeval)(struct timeval *tv); // 暂未实现 rt_err_t (*set_timeval)(struct timeval *tv); // 暂未实现 };
函数原型 |
rt_err_t rtc_ops_init(void) |
---|---|
功能说明 |
RTC 控制器的初始化 |
参数定义 |
无 |
返回值 |
0,成功 |
注意事项 |
- |
函数原型 |
rt_err_t rtc_ops_get_secs(time_t *sec) |
---|---|
功能说明 |
获取当前的 RTC 时间 |
参数定义 |
sec - 用于保存返回的时间信息,单位:秒 |
返回值 |
0,成功 |
注意事项 |
- |
函数原型 |
rt_err_t rtc_ops_set_secs(time_t *sec) |
---|---|
功能说明 |
设置当前的 RTC 时间 |
参数定义 |
sec - 用于保存要设置的时间信息,单位:秒 |
返回值 |
0,成功 |
注意事项 |
- |
函数原型 |
rt_err_t rtc_ops_get_alarm(struct rt_rtc_wkalarm *alarm) |
---|---|
功能说明 |
获取当前的 Alarm 信息 |
参数定义 |
alarm - 用于保存返回的 alarm 信息 |
返回值 |
0,成功 |
注意事项 |
- |
函数原型 |
rt_err_t rtc_ops_set_alarm(struct rt_rtc_wkalarm *alarm) |
---|---|
功能说明 |
设置 Alarm 信息 |
参数定义 |
alarm - 用于保存要设置的 Alarm 信息 |
返回值 |
0,成功 |
注意事项 |
- |
HAL 层接口设计
s32 hal_rtc_init(void);
s32 hal_rtc_deinit(void);
void hal_rtc_read_time(u32 *sec);
void hal_rtc_set_time(u32 sec);
s32 hal_rtc_read_alarm(u32 *sec);
void hal_rtc_set_alarm(u32 sec);
void hal_rtc_alarm_io_output(void);
void hal_rtc_32k_clk_output(void);
irqreturn_t hal_rtc_irq(int irq, void *arg);
void hal_rtc_cali(s32 clk_rate);
s32 hal_rtc_register_callback(rtc_callback_t callback);
Demo
如果要获取 RTC 时间,调用 RT-Thread 的标准时间接口即可,比如,- time() - clock_gettime() - gettimeofday()
static void test_alarm_callback(rt_alarm_t alarm, time_t timestamp) { pr_info("Test alarm callback function.\n"); } static void cmd_test_alarm(int argc, char **argv) { struct rt_alarm_setup setup; struct rt_alarm *alarm = RT_NULL; u32 timeout = 0; time_t now; struct tm p_tm; if (argc != 2) { pr_err("Invalid parameter\n"); return; } sscanf((char *)argv[1], "%u", &timeout); now = time(NULL) + timeout; gmtime_r(&now, &p_tm); setup.wktime = p_tm; alarm = rt_alarm_create(test_alarm_callback, &setup); if (alarm) { alarm->flag = RT_ALARM_ONESHOT; rt_alarm_start(alarm); } }