Skip to content

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_result converts 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

1
Prepare the Hardware

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.

2
Enter the Example Directory

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_poll
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). 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_32
  • scan_conv_mode: scan mode, disabled in the example
  • continuous_conv_mode: continuous conversion, enabled
  • differential_mode: differential mode, disabled (single-ended)
  • resolution: resolution, ADC_RESOLUTION_16B
  • vref: reference voltage, ADC_VREF_3P2V

Parameters:

  • adc: ADC device handle
  • config: 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 handle
  • chan: channel config array
  • num: 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 handle
  • raw_data: raw data array
  • result: struct bflb_adc_result_s array containing pos_chan (channel) and millivolt (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
c
#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

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