Skip to content

Concepts First

  • PWM (pulse-width modulation): simulates an average voltage by switching very fast — the switching rate is the frequency, and the high-time share is the duty cycle.
  • Duty cycle: thresholds 100–500 over a period of 1000 give (500-100)/1000 = 40%. A larger duty means a brighter LED or faster motor.
  • Output pin: PWM channel 0 (PWM_CH0) defaults to GPIO24 (with io_sel=0, GPIO24–27 map to channels 0–3 positive outputs); an oscilloscope/logic analyzer is the easiest way to observe it.

Example Overview

This page is based on the pwm_basic example in the official Bouffalo SDK (examples/peripherals/pwm_v2/pwm_basic), which demonstrates basic output with the PWM v2 driver:

  • Clock source XCLK (40 MHz) divided by 40 gives 1 MHz; with a period register of 1000, the output frequency = 1 MHz / 1000 = 1 kHz;
  • Channel 0 thresholds are set to 100–500, so the high time = (500-100)/1000 = 40% duty cycle;
  • After starting channel 0 and the PWM, the square wave is output continuously and the serial port prints a running message every 2 s.
  • Sibling examples (examples/peripherals/pwm_v2/): pwm_all_channels (all-channel output), pwm_config_channel (channel config), pwm_deadtime (dead time), pwm_int (interrupt), pwm_6step / pwm_spwm / pwm_svpwm / pwm_foc_* (motor/inverter control).

Operation Steps

1
Prepare the Hardware

The default output pin of PWM channel 0 (PWM_CH0) is GPIO24 (with io_sel=0, GPIO24–27 map to the positive outputs of channels 0–3); board_pwm_gpio_init() already configures it as an alternate function. Use an oscilloscope or logic analyzer to observe the waveform on GPIO24; alternatively, connect an LED through a resistor to GPIO24 and watch the brightness.

2
Enter the Example Directory

BL616/BL618 use the PWM v2 driver; the corresponding example is pwm_basic. Open a terminal and enter it (prerequisite: set up the environment as in Quick Start (Linux) or Windows):

cd examples/peripherals/pwm_v2/pwm_basic
3
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
4
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
5
Run and Verify

Open a serial tool (baud rate 2000000); it prints pwm basic running every 2 s. Measure the PWM channel 0 pin with an oscilloscope or logic analyzer — you should see a 1 kHz square wave with a 40% duty cycle.

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

Gets the PWM v2 device handle; on BL616/BL618 the name is "pwm_v2_0".

Parameters:

  • name: device name string

Return: struct bflb_device_s * device handle

bflb_pwm_v2_feature_control(pwm, PWM_CMD_IO_SEL, PWM_IO_SEL_SINGLE_END)

Configures PWM output as single-ended (required on all chips except BL702L).

Parameters:

  • pwm: PWM device handle
  • cmd: command, PWM_CMD_IO_SEL selects the output mode
  • arg: PWM_IO_SEL_SINGLE_END single-ended output

Return: 0 on success; negative error code on failure

bflb_pwm_v2_init(pwm, config)

Initializes the PWM time base. In struct bflb_pwm_v2_config_s:

  • clk_source: clock source, BFLB_SYSTEM_XCLK
  • clk_div: divider, 40 on BL616/BL618 (1 MHz)
  • period: period count, 1000 → 1 kHz

Parameters:

  • pwm: PWM device handle
  • config: pointer to the config struct

Return: 0 on success; negative error code on failure

bflb_pwm_v2_channel_set_threshold(pwm, PWM_CH0, 100, 500)

Sets the low/high thresholds of a channel; duty cycle = (high - low) / period = 40%.

Parameters:

  • pwm: PWM device handle
  • channel: channel number, PWM_CH0
  • threshold_low: low threshold 100
  • threshold_high: high threshold 500

Return: 0 on success; negative error code on failure

bflb_pwm_v2_channel_positive_start(pwm, PWM_CH0)

Starts the positive output of channel 0.

Parameters:

  • pwm: PWM device handle
  • channel: channel number, PWM_CH0

Return: 0 on success; negative error code on failure

bflb_pwm_v2_start(pwm)

Starts the PWM peripheral to output the square wave. Start the channel first, then the PWM.

Parameters:

  • pwm: PWM device handle

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/pwm_v2/pwm_basic); the LED pins are adapted to the Ai-M61/62-32S-Kit onboard RGB LED. Collapsed by default, click to expand:

📜 Click to expand pwm_basic/main.c full code
c
#include "bflb_mtimer.h"
#include "bflb_pwm_v2.h"
#include "bflb_clock.h"
#include "board.h"

struct bflb_device_s *pwm;

int main(void)
{
    board_init();
    board_pwm_gpio_init();

    pwm = bflb_device_get_by_name("pwm_v2_0");

    /* period = .XCLK / .clk_div / .period = 40MHz( 32MHZ for bl702l) / 40( 32 for bl702l) / 1000 = 1KHz */
    struct bflb_pwm_v2_config_s cfg = {
        .clk_source = BFLB_SYSTEM_XCLK,
#if defined(BL702L)
        .clk_div = 32,
#else
        .clk_div = 40,
#endif
        .period = 1000,
    };

#if !defined(BL702L)
    bflb_pwm_v2_feature_control(pwm, PWM_CMD_IO_SEL, PWM_IO_SEL_SINGLE_END);
#endif
    bflb_pwm_v2_init(pwm, &cfg);
    bflb_pwm_v2_channel_set_threshold(pwm, PWM_CH0, 100, 500); /* duty = (500-100)/1000 = 40% */
    bflb_pwm_v2_channel_positive_start(pwm, PWM_CH0);
    bflb_pwm_v2_start(pwm);

    while (1) {
        printf("pwm basic running\r\n");
        bflb_mtimer_delay_ms(2000);
    }
}

FAQ

No waveform on the PWM pin

Make sure you are probing the pin mapped to PWM channel 0 (defined by board_pwm_gpio_init()); check the call order: bflb_pwm_v2_channel_positive_start first, then bflb_pwm_v2_start.

Wrong frequency or duty cycle

Frequency = XCLK / clk_div / period; duty = (high - low) / period. Adjust period, clk_div, and the thresholds; note that XCLK is 40 MHz on BL616/BL618.

Cannot find the pwm_v2 example

The old pwm_v1 examples only support older chips such as BL602/BL702. BL616/BL618 must use the examples under examples/peripherals/pwm_v2.

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