当前位置: 首页 > news >正文

zephyr实现ADC方式有那些?

在 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正是采用了这种方式。

  • 核心步骤

    1. 设备树配置:在板级或应用 Overlay 文件中,于zephyr,user节点下通过io-channels属性指定 ADC 通道。通道的具体参数(如增益、参考电压)也在设备树中配置。

    2. 代码实现

      • 使用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 代码中灵活控制采样序列,适用于需要动态配置通道或采样顺序的场景。

  • 核心步骤

    1. 设备获取:直接通过DEVICE_DT_GET()device_get_binding()获取 ADC 设备实例。

    2. 通道配置:在代码中填充adc_channel_cfg结构体,并调用adc_channel_setup()进行配置。

    3. 定义序列:填充adc_sequence结构体,指定缓冲区、通道等。可通过adc_sequence_options设置采样间隔等高级选项。

    4. 执行读取:调用adc_read()触发采样。

🌊 方式三:Generic ADC stream

这是为需要持续、高速数据流的场景(如音频采集)设计的高效 API。

  • 核心步骤

    1. 设备树别名:需要为 ADC 设备设置adc0别名。

    2. 流式读取:使用专门的 Stream API 来管理和处理持续涌入的 ADC 数据。

除了以上三种主要方式,Zephyr 的 ADC 驱动还提供了一些高级特性:

  • 异步读取:支持不阻塞线程的异步读取,完成后通过回调函数通知。

  • 连续采样模式:通过配置adc_sequence_optionsextra_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_COVERAGE

while (1) {

#else

for (int k = 0; k < 10; k++) {

#endif

printk("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 2's

* 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;

}

http://www.jsqmd.com/news/1107072/

相关文章:

  • CNN端到端2D路径规划:从图像直觉到嵌入式实时导航
  • 工业防爆监控系统选型技术方案|北京易燃易爆场景落地参考
  • 联合疗法新突破 | tDCS协同针刺重塑膝骨关节炎患者脑网络连接,镇痛效果优于单一疗法
  • U535992 J-C 小梦的宝石收集
  • 自动售货机总是卡货?教你几招轻松搞定~YH
  • 什么是联盟营销(Affiliate Marketing)?2026海内外创作者商业化指南
  • 从Markdown到PDF:前端Canvas排版优化实践
  • 基于STM32单片机智能窨井盖井报警系统 倾斜角度水位气体WIFI 2(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)
  • navicat执行事务操作,不小心关闭session窗口后事务结果如何?
  • 销售讲不清产品内部结构?3D展示让客户一眼看透
  • Cognee — 开源 AI 记忆知识库平台
  • 上就是代码的解析,相信很多人疑惑的地方就是Vector128.Max做了什么,我们可以构造一个代码,让大家简单的看出来发生了什么。代码和运行结果如下所示:
  • AI驱动的销售商机管理工具DingTalkA1实战解析
  • API 服务端数据库全表设计与 SQL 实现
  • QQ群聊天记录分析终极指南:三分钟解锁你的群聊数据洞察力
  • 计算机毕业设计之基于大数据加护的国产美妆行业发展状况研究
  • 多款远程桌面工具实测分享,谁才是打工人心中 TOP1?
  • AI Agent 开发实战:用LangChain4j构建你的第一个Java智能体
  • 无犯罪公证书在哪里办理?无犯罪公证书材料是啥?
  • 从小智停服说起:AI精神陪伴与社交产品硬伤分析
  • 从记忆到人格现行:我如何设计一个会“长出性格”的陪伴智能体
  • 天龙八部单机版GM工具终极指南:5分钟掌握游戏数据管理
  • 突破万亿Token!中国大模型“Token出海”大爆发,开发者如何搭上这趟红利快车?
  • GPT-5发布:当AI能操控你的整个桌面,运维还能信谁?
  • PDF 加盖骑缝章时如何使用数字签名
  • 基于 RBAC 的细粒度工具访问控制:MCP 权限模型与安全策略实施
  • ISO 13355:2016简单介绍,ISO 13355标准是啥
  • PvZ Tools:重新定义你的植物大战僵尸游戏体验
  • 游戏运营的核心资产:当玩家信任成为长线运营的胜负手
  • 数据库的种类