
在 Zephyr RTOS 中使用 ADC 主要有三种官方示例所展示的标准方式它们可以满足从简单到复杂的各类采集需求。下面的表格可以帮你快速了解它们的区别特性方式一: ADC with devicetree方式二: ADC sequence sample方式三: Generic ADC stream核心思路通过设备树 (devicetree) 静态配置通道简化代码在代码中动态配置通道进行单次或多次序列采样使用流式API (Stream API)持续、高效地采集数据配置方式设备树(.dts/.overlay) 中定义io-channels和通道参数C 代码中填充adc_channel_cfg结构体设备树中为 ADC 设备添加adc0别名典型场景推荐方式通道固定、配置不变的工程需要动态调整通道或采样顺序的场景需要高频率、持续采样如音频、传感器数据流上手难度低中高 方式一ADC with devicetree推荐这是 Zephyr最推荐的使用方式通过设备树将硬件配置与代码逻辑分离使代码更简洁、可移植性更强。你的vbat.c正是采用了这种方式。核心步骤设备树配置在板级或应用 Overlay 文件中于zephyr,user节点下通过io-channels属性指定 ADC 通道。通道的具体参数如增益、参考电压也在设备树中配置。代码实现使用ADC_DT_SPEC_GET_BY_IDX等宏自动从设备树生成通道规格数组adc_dt_spec。调用adc_channel_setup_dt()和adc_sequence_init_dt()进行初始化。调用adc_read_dt()触发读取。优点代码与硬件解耦修改通道或参数时无需改动 C 代码只需更新设备树文件。 方式二ADC sequence sample此方式更侧重于在C 代码中灵活控制采样序列适用于需要动态配置通道或采样顺序的场景。核心步骤设备获取直接通过DEVICE_DT_GET()或device_get_binding()获取 ADC 设备实例。通道配置在代码中填充adc_channel_cfg结构体并调用adc_channel_setup()进行配置。定义序列填充adc_sequence结构体指定缓冲区、通道等。可通过adc_sequence_options设置采样间隔等高级选项。执行读取调用adc_read()触发采样。 方式三Generic ADC stream这是为需要持续、高速数据流的场景如音频采集设计的高效 API。核心步骤设备树别名需要为 ADC 设备设置adc0别名。流式读取使用专门的 Stream API 来管理和处理持续涌入的 ADC 数据。除了以上三种主要方式Zephyr 的 ADC 驱动还提供了一些高级特性异步读取支持不阻塞线程的异步读取完成后通过回调函数通知。连续采样模式通过配置adc_sequence_options的extra_samplings等字段可在一次请求中完成多次采样。内部温度传感器部分 SoC 支持读取内部温度传感器可与普通 ADC 采样功能共存。DMA 支持许多 ADC 驱动底层实现了 DMA 支持可高效传输大量数据。 总结与建议对于大多数嵌入式应用强烈推荐使用“方式一ADC with devicetree”。这是 Zephyr 的现代开发范式能最大程度提升代码的可移植性和可维护性。下面是方式一参考程序/** Copyright (c) 2020 Libre Solar Technologies GmbH** SPDX-License-Identifier: Apache-2.0*/#include inttypes.h#include stddef.h#include stdint.h#include zephyr/device.h#include zephyr/devicetree.h#include zephyr/drivers/adc.h#include zephyr/kernel.h#include zephyr/sys/printk.h#include zephyr/sys/util.h#if !DT_NODE_EXISTS(DT_PATH(zephyr_user)) || \!DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels)#error No suitable devicetree overlay specified#endif#define DT_SPEC_AND_COMMA_FOR_INPUTS(node_id, prop, idx) \COND_CODE_1(DT_PHA_HAS_CELL_AT_IDX(node_id, prop, idx, input), \(ADC_DT_SPEC_GET_BY_IDX(node_id, idx),), ())/* Data of ADC io-channels specified in devicetree. */static const struct adc_dt_spec adc_channels[] {DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels,DT_SPEC_AND_COMMA_FOR_INPUTS)};int main(void){int err;uint32_t count 0;uint32_t buf 0;struct adc_sequence sequence {.buffer buf,/* buffer size in bytes, not number of samples */.buffer_size sizeof(buf),#if CONFIG_SAMPLE_ADC_CALIBRATE_REQUIRED.calibrate true,#endif};/* Configure channels individually prior to sampling. */for (size_t i 0U; i ARRAY_SIZE(adc_channels); i) {if (!adc_is_ready_dt(adc_channels[i])) {printk(ADC controller device %s not ready\n, adc_channels[i].dev-name);return 0;}err adc_channel_setup_dt(adc_channels[i]);if (err 0) {printk(Could not setup channel #%d (%d)\n, i, err);return 0;}}#ifndef CONFIG_COVERAGEwhile (1) {#elsefor (int k 0; k 10; k) {#endifprintk(ADC reading[%u]:\n, count);for (size_t i 0U; i ARRAY_SIZE(adc_channels); i) {int32_t val_mv;/** Clear buffer before reading. This ensures the upper 16-bits will be zero* when the adc uses a 16-bit buffer size.*/buf 0;printk(- %s, channel %d: ,adc_channels[i].dev-name,adc_channels[i].channel_id);(void)adc_sequence_init_dt(adc_channels[i], sequence);err adc_read_dt(adc_channels[i], sequence);if (err 0) {printk(Could not read (%d)\n, err);continue;}/** If using differential mode, the 16 bit value* in the ADC sample buffer should be a signed 2s* complement value.*/if (adc_channels[i].channel_cfg.differential) {val_mv (int32_t)((int16_t)buf);} else {val_mv (int32_t)buf;}printk(%PRId32, val_mv);err adc_raw_to_millivolts_dt(adc_channels[i],val_mv);/* conversion to mV may not be supported, skip if not */if (err 0) {printk( (value in mV not available)\n);} else {printk( %PRId32 mV\n, val_mv);}}k_sleep(K_MSEC(1000));}return 0;}