Concepts First
- ADC (analog-to-digital): converts a continuous voltage into a number. The example uses 16-bit resolution, so readings range 0–65535.
- Reference voltage (VREF): the ADC's full-scale limit; the example uses 3.2V — do not input more than that or the pin may be damaged.
- Continuous vs single conversion: continuous mode samples endlessly (good for monitoring); polling mode just reads the result when the program asks.
Example Overview
This page is based on the adc_poll example in the official Bouffalo SDK (examples/peripherals/adc/adc_poll), which demonstrates ADC sampling in polling mode:
- Configured for continuous conversion, 16-bit resolution, and a 3.2V reference (
ADC_VREF_3P2V); - Samples ADC channels 0–10 in turn (negative input tied to GND), taking 26 points per channel and discarding the first 10 unstable ones;
bflb_adc_parse_resultconverts raw values into millivolts and prints them.- Sibling examples (
examples/peripherals/adc/):adc_dma(DMA sampling),adc_int(interrupt sampling),adc_poll_onechan(single channel),adc_poll_diff_mode(differential),adc_tsen(temperature sensor),adc_vbat(battery voltage).
Operation Steps
The ADC sampling pins are configured by board_adc_gpio_init(). Connect the voltage to be measured (do not exceed 3.2V) to any ADC channel pin and share ground with the board.
Open a terminal and enter the ADC example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):
cd examples/peripherals/adc/adc_pollRun 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). The example samples ADC channels 0–10 in turn and prints the raw value (raw data) and the converted voltage (mv); compare it with the measured voltage to verify accuracy.
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("adc")
Gets the ADC device handle.
Parameters:
name: device name string, always"adc"for ADC
Return: struct bflb_device_s * device handle
bflb_adc_init(adc, config)
Initializes the ADC. In struct bflb_adc_config_s:
clk_div: clock divider,ADC_CLK_DIV_32scan_conv_mode: scan mode, disabled in the examplecontinuous_conv_mode: continuous conversion, enableddifferential_mode: differential mode, disabled (single-ended)resolution: resolution,ADC_RESOLUTION_16Bvref: reference voltage,ADC_VREF_3P2V
Parameters:
adc: ADC device handleconfig: pointer to the config struct
Return: 0 on success; negative error code on failure
bflb_adc_channel_config(adc, chan, 1)
Configures a sampling channel. In struct bflb_adc_channel_s, pos_chan is the positive channel (e.g. ADC_CHANNEL_1) and neg_chan is the negative channel (ADC_CHANNEL_GND for single-ended).
Parameters:
adc: ADC device handlechan: channel config arraynum: channel count, 1 per call in the example
Return: 0 on success; negative error code on failure
bflb_adc_start_conversion(adc)
Starts the conversion; poll bflb_adc_get_count(adc) for data availability and read raw values with bflb_adc_read_raw(adc). Call bflb_adc_stop_conversion(adc) when done.
Parameters:
adc: ADC device handle
Return: 0 on success; negative error code on failure
bflb_adc_parse_result(adc, raw_data, result, count)
Converts raw samples into voltage results.
Parameters:
adc: ADC device handleraw_data: raw data arrayresult:struct bflb_adc_result_sarray containingpos_chan(channel) andmillivolt(voltage)count: number of samples
Return: 0 on success; negative error code on failure
bflb_adc_deinit(adc)
Deinitializes the ADC after sampling.
Parameters:
adc: ADC 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/adc/adc_poll); the LED pins are adapted to the Ai-M61/62-32S-Kit onboard RGB LED. Collapsed by default, click to expand:
📜 Click to expand adc_poll/main.c full code
#include "bflb_adc.h"
#include "bflb_mtimer.h"
#include "board.h"
struct bflb_device_s *adc;
#define TEST_ADC_CHANNEL_0 1
#define TEST_ADC_CHANNEL_1 1
#define TEST_ADC_CHANNEL_2 1
#define TEST_ADC_CHANNEL_3 1
#define TEST_ADC_CHANNEL_4 1
#define TEST_ADC_CHANNEL_5 1
#define TEST_ADC_CHANNEL_6 1
#define TEST_ADC_CHANNEL_7 1
#define TEST_ADC_CHANNEL_8 1
#define TEST_ADC_CHANNEL_9 1
#define TEST_ADC_CHANNEL_10 1
#define TEST_ADC_CHANNELS (TEST_ADC_CHANNEL_0 + \
TEST_ADC_CHANNEL_1 + \
TEST_ADC_CHANNEL_2 + \
TEST_ADC_CHANNEL_3 + \
TEST_ADC_CHANNEL_4 + \
TEST_ADC_CHANNEL_5 + \
TEST_ADC_CHANNEL_6 + \
TEST_ADC_CHANNEL_7 + \
TEST_ADC_CHANNEL_8 + \
TEST_ADC_CHANNEL_9 + \
TEST_ADC_CHANNEL_10)
struct bflb_adc_channel_s chan[] = {
#if TEST_ADC_CHANNEL_0
{ .pos_chan = ADC_CHANNEL_0,
.neg_chan = ADC_CHANNEL_GND },
#endif
#if TEST_ADC_CHANNEL_1
{ .pos_chan = ADC_CHANNEL_1,
.neg_chan = ADC_CHANNEL_GND },
#endif
#if TEST_ADC_CHANNEL_2
{ .pos_chan = ADC_CHANNEL_2,
.neg_chan = ADC_CHANNEL_GND },
#endif
#if TEST_ADC_CHANNEL_3
{ .pos_chan = ADC_CHANNEL_3,
.neg_chan = ADC_CHANNEL_GND },
#endif
#if TEST_ADC_CHANNEL_4
{ .pos_chan = ADC_CHANNEL_4,
.neg_chan = ADC_CHANNEL_GND },
#endif
#if TEST_ADC_CHANNEL_5
{ .pos_chan = ADC_CHANNEL_5,
.neg_chan = ADC_CHANNEL_GND },
#endif
#if TEST_ADC_CHANNEL_6
{ .pos_chan = ADC_CHANNEL_6,
.neg_chan = ADC_CHANNEL_GND },
#endif
#if TEST_ADC_CHANNEL_7
{ .pos_chan = ADC_CHANNEL_7,
.neg_chan = ADC_CHANNEL_GND },
#endif
#if TEST_ADC_CHANNEL_8
{ .pos_chan = ADC_CHANNEL_8,
.neg_chan = ADC_CHANNEL_GND },
#endif
#if TEST_ADC_CHANNEL_9
{ .pos_chan = ADC_CHANNEL_9,
.neg_chan = ADC_CHANNEL_GND },
#endif
#if TEST_ADC_CHANNEL_10
{ .pos_chan = ADC_CHANNEL_10,
.neg_chan = ADC_CHANNEL_GND },
#endif
};
#define TEST_COUNT (16 + 10) /* must drop 10 counts */
uint32_t raw_data[TEST_COUNT];
struct bflb_adc_result_s result[TEST_COUNT];
int main(void)
{
board_init();
board_adc_gpio_init();
adc = bflb_device_get_by_name("adc");
/* adc clock = XCLK / 2 / 32 */
struct bflb_adc_config_s cfg;
cfg.clk_div = ADC_CLK_DIV_32;
cfg.scan_conv_mode = false; /* do not support scan mode */
cfg.continuous_conv_mode = true; /* do not support single mode */
cfg.differential_mode = false;
cfg.resolution = ADC_RESOLUTION_16B;
cfg.vref = ADC_VREF_3P2V;
bflb_adc_init(adc, &cfg);
for (size_t i = 0; i < TEST_ADC_CHANNELS; i++) {
printf("ch :%d\r\n", i);
memset(raw_data, 0, sizeof(raw_data));
bflb_adc_feature_control(adc, ADC_CMD_CLR_FIFO, 0);
bflb_adc_channel_config(adc, &chan[i], 1);
bflb_adc_start_conversion(adc);
for (uint16_t j = 0; j < TEST_COUNT; j++) {
while (bflb_adc_get_count(adc) == 0) {
bflb_mtimer_delay_ms(1);
}
raw_data[j] = bflb_adc_read_raw(adc);
}
bflb_adc_stop_conversion(adc);
bflb_adc_parse_result(adc, raw_data, result, TEST_COUNT);
for (size_t k = 10; k < TEST_COUNT; k++) {
printf("raw data:%08x\r\n", raw_data[k]);
printf("pos chan %d,%d mv \r\n", result[k].pos_chan, result[k].millivolt);
}
}
bflb_adc_deinit(adc);
while (1) {
}
}FAQ
The measured voltage is always 0
Make sure the signal is connected to a valid ADC channel pin and shares ground with the board; the example scans all channels, so find the data for the channel you wired in the serial output.
The input voltage exceeds the range
The reference is 3.2V — do not input more than 3.2V or the pin may be damaged. Use a resistor divider for higher voltages.
The readings fluctuate
Some fluctuation is normal in continuous conversion. In a real project, sample multiple times and average; you can also use differential mode or lower the clock divider for better stability.
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

