Skip to content

概念先知道

  • 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 供电(纽扣电池/后备电源)。

操作步骤

1
进入例程目录

本页不需要额外接线。在终端进入 SDK 的 RTC 例程目录(前提:已按快速开始(Linux)Windows搭建好环境):

cd examples/peripherals/rtc
2
编译工程

执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:

make CHIP=bl616 BOARD=bl616dk
3
烧录固件

用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
运行验证

打开串口助手(波特率 2000000)。例程先把时间设置为 2023-11-27 10:02:01(周一),之后每 5 秒打印一次当前时间:time:<Unix秒数>utc time:2023-11-27, 10:02:06, wday:1

代码执行流程

例程从启动到运行的完整流程如下(图中的循环箭头表示反复执行):

例程调用的 API 介绍

bflb_device_get_by_name("rtc")

获取 RTC 外设的设备句柄。

参数

  • name:外设名称,固定 "rtc"

返回值struct bflb_device_s * 设备句柄

bflb_rtc_set_time(rtc, 0)

把 RTC 计数值清零。

参数

  • rtc:RTC 设备句柄
  • time:计数值,例程传 0

返回值:成功返回 0;失败返回负值错误码

bflb_rtc_set_utc_time(&g_time)

用 UTC 时间结构体设置 RTC。struct bflb_tmtm_year 从 1900 起算(2023 → 2023-1900),tm_mon 从 0 起算(10 = 11 月)。

参数

  • tmstruct bflb_tm * 时间结构体指针

返回值:成功返回 0;失败返回负值错误码

bflb_rtc_get_time(rtc)

读取 RTC 当前计数值,配合 BFLB_RTC_TIME2SEC() 得到 Unix 秒数。

参数

  • rtc:RTC 设备句柄

返回值:RTC 计数值

bflb_rtc_get_utc_time(&g_time)

把当前时间拆分到 struct bflb_tm,用于打印年月日时分秒与星期。

参数

  • tmstruct bflb_tm * 输出结构体指针

返回值:成功返回 0;失败返回负值错误码

完整代码

以下为 rtc/main.c 完整源码,与官方示例(examples/peripherals/rtc)一致;点灯引脚已适配 Ai-M61/62-32S-Kit 板载 RGB 灯,默认折叠,点击展开:

📜 点击展开 rtc/main.c 完整代码
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 = 年份 - 1900tm_mon = 月份 - 1)后重新编译烧录即可。

断电后时间丢失

RTC 在断电后需要持续供电(纽扣电池/超级电容)才能保持走时;无后备电源时每次上电都是初始时间。

遇到问题?

如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

Released under the MIT License. Build Time 2026-09-11 14:52:23