Skip to content

Concepts First

  • RTC (real-time clock): the chip's built-in "calendar + stopwatch" that keeps running, usually driven by a 32K crystal/RC clock.
  • Unix time: seconds since 1970-01-01 00:00:00; that is what the time: print shows.
  • Backup power: whether the RTC keeps time after power-off depends on the hardware (coin cell/supercapacitor) supplying it.

Example Overview

This page is based on the rtc example in the official Bouffalo SDK (examples/peripherals/rtc), which demonstrates RTC set and read:

  • After clearing with bflb_rtc_set_time, it writes the demo time (2023-11-27 10:02:01 Monday) with bflb_rtc_set_utc_time;
  • The main loop reads every 5 seconds: BFLB_RTC_TIME2SEC(bflb_rtc_get_time(rtc)) prints Unix seconds, and bflb_rtc_get_utc_time prints the broken-out date/time and weekday;
  • The seconds advancing means the RTC is running; keeping time after power-off depends on the backup power (coin cell) on the hardware.

Operation Steps

1
Enter the Example Directory

No external wiring is needed for this page. Open a terminal and enter the RTC example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):

cd examples/peripherals/rtc
2
Build the Project

Run the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:

make CHIP=bl616 BOARD=bl616dk
3
Flash the Firmware

Connect the board with a USB cable, hold the BOOT button (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash (replace the serial port with the one on your computer):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
Run and Verify

Open a serial tool (baud rate 2000000). The example sets the time to 2023-11-27 10:02:01 (Monday) and then prints the current time every 5 seconds: time:<Unix seconds> and utc time:2023-11-27, 10:02:06, wday:1.

Code Execution Flow

The complete execution flow from startup to running is shown below (loop arrows mean repeated execution):

APIs Used by the Example

bflb_device_get_by_name("rtc")

Gets the RTC device handle.

Parameters:

  • name: device name, always "rtc"

Return: struct bflb_device_s * device handle

bflb_rtc_set_time(rtc, 0)

Clears the RTC counter.

Parameters:

  • rtc: RTC device handle
  • time: counter value, 0 in the example

Return: 0 on success; negative error code on failure

bflb_rtc_set_utc_time(&g_time)

Sets the RTC from a UTC time struct. In struct bflb_tm, tm_year is offset from 1900 (2023 → 2023-1900) and tm_mon is 0-based (10 = November).

Parameters:

  • tm: pointer to struct bflb_tm

Return: 0 on success; negative error code on failure

bflb_rtc_get_time(rtc)

Reads the RTC counter; combine with BFLB_RTC_TIME2SEC() for Unix seconds.

Parameters:

  • rtc: RTC device handle

Return: RTC counter value

bflb_rtc_get_utc_time(&g_time)

Breaks the current time into struct bflb_tm for printing.

Parameters:

  • tm: pointer to the output struct

Return: 0 on success; negative error code on failure

Complete Code

The complete source below matches the effect described on this page. It is based on the official example (examples/peripherals/rtc); the LED pins are adapted to the Ai-M61/62-32S-Kit onboard RGB LED. Collapsed by default, click to expand:

📜 Click to expand rtc/main.c full code
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

The printed time does not advance

Make sure both bflb_rtc_set_time(rtc, 0) and bflb_rtc_set_utc_time succeeded; if the example is trimmed, check the RTC clock source config (32K crystal/RC).

I want to set my own time

Modify the g_time fields (tm_year = year - 1900, tm_mon = month - 1) and rebuild/flash.

Time is lost after power-off

The RTC needs continuous power (coin cell/supercapacitor) to keep time; without backup power it resets on every power-up.

Have questions?

For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

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