跳转至

adc开发指南

本文档以 adc_demo.c 为例,带你在 HiDiTing 开发板上快速跑通第一个 ADC(模数转换器)控制应用,并了解如何基于它构建自己的应用。


ADC 驱动背景知识

ADC 工作原理

ADC(Analog-to-Digital Converter 模数转换器)实现对外部模拟信号转换成一定比例的数字值,从而实现对模拟信号的测量,可应用于电量检测、按键检测等,支持手动采样和自动扫描,功能场景如下:

功能 说明 应用场景
手动采样 通过软件触发单次采样,可配置采样次数和丢弃次数 电池电压检测、传感器数据采集
自动扫描 硬件自动连续采样,通过回调函数通知结果 连续波形采集、实时监控
通道选择 选择 ADC 通道进行采样(2/3) 多路模拟信号采集
电压转换 将采样值转换为实际电压值 电压测量、信号分析

采样模式说明:

  • 手动采样:通过 uapi_adc_manual_sample 接口触发单次采样,可配置采样次数(1-100000)和丢弃次数(0-10000),最终返回平均采样值和转换电压;ADC自动采样支持的采样频率有100Hz、200Hz、250Hz、500Hz、1000Hz。
  • 自动扫描:通过 uapi_adc_auto_scan_ch_enable 接口启动硬件自动采样,采样结果通过回调函数返回,支持持续采样和单次采样模式。

  • 注意事项:ADC 自动采样与手动采样互斥。自动采样需要开启宏 CONFIG_ADC_SUPPORT_AUTO_SCANCONFIG_ADC_SUPPORT_LONG_SAMPLE,手动采样需要关闭这两个宏。目标配置文件位于 /src/build/config/target_config/3322/menuconfig/acore,请按实际构建目标修改对应的 .config 文件。

操作流程

本示例支持手动采样和自动扫描两种操作模式,每种操作都遵循标准的初始化流程:

通用初始化步骤(所有操作共享):

  1. ADC 初始化:调用 uapi_adc_init(ADC_CLOCK_500KHZ) 初始化 ADC 模块,设置时钟为 500KHz。
  2. 电源使能:调用 uapi_adc_power_en(AFE_SCAN_MODE_MAX_NUM, true) 启用 ADC 电源。

各操作特有流程:

操作 流程
手动采样 禁用自动扫描 → 打开通道 → 丢弃指定次数采样 → 采集指定次数采样 → 计算平均值 → 转换为电压 → 关闭通道 → 禁用电源
启动自动扫描 启用电源 → 配置扫描参数 → 注册回调函数 → 使能自动扫描通道
停止自动扫描 设置停止标志 → 禁用自动扫描通道

接口说明:本示例基于AT命令框架实现,通过串口发送AT命令控制ADC,每个操作函数都封装了完整的初始化、配置、操作和验证流程。

通道与参数说明

参数 范围 说明
channel 2/3 ADC 通道号
sample_num 1-100000 手动采样次数
sample_discard 0-10000 手动采样丢弃次数
freq 0-7 自动扫描频率配置
print_buffer 0/1 是否打印缓冲区所有数据
continue_sample 0/1 是否持续采样

API 接口列表

本文档示例代码中使用的接口:

接口函数 说明
uapi_adc_init 初始化 ADC 模块
uapi_adc_deinit 去初始化 ADC
uapi_adc_power_en ADC 电源使能/禁用
uapi_adc_open_channel 打开 ADC 通道
uapi_adc_close_channel 关闭 ADC 通道
uapi_adc_manual_sample 手动采样
uapi_adc_auto_scan_ch_enable 启动自动扫描
uapi_adc_auto_scan_ch_disable 停止自动扫描
uapi_adc_auto_scan_disable 禁用自动扫描
uapi_adc_auto_scan_is_enabled 查询自动扫描状态

完整 API 列表

更多 ADC 接口请参考:ADC API 参考


快速跑通 ADC Demo

功能说明

ADC Demo 通过 AT 命令方式提供交互式控制,支持以下命令:

