Skip to content

Concepts First

  • Timer (TIMER): the chip's "stopwatch". It counts at a configured rate and fires an interrupt when it reaches a compare value.
  • Divider (clock_div): slows the high-frequency clock before counting. The example divides 40 MHz by 40 (39+1) to get 1 MHz; compare value 64000 → one interrupt every 64 ms.
  • Compare interrupt: generated when the counter reaches a compare channel (comp0/1/2) value — used for periodic tasks, PWM time bases, and timing.

Example Overview

This page is based on the timer_int example in the official Bouffalo SDK (examples/peripherals/timer/timer_int), which demonstrates the hardware timer compare interrupt:

  • Timer clock = XCLK / (divider + 1); on BL616/BL618 the XCLK is 40 MHz and the example uses divider 39, giving a 1 MHz counting clock;
  • With comp0_val = 64000, the compare interrupt fires every 64000 counts (about 64 ms);
  • The ISR prints timer0 comp0 trigger, while the main loop prints hello every 1.5 s to show the program is still running.
  • Sibling examples (examples/peripherals/timer/): timer_capture (input capture), timer_clksource_check (clock-source check), timer_gpio_clock (GPIO clock output).

Operation Steps

1
Enter the Example Directory

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

cd examples/peripherals/timer/timer_int
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 serial port prints timer0 comp0 trigger about every 64 ms (timer compare interrupt), and the main loop prints hello every 1.5 s.

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("timer0")

Gets the timer device handle; this example uses timer0.

Parameters:

  • name: device name string, e.g. "timer0"

Return: struct bflb_device_s * device handle

bflb_timer_init(timer, config)

Initializes the timer. Key fields of struct bflb_timer_config_s:

  • counter_mode: counting mode, TIMER_COUNTER_MODE_PROLOAD reloads after a match
  • clock_source: clock source, TIMER_CLKSRC_XTAL uses the crystal clock
  • clock_div: divider; counting clock = XCLK / (clock_div + 1), use 39 on BL616/BL618
  • trigger_comp_id: trigger compare channel, TIMER_COMP_ID_0
  • comp0_val/comp1_val/comp2_val: match values of the three compare channels (this example only uses comp0)
  • preload_val: reload value, 0

Parameters:

  • timer: timer device handle
  • config: pointer to the struct bflb_timer_config_s config

Return: 0 on success; negative error code on failure

bflb_irq_attach(timer->irq_num, isr, NULL)

Registers the timer interrupt callback and enables the interrupt (bflb_irq_enable).

Parameters:

  • irq_num: timer interrupt number, timer->irq_num
  • isr: callback function, e.g. void isr(int irq, void *arg)

Return: none

bflb_timer_start(timer)

Starts the timer. The timer only runs after this function is called.

Parameters:

  • timer: timer device handle

Return: 0 on success; negative error code on failure

bflb_timer_get_compint_status(timer, comp_id)

Reads the interrupt status of a compare channel to know which channel fired.

Parameters:

  • timer: timer device handle
  • comp_id: compare channel, TIMER_COMP_ID_0/1/2

Return: true fired; false not fired

bflb_timer_compint_clear(timer, comp_id)

Clears the interrupt flag of a compare channel. It must be called in the ISR, otherwise the interrupt keeps firing.

Parameters:

  • timer: timer device handle
  • comp_id: compare channel number

Return: none

Complete Code

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

📜 Click to expand timer_int/main.c full code
c
#include "bflb_mtimer.h"
#include "bflb_timer.h"
#include "board.h"

#define TEST_TIMER_COMP_ID TIMER_COMP_ID_0

struct bflb_device_s *timer0;
struct bflb_device_s *timer1;

void timer0_isr(int irq, void *arg)
{
    bool status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_0);
    if (status) {
        bflb_timer_compint_clear(timer0, TIMER_COMP_ID_0);
        printf("timer0 comp0 trigger\r\n");
    }
    status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_1);
    if (status) {
        bflb_timer_compint_clear(timer0, TIMER_COMP_ID_1);
        printf("timer0 comp1 trigger\r\n");
    }
    status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_2);
    if (status) {
        bflb_timer_compint_clear(timer0, TIMER_COMP_ID_2);
        printf("timer0 comp2 trigger\r\n");
    }
}

