概念先知道
- RTC(实时时钟):芯片内置的“日历+秒表”,持续走秒,常配合 32K 晶振/RC 时钟。
- Unix 时间:从 1970-01-01 00:00:00 起算的秒数,例程的
time:打印的就是它。 - 后备电源:断电后 RTC 是否继续走,取决于硬件是否给它持续供电(纽扣电池/超级电容)。
例程功能简介
本页对应博流官方 SDK 的 rtc 例程(examples/peripherals/rtc),演示实时时钟的设置与读取:
- 调用
bflb_rtc_set_time清零后,用bflb_rtc_set_utc_time写入演示时间(2023-11-27 10:02:01 周一); - 主循环每 5 秒读取一次:
BFLB_RTC_TIME2SEC(bflb_rtc_get_time(rtc))输出 Unix 秒数,bflb_rtc_get_utc_time输出拆分后的年月日时分秒与星期; - 时间走秒说明 RTC 正常运行;掉电后是否保持取决于硬件是否给 RTC 供电(纽扣电池/后备电源)。
操作步骤
本页不需要额外接线。在终端进入 SDK 的 RTC 例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/peripherals/rtc执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:
make CHIP=bl616 BOARD=bl616dk用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):
make flash CHIP=bl616 COMX=/dev/ttyUSB0打开串口助手(波特率 2000000)。例程先把时间设置为 2023-11-27 10:02:01(周一),之后每 5 秒打印一次当前时间:time:<Unix秒数> 和 utc time:2023-11-27, 10:02:06, wday:1。
代码执行流程
例程从启动到运行的完整流程如下(图中的循环箭头表示反复执行):
例程调用的 API 介绍
bflb_rtc_set_utc_time(&g_time)
用 UTC 时间结构体设置 RTC。struct bflb_tm 中 tm_year 从 1900 起算(2023 → 2023-1900),tm_mon 从 0 起算(10 = 11 月)。
参数:
tm:struct bflb_tm *时间结构体指针
返回值:成功返回 0;失败返回负值错误码
bflb_rtc_get_utc_time(&g_time)
把当前时间拆分到 struct bflb_tm,用于打印年月日时分秒与星期。
参数:
tm:struct bflb_tm *输出结构体指针
返回值:成功返回 0;失败返回负值错误码
完整代码
以下为 rtc/main.c 完整源码,与官方示例(examples/peripherals/rtc)一致;点灯引脚已适配 Ai-M61/62-32S-Kit 板载 RGB 灯,默认折叠,点击展开:
📜 点击展开 rtc/main.c 完整代码
#include "bflb_mtimer.h"
#include "bflb_rtc.h"
#include "bflb_clock.h"
#include "board.h"
#if defined (BL616CL) || defined (BL618DG)
#if defined (BL616CL)
#include "bl616cl_hbn.h"
#elif defined(BL618DG)
#include "bl618dg_hbn.h"
#endif
#endif
struct bflb_device_s *rtc;
struct bflb_tm g_time;
#if defined (BL616CL) || defined (BL618DG)
#define BL_PDS_CNT_TO_US(cnt) ((cnt) * 15625 / 512) /* cnt / 32768 * 1000000 */
/* RC32K fine trim calibration using mtimer as reference */
static int rc32k_fine_trim(void)
{
uint32_t retry_cnt = 0;
uint64_t rtc_cnt;
uint64_t rtc_record_us, rtc_now_us;
uint64_t mtimer_record_us, mtimer_now_us;
uint32_t rtc_us, mtimer_us;
int error_ppm;
int last_error_ppm = 0;
uint32_t rc32k_code;
uint32_t last_diff_code = 0;
int first_measure = 1;
printf("rc32k_fine_trim: r_code=%u\r\n",HBN_Get_RC32K_R_Code());
while (retry_cnt < 100) {
retry_cnt++;
/* Disable IRQ */
__disable_irq();
mtimer_record_us = bflb_mtimer_get_time_us();
HBN_Get_RTC_Timer_Val((uint32_t *)&rtc_cnt, (uint32_t *)&rtc_cnt + 1);
__enable_irq();
rtc_record_us = BL_PDS_CNT_TO_US(rtc_cnt);
bflb_mtimer_delay_ms(100);
/* Disable IRQ */
__disable_irq();
mtimer_now_us = bflb_mtimer_get_time_us();
HBN_Get_RTC_Timer_Val((uint32_t *)&rtc_cnt, (uint32_t *)&rtc_cnt + 1);
__enable_irq();
rtc_now_us = BL_PDS_CNT_TO_US(rtc_cnt);
/* Calculate time difference and PPM error */
rtc_us = (uint32_t)(rtc_now_us - rtc_record_us);
mtimer_us = (uint32_t)(mtimer_now_us - mtimer_record_us);
error_ppm = ((int32_t)(rtc_us - mtimer_us)) * 1000000 / mtimer_us;
printf("rc32k_fine_trim: mtimer_us=%u, rtc_us=%u, ppm=%d\r\n",
mtimer_us, rtc_us, error_ppm);
/* Fine trim target: ±500ppm */
if (abs(error_ppm) > 500) {
/* Calculate adjustment code (step ~200ppm) */
int diff_code;
int is_diverging = 0;
/* Improved divergence detection: only when error absolute value increases */
if (!first_measure && abs(error_ppm) > abs(last_error_ppm)) {
if (abs(last_error_ppm) > 1) {
is_diverging = 1;
}
}
if (is_diverging) {
/* Diverging, rollback to last code and adjust by ±1 */
printf("rc32k_fine_trim: detected divergence (ppm:%d, last:%d), rollback\r\n",
error_ppm, last_error_ppm);
rc32k_code = (int)HBN_Get_RC32K_R_Code() - (int)last_diff_code;
/* Rollback to last code and adjust by ±1 */
if (last_error_ppm < 0) {
diff_code = -1;
} else {
diff_code = 1;
}
rc32k_code += diff_code;
HBN_Set_RC32K_R_Code(rc32k_code);
printf("rc32k_fine_trim: retry_cnt=%u, adjust code=%u (diff=%d)\r\n",
retry_cnt, rc32k_code, diff_code);
/* Record current error for next divergence detection */
/* Diverging diff_code not Record */
last_diff_code = diff_code;
first_measure = 0;
/* Wait for RC32K frequency to settle */
bflb_mtimer_delay_ms(5);
} else {
/* Normal calculation of adjustment code */
diff_code = error_ppm / 400 ;
/* Limit max adjustment to avoid jumping at fluctuation points */
if (diff_code > 5) diff_code = 5;
if (diff_code < -5) diff_code = -5;
rc32k_code = HBN_Get_RC32K_R_Code();
rc32k_code += diff_code;
HBN_Set_RC32K_R_Code(rc32k_code);
printf("rc32k_fine_trim: retry_cnt=%u, adjust code=%u (diff=%d)\r\n",
retry_cnt, rc32k_code, diff_code);
/* Record current error for next divergence detection */
last_error_ppm = error_ppm;
last_diff_code = diff_code;
first_measure = 0;
/* Wait for RC32K frequency to settle */
bflb_mtimer_delay_ms(5);
}
} else {
printf("rc32k_fine_trim: success! retry_cnt=%u, ppm=%d, code=%u\r\n",
retry_cnt, error_ppm, HBN_Get_RC32K_R_Code());
return error_ppm;
}
}
printf("rc32k_fine_trim: timeout!\r\n");
return -1;
}
#endif
int main(void)
{
board_init();
rtc = bflb_device_get_by_name("rtc");
bflb_rtc_set_time(rtc, 0);
#if defined (BL616CL) || defined (BL618DG)
HBN_Trim_RC32K();
rc32k_fine_trim();
#endif
/* Set RTC time: 2023-11-27, 10:2:1, Monday */
g_time.tm_sec = 1;
g_time.tm_min = 2;
g_time.tm_hour = 10;
g_time.tm_wday = 1;
g_time.tm_mday = 27;
g_time.tm_mon = 10;
g_time.tm_year = 2023 - 1900;
bflb_rtc_set_utc_time(&g_time);
while (1) {
printf("time:%lld\r\n", BFLB_RTC_TIME2SEC(bflb_rtc_get_time(rtc)));
bflb_rtc_get_utc_time(&g_time);
printf("utc time:%u-%u-%u, %u:%u:%u, wday:%u\r\n",
g_time.tm_year + 1900, g_time.tm_mon + 1, g_time.tm_mday,
g_time.tm_hour, g_time.tm_min, g_time.tm_sec,
g_time.tm_wday);
bflb_mtimer_delay_ms(5000);
}
}FAQ
打印的时间不递增
确认 bflb_rtc_set_time(rtc, 0) 与 bflb_rtc_set_utc_time 都调用成功;如果例程被裁剪,检查 RTC 时钟源配置(32K 晶振/RC)。
想设置自己的时间
修改 g_time 各字段(注意 tm_year = 年份 - 1900、tm_mon = 月份 - 1)后重新编译烧录即可。
断电后时间丢失
RTC 在断电后需要持续供电(纽扣电池/超级电容)才能保持走时;无后备电源时每次上电都是初始时间。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