AT 命令 功能 参数说明
AT+GETADCDEMOMANUAL=channel,sample_num,sample_discard ADC 手动采样 channel: 通道号(2/3), sample_num: 采样次数(1-100000), sample_discard: 丢弃次数(0-10000)
AT+STARTADCDEMOAUTO=channel,freq,print_buffer,continue_sample 启动 ADC 自动扫描 channel: 通道号(2/3), freq: 频率(0-7), print_buffer: 是否打印(0-1), continue_sample: 是否持续(0-1)
AT+STOPADCDEMOAUTO=channel 停止 ADC 自动扫描 channel: 通道号(2/3)

编译

完成一站式 CLI 环境配置后执行:

# 编译固件,编译生成的固件从 output/3322/fwpkg 中获取 diting-community.fwpkg
fbb set-target pack_diting_community
fbb build

构建成功后,使用一站式 CLI 烧写固件并打开 UART2 串口监视器。以下为 Windows USB DFU 示例;将 COM3 替换为实际日志串口,其他平台和串口烧写参数参见一站式 CLI 开发环境使用指南

fbb flash -f "$env:FBB_SDK_DIR\output\3322\fwpkg\diting-community.fwpkg" --chip 3322 -d --timeout 180
fbb monitor --port COM3 --baud 750000

使用方式

  1. 烧录固件并启动设备。
  2. 找到adc通道引脚,用可调电源正极给要测试的引脚输入直流电源(不超过1.8V),负极接地:本示例是以adc通道2,输入1.2V电压为例测试。
  3. 通过串口发送 AT 命令,格式如下:

    # ADC 手动采样示例:通道 2,采样 40 次,丢弃 10 次
    AT+GETADCDEMOMANUAL=2,40,10
    
    # 启动 ADC 自动扫描示例:通道 2,频率 4,打印数据,持续采样
    AT+STARTADCDEMOAUTO=2,4,1,1
    
    # 停止 ADC 自动扫描示例:通道 2
    AT+STOPADCDEMOAUTO=2
    

通过串口发送 AT 命令,格式如下

预期结果

ADC 手动采样(AT+GETADCDEMOMANUAL)

ADC 手动采样(AT+GETADCDEMOMANUAL)

ADC 启动自动扫描(AT+STARTADCDEMOAUTO)

ADC 启动自动扫描(AT+STARTADCDEMOAUTO)

ADC 停止自动扫描(AT+STOPADCDEMOAUTO)

ADC 停止自动扫描(AT+STOPADCDEMOAUTO)


文件结构与代码走读

文件职责

文件 作用
samples/native_samples/adc/CMakeLists.txt 定义 adc_sample 组件、依赖头文件和 AT 适配宏。
samples/native_samples/adc/adc_demo.h 定义串口测试参数结构、参数约束和命令表。
samples/native_samples/adc/adc_demo.c 实现手动采样、自动扫描、结果统计和资源释放。
samples/native_samples/adc/README.md 给出完整命令、参数和预期日志。

完整实现以以上 Sample 源文件为准,本文只说明业务必须理解的参数和调用顺序。

关键参数与状态

名称 作用 开发注意事项
channel 选择 ADC 通道。当前示例验证通道 2、3。 应同时确认芯片通道和板级引脚复用关系。
sample_num / sample_discard 控制有效采样数和前置丢弃次数。 丢弃采样可减小切换通道后的瞬态影响;缓冲区大小必须按采样数检查。
adc_scan_config_t.freq 设置自动扫描频率档位。 档位是驱动枚举,不应直接当作 Hz 使用。
g_adc_continue_sample 决定自动扫描回调后是否继续。 停止业务时先清除此状态,再关闭对应通道。
CONFIG_ADC_SUPPORT_AUTO_SCAN 控制自动扫描代码是否参与构建。 手动采样与自动扫描互斥,修改目标配置后重新构建。

adc_manual_args_tadc_start_auto_args_tadc_stop_auto_args_t 只负责把串口参数传给上述业务流程;自定义应用不需要复制 AT 参数结构和命令表。

核心业务流程

手动采样流程

  1. 使用 uapi_adc_init() 初始化 ADC,并确认自动扫描未占用 ADC。
  2. 调用 uapi_adc_power_en() 上电,再用 uapi_adc_open_channel() 打开目标通道。
  3. 按需丢弃前几次结果,循环调用 uapi_adc_manual_sample() 获取有效样本。
  4. 统计有效样本并通过 adc_port_stick_transfer_voltage() 换算电压。
  5. 无论采样成功还是失败,都关闭通道并关闭 ADC 电源。