void timer1_isr(int irq, void *arg)
{
    bool status = bflb_timer_get_compint_status(timer1, TIMER_COMP_ID_0);
    if (status) {
        bflb_timer_compint_clear(timer1, TIMER_COMP_ID_0);
        printf("timer1 comp0 trigger\r\n");
    }
    status = bflb_timer_get_compint_status(timer1, TIMER_COMP_ID_1);
    if (status) {
        bflb_timer_compint_clear(timer1, TIMER_COMP_ID_1);
        printf("timer1 comp1 trigger\r\n");
    }
    status = bflb_timer_get_compint_status(timer1, TIMER_COMP_ID_2);
    if (status) {
        bflb_timer_compint_clear(timer1, TIMER_COMP_ID_2);
        printf("timer1 comp2 trigger\r\n");
    }
}

int main(void)
{
    board_init();
    printf("Timer basic test\n");

    /* timer clk = XCLK/(div + 1 )*/
    struct bflb_timer_config_s cfg0;
    cfg0.counter_mode = TIMER_COUNTER_MODE_PROLOAD; /* preload when match occur */
    cfg0.clock_source = TIMER_CLKSRC_XTAL;
    cfg0.clock_div = 39; /* for bl616/bl618dg/bl618dg is 39, for bl702 is 31 */
    cfg0.trigger_comp_id = TEST_TIMER_COMP_ID;
    cfg0.comp0_val = 64000; /* match value 0  */
    cfg0.comp1_val = 0xFFFFFFFF; /* match value 1 */
    cfg0.comp2_val = 0xFFFFFFFF; /* match value 2 */
    cfg0.preload_val = 0;    /* preload value */

    #if 0
    struct bflb_timer_config_s cfg1;
    cfg1.counter_mode = TIMER_COUNTER_MODE_PROLOAD;
    cfg1.clock_source = TIMER_CLKSRC_XTAL;
    cfg1.clock_div = 39; /* for bl616/bl618dg/bl618dg is 39, for bl702 is 31 */
    cfg1.trigger_comp_id = TEST_TIMER_COMP_ID;
    cfg1.comp0_val = 1000000; /* match value 0  */
    cfg1.comp1_val = 1500000; /* match value 1 */
    cfg1.comp2_val = 2500000; /* match value 2 */
    cfg1.preload_val = 0;    /* preload value */
    #endif

    timer0 = bflb_device_get_by_name("timer0");
    // timer1 = bflb_device_get_by_name("timer1");

    /* Timer init with default configuration */
    bflb_timer_init(timer0, &cfg0);
    // bflb_timer_init(timer1, &cfg1);

    bflb_irq_attach(timer0->irq_num, timer0_isr, NULL);
    // bflb_irq_attach(timer1->irq_num, timer1_isr, NULL);
    bflb_irq_enable(timer0->irq_num);
    // bflb_irq_enable(timer1->irq_num);

    /* Enable timer */
    bflb_timer_start(timer0);
    // bflb_timer_start(timer1);

    printf("case success.\r\n");
    while (1) {
        bflb_mtimer_delay_ms(1500);
        printf("hello\n\r");
    }
}

FAQ

The timer0 comp0 trigger rate is wrong

The period is determined by the divider and compare value: period = (clock_div + 1) × comp0_val / XCLK. The example gives 40 × 64000 / 40 MHz ≈ 64 ms; change comp0_val to change the period.

Only hello is printed, no comp trigger

Make sure bflb_timer_start(timer0) is called and the interrupt is enabled (bflb_irq_enable); if you changed clock_div, note that XCLK is 40 MHz on BL616/BL618 — do not copy the 32-divider example for other chips.

Abnormal behavior because the flag is not cleared

Call bflb_timer_compint_clear for every fired compare channel in the ISR, otherwise the interrupt re-enters repeatedly.

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