Skip to content

概念先知道

  • ADC(模数转换):把连续的模拟电压变成数字值。例程 16 位分辨率,读数范围 0~65535。
  • 参考电压(VREF):ADC 的量程上限,例程为 3.2V——输入电压不要超过它,否则可能损坏引脚。
  • 连续转换 vs 单次转换:连续模式不停采样,适合持续监测;查询模式就是程序主动读取结果。

例程功能简介

本页对应博流官方 SDK 的 adc_poll 例程(examples/peripherals/adc/adc_poll),演示 ADC 查询模式采样:

  • 配置为连续转换模式、16 位分辨率、参考电压 3.2V(ADC_VREF_3P2V);
  • 依次对 ADC 通道 0~10 采样(负端接 GND),每个通道取 26 个点(丢弃前 10 个不稳定点);
  • 通过 bflb_adc_parse_result 把原始值换算成毫伏电压并打印。
  • 同族例程examples/peripherals/adc/):adc_dma(DMA 采样)、adc_int(中断采样)、adc_poll_onechan(单通道)、adc_poll_diff_mode(差分模式)、adc_tsen(温度传感器)、adc_vbat(电池电压)。

操作步骤

1
硬件准备

ADC 采样引脚由 board_adc_gpio_init() 配置。把被测电压(不要超过 3.2V)接到任一 ADC 通道引脚,并确保与开发板共地。

2
进入例程目录

在终端进入 SDK 的 ADC 例程目录(前提:已按快速开始(Linux)Windows搭建好环境):

cd examples/peripherals/adc/adc_poll
3
编译工程

执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:

make CHIP=bl616 BOARD=bl616dk
4
烧录固件

用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
5
运行验证

打开串口助手(波特率 2000000),例程会依次对 ADC 通道 0~10 采样并打印原始值 raw data 与换算后的电压 mv,可对照被测电压验证精度。

代码执行流程

例程从启动到运行的完整流程如下(图中的循环箭头表示反复执行):

例程调用的 API 介绍

bflb_device_get_by_name("adc")

获取 ADC 外设的设备句柄。

参数

  • name:外设名称字符串,ADC 固定传 "adc"

返回值struct bflb_device_s * 设备句柄

bflb_adc_init(adc, config)

按配置初始化 ADC。struct bflb_adc_config_s 中:

  • clk_div:时钟分频,ADC_CLK_DIV_32
  • scan_conv_mode:扫描模式,例程关闭
  • continuous_conv_mode:连续转换,例程使能
  • differential_mode:差分模式,例程关闭(单端)
  • resolution:分辨率,ADC_RESOLUTION_16B
  • vref:参考电压,ADC_VREF_3P2V

参数

  • adc:ADC 设备句柄
  • config:配置结构体指针

返回值:成功返回 0;失败返回负值错误码

bflb_adc_channel_config(adc, chan, 1)

配置采样通道。struct bflb_adc_channel_spos_chan 为正向通道(如 ADC_CHANNEL_1),neg_chan 为负向通道(单端接 ADC_CHANNEL_GND)。

参数

  • adc:ADC 设备句柄
  • chan:通道配置结构体数组
  • num:通道数量,例程每次配置 1 个

返回值:成功返回 0;失败返回负值错误码

bflb_adc_start_conversion(adc)

启动转换;之后用 bflb_adc_get_count(adc) 查询是否有数据,bflb_adc_read_raw(adc) 读取原始值。转换结束后调用 bflb_adc_stop_conversion(adc) 停止。

参数

  • adc:ADC 设备句柄

返回值:成功返回 0;失败返回负值错误码

bflb_adc_parse_result(adc, raw_data, result, count)

把原始采样值换算为电压结果。

参数

  • adc:ADC 设备句柄
  • raw_data:原始数据数组
  • resultstruct bflb_adc_result_s 结果数组,包含 pos_chan(通道号)与 millivolt(毫伏电压)
  • count:数据个数

返回值:成功返回 0;失败返回负值错误码

bflb_adc_deinit(adc)

采样结束后关闭 ADC 外设。

参数

  • adc:ADC 设备句柄

返回值:成功返回 0;失败返回负值错误码

完整代码

以下为 adc_poll/main.c 完整源码,与官方示例(examples/peripherals/adc/adc_poll)一致;点灯引脚已适配 Ai-M61/62-32S-Kit 板载 RGB 灯,默认折叠,点击展开:

📜 点击展开 adc_poll/main.c 完整代码
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

测量电压一直为 0

确认被测信号已接到有效的 ADC 通道引脚且与开发板共地;例程默认扫描全部通道,请在串口输出中找到对应通道号的数据。

输入电压超过量程

参考电压为 3.2V,输入电压不要超过 3.2V,否则会损坏引脚。需要测更高电压时,请使用电阻分压后再接入。

读数波动大

连续转换模式下读数本身有波动属正常现象,实际项目可多次采样取平均;也可以改用差分模式或降低时钟分频提高稳定性。

遇到问题?

如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

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