分析完RTT的RTC框架,下一步就是看瑞萨是如何适配RTT的RTC框架了。瑞萨芯片本身支持RTC功能,因此其适配的RTC属于片上RTC,在硬件上,仅仅需要保证供电脚一直有电即可。
代码分析
源码位置
bsp\renesas\libraries\HAL_Drivers\drv_rtc.c
注册入口
static const struct rt_rtc_ops ra_rtc_ops =
{
.init = ra_rtc_init,
.get_secs = ra_get_secs,
.set_secs = ra_set_secs,
#ifdef RT_USING_ALARM
.set_alarm = ra_set_alarm,
.get_alarm = ra_get_alarm,
#endif
};
static rt_rtc_dev_t ra_rtc_dev;
static int rt_hw_rtc_init(void)
{
rt_err_t result;
ra_rtc_dev.ops = &ra_rtc_ops;
result = rt_hw_rtc_register(&ra_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR, RT_NULL);
if (result != RT_EOK)
{
LOG_E("rtc register err code: %d", result);
return result;
}
LOG_D("rtc init success");
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_rtc_init);从注册入口的实现上看,瑞萨并没有像其他模块那样添加驱动内部维护的参数,也就意味着,RTT框架层所需要的信息,在瑞萨RA系列上都可以通过芯片寄存器读写的方式直接维护,或者在FSP层维护,框架适配层不需要再加维护的变量。
初始化入口
static rt_err_t ra_rtc_init(void)
{
rt_err_t result = RT_EOK;
if (R_RTC_Open(&g_rtc_ctrl, &g_rtc_cfg) != RT_EOK)
{
LOG_E("rtc init failed.");
result = -RT_ERROR;
}
return result;
}读取秒级时间戳入口
static time_t get_rtc_timestamp(void)
{
struct tm tm_new = {0};
rtc_time_t g_current_time = {0};
R_RTC_CalendarTimeGet(&g_rtc_ctrl, &g_current_time);
tm_new.tm_year = g_current_time.tm_year;
tm_new.tm_mon = g_current_time.tm_mon;
tm_new.tm_mday = g_current_time.tm_mday;
tm_new.tm_hour = g_current_time.tm_hour;
tm_new.tm_min = g_current_time.tm_min;
tm_new.tm_sec = g_current_time.tm_sec;
tm_new.tm_wday = g_current_time.tm_wday;
tm_new.tm_yday = g_current_time.tm_yday;
tm_new.tm_isdst = g_current_time.tm_isdst;
return timegm(&tm_new);
}
static rt_err_t ra_get_secs(time_t *sec)
{
*(rt_uint32_t *)sec = get_rtc_timestamp();
LOG_D("RTC: get rtc_time %x\n", *(rt_uint32_t *)sec);
return RT_EOK;
}设置秒级时间戳入口
static rt_err_t set_rtc_time_stamp(time_t time_stamp)
{
struct tm now;
rtc_time_t g_current_time = {0};
gmtime_r(&time_stamp, &now);
if (now.tm_year < 100)
{
return -RT_ERROR;
}
g_current_time.tm_sec = now.tm_sec ;
g_current_time.tm_min = now.tm_min ;
g_current_time.tm_hour = now.tm_hour;
g_current_time.tm_mday = now.tm_mday;
g_current_time.tm_mon = now.tm_mon;
g_current_time.tm_year = now.tm_year;
g_current_time.tm_wday = now.tm_wday;
g_current_time.tm_yday = now.tm_yday;
if (R_RTC_CalendarTimeSet(&g_rtc_ctrl, &g_current_time) != FSP_SUCCESS)
{
LOG_E("set rtc time failed.");
return -RT_ERROR;
}
return RT_EOK;
}
static rt_err_t ra_set_secs(time_t *sec)
{
rt_err_t result = RT_EOK;
if (set_rtc_time_stamp(*(rt_uint32_t *)sec))
{
result = -RT_ERROR;
}
LOG_D("RTC: set rtc_time %x\n", *(rt_uint32_t *)sec);
return result;
}获取闹钟信息入口
static rt_err_t ra_get_alarm(struct rt_rtc_wkalarm *alarm)
{
rt_err_t result = RT_EOK;
struct rt_rtc_wkalarm *wkalarm = alarm;
rtc_alarm_time_t alarm_time_get =
{
.sec_match = RT_FALSE,
.min_match = RT_FALSE,
.hour_match = RT_FALSE,
.mday_match = RT_FALSE,
.mon_match = RT_FALSE,
.year_match = RT_FALSE,
.dayofweek_match = RT_FALSE,
};
if (RT_EOK == R_RTC_CalendarAlarmGet(&g_rtc_ctrl, &alarm_time_get))
{
wkalarm->tm_hour = alarm_time_get.time.tm_hour;
wkalarm->tm_min = alarm_time_get.time.tm_min;
wkalarm->tm_sec = alarm_time_get.time.tm_sec;
}
else
{
LOG_E("Calendar alarm Get failed.");
}
return result;
}设置闹钟信息入口
static rt_err_t ra_set_alarm(struct rt_rtc_wkalarm *alarm)
{
rt_err_t result = RT_EOK;
struct rt_rtc_wkalarm *wkalarm = alarm;
rtc_alarm_time_t alarm_time_set =
{
.sec_match = RT_TRUE,
.min_match = RT_TRUE,
.hour_match = RT_TRUE,
.mday_match = RT_FALSE,
.mon_match = RT_FALSE,
.year_match = RT_FALSE,
.dayofweek_match = RT_FALSE,
};
alarm_time_set.time.tm_hour = wkalarm->tm_hour;
alarm_time_set.time.tm_min = wkalarm->tm_min;
alarm_time_set.time.tm_sec = wkalarm->tm_sec;
if (1 == wkalarm->enable)
{
if (RT_EOK != R_RTC_CalendarAlarmSet(&g_rtc_ctrl, &alarm_time_set))
{
LOG_E("Calendar alarm Set failed.");
result = -RT_ERROR;
}
}
else
{
alarm_time_set.sec_match = RT_FALSE;
alarm_time_set.min_match = RT_FALSE;
alarm_time_set.hour_match = RT_FALSE;
if (RT_EOK != R_RTC_CalendarAlarmSet(&g_rtc_ctrl, &alarm_time_set))
{
LOG_E("Calendar alarm Stop failed.");
result = -RT_ERROR;
}
}
return result;
}闹钟时间到中断入口
void rtc_callback(rtc_callback_args_t *p_args)
{
#ifdef RT_USING_ALARM
static rt_device_t ra_device;
if (RTC_EVENT_ALARM_IRQ == p_args->event)
{
rt_alarm_update(ra_device, 1);
}
#endif
}总结
从瑞萨的RTC框架适配上看,我们甚至可以以此代码为模板去适配新的平台(唯一需要补充的是比秒级更精细时间戳的设置读取接口),因为这一层没有加任何料,仅仅是将接口与FSP层对接罢了。
另外,从驱动上看,我们可以发现,瑞萨的RTC功能支持中断回调,也就意味着支持闹铃功能。从芯片价格、原厂支持力度、供应链稳定性等角度去考量,如果综合考量后瑞萨的片子最优,就可以拿来做一些需要闹钟,低功耗唤醒类的产品。
我要赚赏金