自动扫描流程

  1. 初始化并上电,填写 adc_scan_config_t,注册采样回调。
  2. 调用 uapi_adc_auto_scan_ch_enable() 启动通道;回调按 length 处理缓冲区,不保存回调结束后失效的地址。
  3. 回调通过 next 决定是否继续;停止业务时调用 uapi_adc_auto_scan_ch_disable()

AT 命令仅用于快速触发和板端回归,命令注册、日志拼装与上述驱动流程解耦。自定义应用的可构建 app_run 入口见下一节。

基于 ADC Demo 开发自己的应用

上面的demo是使用AT指令触发运行,HiDiTing还支持app_run方式触发应用在系统启动时自动运行,以下示例将以app_run的方式开发一个开发者自己的应用

  • app_run(func) 是HiDiTing中应用层注册应用函数的宏,基于 GCC 编译器属性和自定义段区(section)自动注册来实现集中调用应用函数,系统启动时会自动遍历所有用 app_run 注册过的函数并执行,无需在系统 main 函数里逐个调用函数。

代码清单

新建一个 ADC 控制应用(以 my_adc_demo 为例)通常只需要以下改动:

  • 新建 my_adc_demo.c 源文件
  • 新建 my_adc_demo.h 头文件
  • 新建 CMakeLists.txt 源文件
  • my_adc_demo.c 中实现ADC操作函数
  • my_adc_demo.h 中定义宏常量和函数声明
  • CMakeLists.txt 源文件编译规则

my_adc_demo文件结构:

/samples/native_samples/my_adc_demo/
├── my_adc_demo.c
├── my_adc_demo.h
└── CMakeLists.txt

CMakeLists.txt 修改示例

# 在新建的 CMakeLists.txt 中添加应用的源文件
set(SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/my_adc_demo.c
)

set(PUBLIC_HEADER
    ${CMAKE_CURRENT_SOURCE_DIR}
)

set(PRIVATE_HEADER

)

set(PUBLIC_DEFINES
    MY_ADC_DEMO_ENABLE
)

build_component()

关键代码片段

my_adc_demo.c —— ADC 控制实现

#include <stdio.h>
#include <string.h>
#include "errcode.h"
#include "adc_porting.h"
#include "adc.h"
#include "my_adc_demo.h"

#define ADC_TEST_CHANNEL    2   // 测试使用的通道号
#define ADC_SAMPLE_NUM      10  // 采样次数
#define ADC_SAMPLE_DISCARD  2   // 丢弃次数
#define TASK_PRIO           26
#define TASK_STACK_SIZE     0x1000

static void *my_adc_task(const char *arg)
{
    unused(arg);
    printf("my_adc_demo start\n");

    // step1: 初始化ADC,设置时钟为500KHz
    uapi_adc_init(ADC_CLOCK_500KHZ);

#if defined(CONFIG_ADC_SUPPORT_AUTO_SCAN)
    // step2: 禁用自动扫描模式
    uapi_adc_auto_scan_disable();
#endif

    // step3: 启用ADC电源
    uapi_adc_power_en(AFE_SCAN_MODE_MAX_NUM, true);

    // step4: 打开指定通道
    errcode_t ret = uapi_adc_open_channel(ADC_TEST_CHANNEL);
    if (ret == ERRCODE_SUCC) {
        printf("adc open channel success\n");

        // step5: 丢弃指定次数的采样结果
        for (uint32_t i = 0; i < ADC_SAMPLE_DISCARD; i++) {
            (void)uapi_adc_manual_sample(ADC_TEST_CHANNEL);
            uapi_tcxo_delay_ms(3);
        }

        // step6: 执行采样
        uint32_t stick = 0;
        for (uint32_t i = 0; i < ADC_SAMPLE_NUM; i++) {
            uint16_t sample = (uint16_t)uapi_adc_manual_sample(ADC_TEST_CHANNEL);
            printf("sample[%d] = %d\n", i, sample);
            stick += sample;
        }

        // step7: 计算平均值并转换为电压
        stick = stick / ADC_SAMPLE_NUM;
        float voltage = adc_port_stick_transfer_voltage((uint16_t)stick);
        printf("average tick: %d, voltage: %.4f\n", stick, voltage);
    } else {
        printf("uapi_adc_open_channel failed=%d\r\n", ret);
    }

    // step8: 关闭通道并禁用电源
    uapi_adc_close_channel(ADC_TEST_CHANNEL);
    uapi_adc_power_en(AFE_SCAN_MODE_MAX_NUM, false);

    printf("my_adc_demo done\n");
    return NULL;
}

