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
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.
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_basicRun the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:
make CHIP=bl616 BOARD=bl616dkConnect 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/ttyUSB0Open 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 handlecmd: command,PWM_CMD_IO_SELselects the output modearg:PWM_IO_SEL_SINGLE_ENDsingle-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_XCLKclk_div: divider,40on BL616/BL618 (1 MHz)period: period count,1000→ 1 kHz
Parameters:
pwm: PWM device handleconfig: 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 handlechannel: channel number,PWM_CH0threshold_low: low threshold100threshold_high: high threshold500
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 handlechannel: 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
#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.
Related Best Practices
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