static void my_adc_entry(void)
{
    osal_task *task_handle = NULL;
    osal_kthread_lock();
    task_handle = osal_kthread_create((osal_kthread_handler)my_adc_task, 0, "MyAdcTask", TASK_STACK_SIZE);
    if (task_handle != NULL) {
        osal_kthread_set_priority(task_handle, TASK_PRIO);
    }
    osal_kthread_unlock();
}

app_run(my_adc_entry);

测试验证

完成一站式 CLI 环境配置后执行:

# 编译固件,编译生成的固件从 output/3322/fwpkg 中获取 diting-community.fwpkg
fbb set-target pack_diting_community
fbb build
  1. 烧录固件并启动设备
  2. 通过串口观察输出日志:

说明

  • 因为app_run是在系统启动时运行应用,所以应用的运行日志会被夹在开机启动日志中。
  • 为使该adc应用参与编译,还需在上层CMakeLists.txt文件中以宏MY_ADC_DEMO_ENABLE为条件添加该my_adc_demo文件。
  • MY_ADC_DEMO_ENABLE默认关闭。如需启用新建ADC示例功能,需要在config.py中的diting-community配置项下将其打开。

app_run运行配置

app_run应用默认是关闭的,如需启用此应用,需用户手动在acore.prelds文件中添加 KEEP (*(SORT(.zinitcall.app_run*.init))) 具体参考示意图如下:

apprun运行配置

acore.prelds


注意事项

ADC 初始化注意事项:

  • 使用前必须调用 uapi_adc_init(ADC_CLOCK_500KHZ) 完成 ADC 初始化。
  • 必须调用 uapi_adc_power_en(AFE_SCAN_MODE_MAX_NUM, true) 启用 ADC 电源。
  • 采样完成后必须调用 uapi_adc_close_channel() 关闭通道,并调用 uapi_adc_power_en(AFE_SCAN_MODE_MAX_NUM, false) 禁用电源。

手动采样注意事项:

  • 手动采样前建议先丢弃若干次采样结果,以消除初始采样不稳定带来的误差。
  • 采样结果中值为 0 的数据会被视为无效数据并自动排除。
  • 采样间隔至少为 3ms,以确保采样稳定性。

自动扫描注意事项:

  • 自动扫描功能需要硬件支持,通过宏 CONFIG_ADC_SUPPORT_AUTO_SCAN 控制。
  • 启动自动扫描前需要先初始化 ADC 并启用电源。
  • 回调函数中通过 *next 参数控制是否继续采样。
  • 停止自动扫描时,需要先设置 g_adc_continue_sample = false,再禁用通道。

AT 命令注意事项:

  • 命令名称为大写字母,如 GETADCDEMOMANUAL
  • 参数使用逗号分隔,如 AT+GETADCDEMOMANUAL=2,10,2
  • 参数范围:channel 为 2/3,sample_num 为 1-100000,sample_discard 为 0-10000。

常见错误

错误现象 原因 解决方法
undefined reference to 'uapi_adc_init' 未链接 ADC 驱动库 检查 CMakeLists.txt 中是否正确添加了头文件路径
undefined reference to 'uapi_at_cmd_table_register' 未链接 AT 命令库 检查 CMakeLists.txt 中是否正确添加了 AT 相关头文件路径
编译报 at_ret_t 未定义 未包含 at.h 头文件 添加 #include "at.h"
串口无输出 AT 命令未注册 确认调用了 at_diting_adc_example_cmd_register()
采样结果全为 0 ADC 通道未正确打开或电源未使能 确认依次调用了 uapi_adc_init()uapi_adc_power_en()uapi_adc_open_channel()
自动扫描无回调 自动扫描功能未启用 确认宏 CONFIG_ADC_SUPPORT_AUTO_SCAN 已定义