diff --git a/_scripts/db.json b/_scripts/db.json index e36b2d24420b62e75c9b62832a0b899452f0702d..25ed06a8316b3ca2b1a3f7f00a2cdf4cc393d234 100644 --- a/_scripts/db.json +++ b/_scripts/db.json @@ -48,7 +48,7 @@ var url = { "programming-manual/at/at.md": "rt-thread-version/rt-thread-standard/programming-manual/at/at.md", "programming-manual/basic/basic.md": "rt-thread-version/rt-thread-standard/programming-manual/basic/basic.md", "programming-manual/device/device.md": "rt-thread-version/rt-thread-standard/programming-manual/device/device.md", - "programming-manual/device/adc/adc.md": "rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc.md", + "programming-manual/device/adc/adc_v1/adc.md": "rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1/adc.md", "programming-manual/device/audio/audio.md": "rt-thread-version/rt-thread-standard/programming-manual/device/audio/audio.md", "programming-manual/device/can/can.md": "rt-thread-version/rt-thread-standard/programming-manual/device/can/can.md", "programming-manual/device/crypto/crypto.md": "rt-thread-version/rt-thread-standard/programming-manual/device/crypto/crypto.md", diff --git a/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/nano-device-adc.md b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/nano-device-adc.md index ec17ed1f279ce90c752dd45af5136816fc578fa3..7d93331ce5941520f7039d4a8eb5697c3c6afbe1 100644 --- a/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/nano-device-adc.md +++ b/rt-thread-version/rt-thread-nano/nano-ref/nano-device-adc/nano-device-adc.md @@ -19,7 +19,7 @@ ## ADC 设备接口 -1. 在 RT-Thread 标准版中,[ADC 设备](../../../rt-thread-standard/programming-manual/device/adc/adc.md) 驱动提供了一套设备管理接口来访问 ADC,用户程序可以直接使用该 API 操作 ADC 的功能,设备管理接口如下: +1. 在 RT-Thread 标准版中,[ADC 设备](../../../rt-thread-standard/programming-manual/device/adc/adc_v1/adc.md) 驱动提供了一套设备管理接口来访问 ADC,用户程序可以直接使用该 API 操作 ADC 的功能,设备管理接口如下: |**函数**|**描述** | | --------------- | ------------------ | diff --git a/rt-thread-version/rt-thread-standard/_sidebar.md b/rt-thread-version/rt-thread-standard/_sidebar.md index f41d65ac1e8118c9dfd042f1381044d601b2ceba..427aa5288b497b1a81e8adc9aa3fe30a8a1010b0 100644 --- a/rt-thread-version/rt-thread-standard/_sidebar.md +++ b/rt-thread-version/rt-thread-standard/_sidebar.md @@ -50,7 +50,8 @@ - [UART设备](/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart_v1/uart.md) - [UART设备V2版本](/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart_v2/uart.md) - [PIN设备](/rt-thread-version/rt-thread-standard/programming-manual/device/pin/pin.md) - - [ADC设备](/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc.md) + - [ADC设备](/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1/adc.md) + - [ADC设备V2版本](/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/adc.md) - [DAC设备](/rt-thread-version/rt-thread-standard/programming-manual/device/dac/dac.md) - [CAN设备](/rt-thread-version/rt-thread-standard/programming-manual/device/can/can.md) - [HWTIMER设备](/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer.md) diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc.md b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1/adc.md similarity index 100% rename from rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc.md rename to rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1/adc.md diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/adc-p.png b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1/figures/adc-p.png similarity index 100% rename from rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/adc-p.png rename to rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1/figures/adc-p.png diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/adc.vsdx b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1/figures/adc.vsdx similarity index 100% rename from rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/adc.vsdx rename to rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1/figures/adc.vsdx diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/open_other.png b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1/figures/open_other.png similarity index 100% rename from rt-thread-version/rt-thread-standard/programming-manual/device/adc/figures/open_other.png rename to rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1/figures/open_other.png diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/adc.md b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/adc.md new file mode 100644 index 0000000000000000000000000000000000000000..1032926a5e6a0144a4e94f8fe43d84c1f8670fa5 --- /dev/null +++ b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/adc.md @@ -0,0 +1,1337 @@ +# ADC 设备 V2 版本 + +> **说明**:本文面向 ADC V2 的使用与 BSP 适配说明。ADC V2 当前已有 STM32 HAL 后端;其他芯片或 BSP 需要完成对应 backend 适配后才能使用。 +> +> **阅读建议**:先通过 MSH 命令理解设备、session、sequence、stream、trigger 的关系,再在应用代码中使用对应 API。 + +## ADC 简介 + +ADC(Analog-to-Digital Converter,模数转换器)用于把连续变化的模拟信号转换为离散数字量。应用程序通常读取 ADC 原始采样值,再结合参考电压、分辨率和外部分压比例换算成实际电压。 + +常见概念如下: + +| 概念 | 描述 | +| --- | --- | +| 分辨率 | ADC 输出数字量的位数,例如 8 bit、10 bit、12 bit、16 bit。分辨率越高,可区分的量化等级越多。 | +| 原始采样值 | ADC 转换后的数字量。原始值本身不是电压,需要结合参考电压和分辨率换算。 | +| 参考电压 | ADC 换算电压时使用的参考值。ADC V2 支持设置 fallback VREF,也支持后端根据内部参考通道计算 VDDA。 | +| 转换速率 | 完成一次或一组 ADC 转换的速率,与 ADC 时钟、采样周期、通道数量和触发方式有关。 | + +## ADC V2 简介 + +ADC V2 在传统 ADC 读值能力基础上增加了 session、sequence、voltage、stream、trigger 等模型,用于覆盖多通道采样、同步读取、连续采样、硬件触发和 BSP 后端适配。 + +![ADC V2 使用流程](figures/adc_v2_msh_api_flow.png) + +### ADC V1 与 ADC V2 差异 + +| 项目 | ADC V1 | ADC V2 | +| --- | --- | --- | +| 基本模型 | enable/read/disable 单通道模型 | session/sequence/stream/trigger 模型 | +| 多通道采样 | 应用逐通道调用 | 使用 channel mask 描述一个采样 session | +| 单次采样 | `rt_adc_read()` | `rt_adc_read_sequence()` 或配置 session 后 `rt_device_read()` | +| 电压换算 | 应用侧手工换算或使用旧接口 | `rt_adc_voltage()`、`rt_adc_raw_to_voltage_mv()`、`rt_adc_raw_to_scaled_voltage_mv()` | +| 连续采样 | 无统一 stream API | 支持 latest-frame 和 FIFO stream 策略 | +| 硬件触发 | 无统一 trigger 抽象 | 支持 timer update、timer compare、analog comparator 等 trigger 类型 | +| 调试入口 | `adc probe/enable/read/disable` | `adc probe/config/read/voltage/seq/stream/trigger/special` | + +### 当前后端支持 + +ADC V2 当前已有 STM32 HAL backend。非 STM32 平台如果需要使用 ADC V2,需要由 BSP 提供对应的 ADC V2 backend。 + +STM32 内部通道能力通过 backend-specific `special` 命令提供,见“STM32 内部通道”。本文只说明使用方式和约束,不展开 STM32 寄存器、校准地址和具体系列差异。 + +## 配置 ADC V2 + +### 通用配置项 + +| 配置项 | 描述 | +| --- | --- | +| `RT_USING_ADC` | 使能 ADC 设备驱动框架。 | +| `RT_USING_ADC_V2` | 使能 ADC V2 framework。ADC V2 使用 ADC public namespace,不能与 ADC V1 同时启用。 | +| `RT_ADC_V2_USING_MSH` | 导出 ADC V2 MSH 命令。 | +| `RT_ADC_USING_STREAM` | 使能 ADC stream framework。 | +| `RT_ADC_STREAM_USING_LATEST` | 使能 latest-frame stream 策略。 | +| `RT_ADC_STREAM_USING_FIFO` | 使能 FIFO stream 策略。 | +| `RT_ADC_USING_TRIGGER` | 使能 ADC trigger framework。 | +| `RT_ADC_TRIGGER_USING_TIMER` | 使能 timer update/TRGO 和 timer compare 触发选择。 | +| `RT_ADC_TRIGGER_USING_COMPARE` | 使能 analog comparator 输出触发选择。 | + +### STM32 后端配置项 + +STM32 BSP 侧通常需要同时打开 ADC V2 framework 和 STM32 ADC V2 backend。配置项名称以实际 BSP 为准,常见组合如下: + +```text +CONFIG_RT_USING_ADC_V2=y +CONFIG_BSP_USING_ADC_V2=y +CONFIG_BSP_USING_ADC1=y +CONFIG_RT_ADC_V2_USING_MSH=y +CONFIG_RT_ADC_USING_STREAM=y +CONFIG_RT_ADC_STREAM_USING_LATEST=y +CONFIG_RT_ADC_STREAM_USING_FIFO=y +CONFIG_BSP_ADC1_USING_DMA=y +CONFIG_BSP_ADC_USING_TRIGGER=y +CONFIG_BSP_ADC_USING_TIMER_TRIGGER=y +CONFIG_BSP_ADC_USING_ANALOG_COMPARE_TRIGGER=y +``` + +实际 BSP 需要根据芯片系列、ADC instance、DMA、timer、comparator 支持情况裁剪配置项。 + +## 通过 MSH 命令了解 ADC V2 + +MSH 命令可以直接反映 ADC V2 的使用流程。建议先用 MSH 跑通,再写应用代码。 + +### MSH 命令列表 + +| 命令 | 描述 | +| --- | --- | +| `adc probe ` | 选择一个已注册的 ADC 设备。 | +| `adc close` | 关闭当前选中的 ADC 设备。 | +| `adc config [num1 ...]` | 使用通道号配置当前 session。通道号范围为 `0~31`。 | +| `adc read` | 读取当前 session 的原始采样值。 | +| `adc voltage ` | 读取当前 session,并转换为 mV。 | +| `adc seq [num1 ...]` | 对指定通道执行一次有限 sequence 采样。 | +| `adc stream start [num1 ...]` | 启动 stream 采样。 | +| `adc stream read [timeout_ms]` | 读取 stream 数据。 | +| `adc stream cancel` | 取消阻塞中的 stream 读取。 | +| `adc stream stop` | 停止 stream 采样。 | +| `adc trigger set timer_update ` | 设置 timer update/TRGO 触发。 | +| `adc trigger set timer_compare ` | 设置 timer compare 触发。 | +| `adc trigger set compare [comparator_device] ` | 设置 analog comparator 触发。 | +| `adc trigger clear` | 清除已缓存的 trigger 请求。 | +| `adc trigger status` | 查询当前 ADC 设备是否已配置 trigger。 | +| `adc special list` | BSP 后端命令,列出当前设备支持的内部通道项。 | +| `adc special read [timeout_ms]` | BSP 后端命令,读取指定内部通道项。 | + +### MSH 与 API 对应关系 + +| MSH 命令 | 对应能力 | 主要 API / 机制 | +| --- | --- | --- | +| `adc probe ` | 查找并打开 ADC 设备 | `rt_device_find()`、`rt_device_open()` | +| `adc close` | 关闭 ADC 设备 | `rt_device_close()` | +| `adc config ` | 配置当前 session | `rt_device_control(..., RT_ADC_CMD_SET_SESSION, ...)` | +| `adc read` | 读取当前 session 原始值 | `rt_device_read()` | +| `adc voltage ` | 读取当前 session 并换算 mV | `rt_adc_voltage()` | +| `adc seq ` | 一次有限 sequence 采样 | `rt_adc_read_sequence()` | +| `adc stream start ` | 启动 stream | `rt_adc_stream_start()` | +| `adc stream read [timeout_ms]` | 读取 stream 数据 | `rt_adc_stream_read()` | +| `adc stream cancel` | 唤醒阻塞读取 | `rt_adc_stream_cancel()` | +| `adc stream stop` | 停止 stream | `rt_adc_stream_stop()` | +| `adc trigger set ...` | 设置硬件触发请求 | `rt_adc_trigger_set()` | +| `adc trigger clear` | 清除硬件触发请求 | `rt_adc_trigger_clear()` | +| `adc trigger status` | 查询硬件触发请求 | `rt_adc_trigger_is_set()` | +| `adc special list/read` | 后端私有能力 | BSP backend-specific MSH 实现 | + +### 基本 MSH 使用流程 + +```shell +msh > adc probe adc1 +msh > adc config 0 1 +msh > adc read +msh > adc voltage 1000 +msh > adc seq 1000 0 1 +``` + +说明: + +- `adc probe` 选择当前操作的 ADC 设备。 +- `adc config` 配置长期 session,后续 `adc read` 和 `adc voltage` 使用这个 session。 +- `adc seq` 是一次性 sequence 读取,不依赖长期 session。 +- 多通道结果按 channel mask 从低位到高位排列。 + +### stream MSH 使用流程 + +```shell +msh > adc probe adc1 +msh > adc stream start 0 1 +msh > adc stream read 1000 +msh > adc stream stop +``` + +stream 有两种策略: + +| 策略 | 描述 | +| --- | --- | +| latest-frame | 适合电池电压、温度、电位器、分压检测、板级状态监测等慢变化信号。DMA buffer 保存最新一帧,旧值可能被覆盖。 | +| FIFO | 适合波形、电流、振动、类音频、控制环等连续采样。DMA 完成块复制到 ringbuffer,读线程按 FIFO 顺序读取。 | + +### trigger MSH 使用流程 + +```shell +msh > adc probe adc1 +msh > adc trigger set timer_update timer2 1000 +msh > adc stream start 0 +msh > adc stream read 1000 +msh > adc stream stop +msh > adc trigger clear +``` + +说明: + +- trigger 通常与 stream 配合使用,用于让外设事件决定 ADC 采样节拍。 +- timer update、timer compare、analog comparator 是否可用取决于 framework 配置项和后端实现。 +- trigger set 只缓存触发请求,不表示已经立即写入 ADC 硬件触发寄存器。 + +## 访问 ADC V2 设备 + +应用程序通过 RT-Thread I/O 设备管理接口访问 ADC V2 设备。相关接口如下: + +| 函数 | 描述 | +| --- | --- | +| `rt_device_find()` | 查找 ADC 设备。 | +| `rt_device_open()` | 打开 ADC 设备。 | +| `rt_device_read()` | 读取当前 session 的原始采样值。 | +| `rt_device_control()` | 控制 ADC 设备,例如配置 session、设置 VREF、读取分辨率。 | +| `rt_device_close()` | 关闭 ADC 设备。 | + +ADC V2 额外提供的接口如下: + +![ADC V2 API 分组](figures/adc_v2_api_map.png) + +| 函数 / 宏 | 描述 | +| --- | --- | +| `RT_ADC_CHANNEL_MASK()` | 根据逻辑通道号构造 channel mask。 | +| `rt_adc_channel_mask_count()` | 统计 channel mask 中选中的通道数量。 | +| `rt_adc_channel_mask_take_lsb()` | 取出 channel mask 中最低位通道。 | +| `rt_adc_session_channel_index()` | 获取指定通道在当前 session 结果中的索引。 | +| `rt_adc_read_sequence()` | 执行一次有限 sequence 采样。 | +| `rt_adc_voltage()` | 读取当前 session 并换算为 mV。 | +| `rt_adc_raw_to_voltage_mv()` | 将单个 raw sample 换算为 mV。 | +| `rt_adc_raw_to_scaled_voltage_mv()` | 将单个 raw sample 按比例缩放后换算为 mV。 | +| `rt_adc_stream_start()` | 启动 stream 采样。 | +| `rt_adc_stream_start_frame_aligned_fifo()` | 启动严格 frame-aligned 的 FIFO stream。 | +| `rt_adc_stream_read()` | 从 active stream 中读取采样值。 | +| `rt_adc_stream_cancel()` | 取消阻塞中的 stream 读取。 | +| `rt_adc_stream_stop()` | 停止 stream 采样。 | +| `rt_adc_trigger_set()` | 设置设备级硬件触发请求。 | +| `rt_adc_trigger_clear()` | 清除设备级硬件触发请求。 | +| `rt_adc_trigger_is_set()` | 查询设备是否存在硬件触发请求。 | +| `rt_hw_adc_register()` | BSP/backend 注册 ADC 设备。 | +| `rt_hw_adc_stream_isr()` | BSP/backend 从 ADC/DMA ISR 向 framework 上报 stream 事件。 | + +## 通用设备 API + +### 查找 ADC 设备 + +```c +rt_device_t rt_device_find(const char *name); +``` + +| 参数 | 描述 | +| --- | --- | +| `name` | ADC 设备名称,例如 `"adc1"`。 | + +| 返回值 | 描述 | +| --- | --- | +| 非 `RT_NULL` | 找到对应设备,返回设备句柄。 | +| `RT_NULL` | 未找到对应设备。 | + +示例: + +```c +rt_adc_device_t adc_dev; + +adc_dev = (rt_adc_device_t)rt_device_find("adc1"); +if (adc_dev == RT_NULL) +{ + rt_kprintf("can't find adc1\n"); + return -RT_ERROR; +} +``` + +### 打开 ADC 设备 + +```c +rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags); +``` + +| 参数 | 描述 | +| --- | --- | +| `dev` | ADC 设备句柄。 | +| `oflags` | 打开标志。ADC 同步读值场景通常使用 `RT_DEVICE_FLAG_RDWR`。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 打开成功。 | +| 其他错误码 | 打开失败。 | + +示例: + +```c +if (rt_device_open((rt_device_t)adc_dev, RT_DEVICE_FLAG_RDWR) != RT_EOK) +{ + rt_kprintf("open adc1 failed\n"); + return -RT_ERROR; +} +``` + +### 读取当前 session 原始采样值 + +```c +rt_ssize_t rt_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size); +``` + +| 参数 | 描述 | +| --- | --- | +| `dev` | ADC 设备句柄。 | +| `pos` | ADC V2 当前不使用该参数,通常传 `0`。 | +| `buffer` | 输出 buffer,通常为 `rt_uint32_t[]`。 | +| `size` | 期望读取的 sample 数量。 | + +| 返回值 | 描述 | +| --- | --- | +| `> 0` | 实际读取的 sample 数量。 | +| `0` | 未读取到数据,需要结合当前错误状态判断原因。 | +| `< 0` | 负数错误码。 | + +使用前需要先通过 `RT_ADC_CMD_SET_SESSION` 配置当前 session。 + +### 控制 ADC 设备 + +```c +rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg); +``` + +| 参数 | 描述 | +| --- | --- | +| `dev` | ADC 设备句柄。 | +| `cmd` | ADC 控制命令,见下表。 | +| `arg` | 命令参数指针,具体类型由 `cmd` 决定。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 控制成功。 | +| 其他错误码 | 控制失败。 | + +ADC V2 控制命令: + +| 控制命令 | `arg` 类型 | 描述 | +| --- | --- | --- | +| `RT_ADC_CMD_GET_RESOLUTION` | `rt_uint8_t *` | 获取 ADC 分辨率,单位 bit。 | +| `RT_ADC_CMD_CALIBRATE` | 后端定义或 `RT_NULL` | 执行 ADC 校准。 | +| `RT_ADC_CMD_SET_VREF` | `rt_uint32_t *` | 设置 fallback VREF,单位 mV。 | +| `RT_ADC_CMD_CALC_VREF` | 后端定义 | 根据 raw VREF sample 计算 VDDA。 | +| `RT_ADC_CMD_GET_VREF_CHANNEL` | `rt_uint8_t *` | 获取后端 VREF logical channel。 | +| `RT_ADC_CMD_SET_SESSION` | `rt_uint32_t *` | 配置当前 session channel mask。 | +| `RT_ADC_CMD_SET_CONFIG` | 后端私有结构 | 设置后端私有配置。 | +| `RT_ADC_CMD_GET_CONFIG` | 后端私有结构 | 获取后端私有配置。 | +| `RT_ADC_CMD_CLEAR_CHANNEL_CONFIG` | `RT_NULL` 或后端定义 | 清除后端 channel/rank 配置。 | + +### 关闭 ADC 设备 + +```c +rt_err_t rt_device_close(rt_device_t dev); +``` + +| 参数 | 描述 | +| --- | --- | +| `dev` | ADC 设备句柄。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 关闭成功。 | +| 其他错误码 | 关闭失败。 | + +## ADC V2 通用 API + +### 构造 channel mask + +```c +#define RT_ADC_CHANNEL_MASK(_channel) (1UL << (rt_uint32_t)(_channel)) +``` + +| 参数 | 描述 | +| --- | --- | +| `_channel` | 逻辑通道号,范围通常为 `0~31`。 | + +| 返回值 | 描述 | +| --- | --- | +| `rt_uint32_t` | 对应通道的 bit mask。 | + +示例: + +```c +rt_uint32_t channels; + +channels = RT_ADC_CHANNEL_MASK(0) | RT_ADC_CHANNEL_MASK(1); +``` + +### 统计 channel mask 中的通道数量 + +```c +rt_size_t rt_adc_channel_mask_count(rt_uint32_t channels); +``` + +| 参数 | 描述 | +| --- | --- | +| `channels` | ADC channel mask。 | + +| 返回值 | 描述 | +| --- | --- | +| `rt_size_t` | mask 中被选中的通道数量。 | + +### 取出最低位通道 + +```c +rt_uint8_t rt_adc_channel_mask_take_lsb(rt_uint32_t *channels); +``` + +| 参数 | 描述 | +| --- | --- | +| `channels` | 输入/输出 channel mask 指针。函数会清除已取出的最低位通道。 | + +| 返回值 | 描述 | +| --- | --- | +| `0~31` | 被取出的逻辑通道号。 | +| `0xff` | 参数为空或 mask 为空。 | + +### 获取通道在 session 结果中的索引 + +```c +rt_err_t rt_adc_session_channel_index(rt_uint32_t session_channels, rt_uint32_t channel, rt_size_t *index); +``` + +| 参数 | 描述 | +| --- | --- | +| `session_channels` | 已配置的 session channel mask。 | +| `channel` | 要查询的单通道 mask,例如 `RT_ADC_CHANNEL_MASK(3)`。 | +| `index` | 输出索引。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 查询成功。 | +| 其他错误码 | 参数无效或目标通道不在 session 中。 | + +该接口适合多通道 session 中按 channel 查询 `rt_device_read()` 或 `rt_adc_voltage()` 输出数组索引。 + +## sequence API + +### sequence 配置结构体 + +```c +struct rt_adc_sequence_cfg +{ + rt_uint32_t *buffer; + rt_size_t buffer_length; + rt_int32_t timeout_ms; +}; +``` + +| 成员 | 描述 | +| --- | --- | +| `buffer` | 输出 sample buffer。 | +| `buffer_length` | 输出 buffer 长度,单位为 sample。 | +| `timeout_ms` | 每个 sample 的超时时间,负数表示一直等待。 | + +### 执行一次有限 sequence 采样 + +```c +rt_err_t rt_adc_read_sequence(rt_adc_device_t device, rt_uint32_t channels, const struct rt_adc_sequence_cfg *cfg, rt_size_t *read_count); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备句柄。 | +| `channels` | 本次 sequence 使用的 channel mask。 | +| `cfg` | sequence 请求配置。 | +| `read_count` | 输出实际读取 sample 数量。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 采样成功。 | +| 其他错误码 | 参数错误、设备忙、后端失败或超时。 | + +示例: + +```c +struct rt_adc_sequence_cfg cfg; +rt_uint32_t channels; +rt_uint32_t values[2]; +rt_size_t read_count; + +channels = RT_ADC_CHANNEL_MASK(0) | RT_ADC_CHANNEL_MASK(1); +rt_memset(&cfg, 0, sizeof(cfg)); +cfg.buffer = values; +cfg.buffer_length = RT_ARRAY_SIZE(values); +cfg.timeout_ms = 1000; + +if (rt_adc_read_sequence(adc_dev, channels, &cfg, &read_count) != RT_EOK) +{ + rt_kprintf("sequence read failed\n"); +} +``` + +## voltage API + +### 原始值换算为 mV + +```c +rt_err_t rt_adc_raw_to_voltage_mv(rt_uint32_t raw_value, rt_uint32_t vref_mv, rt_uint8_t resolution_bits, rt_uint32_t *voltage_mv); +``` + +| 参数 | 描述 | +| --- | --- | +| `raw_value` | ADC 原始采样值。 | +| `vref_mv` | ADC 参考电压,单位 mV。 | +| `resolution_bits` | ADC 分辨率,单位 bit。 | +| `voltage_mv` | 输出电压,单位 mV。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 换算成功。 | +| 其他错误码 | 参数无效或换算失败。 | + +### 原始值按比例缩放后换算为 mV + +```c +rt_err_t rt_adc_raw_to_scaled_voltage_mv(rt_uint32_t raw_value, + rt_uint32_t vref_mv, + rt_uint8_t resolution_bits, + rt_uint32_t scale_num, + rt_uint32_t scale_den, + rt_uint32_t *voltage_mv); +``` + +| 参数 | 描述 | +| --- | --- | +| `raw_value` | ADC 原始采样值。 | +| `vref_mv` | ADC 参考电压,单位 mV。 | +| `resolution_bits` | ADC 分辨率,单位 bit。 | +| `scale_num` | 电压缩放比例分子。 | +| `scale_den` | 电压缩放比例分母。不能为 0。 | +| `voltage_mv` | 输出缩放后的电压,单位 mV。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 换算成功。 | +| `-RT_EINVAL` | 参数无效、比例为 0、换算溢出或输出指针为空。 | +| 其他错误码 | 换算失败。 | + +分压场景示例:ADC 采样到分压后的电压,外部电阻使实际电压为 ADC 输入电压的 2 倍,则可使用 `scale_num = 2`、`scale_den = 1`。 + +### 读取当前 session 并换算为 mV + +```c +rt_err_t rt_adc_voltage(rt_adc_device_t device, rt_uint32_t *voltages_mv, rt_size_t size, rt_int32_t timeout_ms); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备句柄。 | +| `voltages_mv` | 输出电压 buffer,单位 mV。 | +| `size` | 输出 buffer 长度,单位为 sample。 | +| `timeout_ms` | 每个 sample 的超时时间,负数表示一直等待。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 读取并换算成功。 | +| 其他错误码 | 参数错误、session 未配置、后端失败或超时。 | + +注意:该接口读取当前已配置 session,不会自动修改 active channel mask。如果 `default_vref_mv` 为 0,而后端需要通过 VREFINT 计算 VDDA,则当前 session 需要包含后端 VREF logical channel。 + +## stream API + +stream API 需要启用 `RT_ADC_USING_STREAM`。latest-frame 与 FIFO 策略需要分别启用对应配置项。 + +### stream 配置结构体 + +```c +struct rt_adc_stream_cfg +{ + enum rt_adc_stream_policy policy; + enum rt_adc_stream_dma_event_mode dma_event_mode; + rt_uint32_t *dma_buffer; + rt_size_t dma_buffer_length; + rt_uint32_t *fifo_buffer; + rt_size_t fifo_buffer_length; + rt_size_t watermark; + rt_adc_stream_callback_t callback; + void *user_data; +}; +``` + +> 注:具体成员名以当前头文件为准。不同配置项关闭时,FIFO 相关成员不会参与编译。 + +| 成员 | 描述 | +| --- | --- | +| `policy` | stream 策略,取 `RT_ADC_STREAM_POLICY_LATEST` 或 `RT_ADC_STREAM_POLICY_FIFO`。 | +| `dma_event_mode` | DMA 事件模式。 | +| `dma_buffer` | DMA circular staging buffer。latest 模式下必须刚好保存一帧。 | +| `dma_buffer_length` | DMA buffer 长度,单位 sample。 | +| `fifo_buffer` | FIFO storage,仅 FIFO 策略使用。 | +| `fifo_buffer_length` | FIFO buffer 长度,单位 sample。 | +| `watermark` | FIFO 唤醒或 callback 阈值,单位 sample。 | +| `callback` | FIFO 数据 callback,可选。该 callback 在 ISR context 调用,不能阻塞。 | +| `user_data` | 传给 callback 的用户数据。 | + +### stream 策略枚举 + +| 枚举值 | 描述 | +| --- | --- | +| `RT_ADC_STREAM_POLICY_LATEST` | 只保留每个 rank 的最新值。适合慢变化状态采样。 | +| `RT_ADC_STREAM_POLICY_FIFO` | 按 FIFO 保存 DMA 完成块。适合连续、对丢样敏感的采样。 | + +### DMA 事件模式 + +| 枚举值 | 描述 | +| --- | --- | +| `RT_ADC_STREAM_DMA_EVENT_AUTO` | 使用所选 stream 策略的默认事件模式。 | +| `RT_ADC_STREAM_DMA_EVENT_NONE` | 不上报 DMA half/full data event,但 DMA error event 仍有效。 | +| `RT_ADC_STREAM_DMA_EVENT_FULL_ONLY` | 只上报 DMA transfer-complete data event。 | +| `RT_ADC_STREAM_DMA_EVENT_HALF_FULL` | 同时上报 DMA half-transfer 和 transfer-complete data event。 | + +### 启动 stream + +```c +rt_err_t rt_adc_stream_start(rt_adc_device_t device, rt_uint32_t channels, const struct rt_adc_stream_cfg *cfg); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备句柄。 | +| `channels` | stream 采样使用的 channel mask。 | +| `cfg` | stream 配置。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 启动成功。 | +| 其他错误码 | 参数错误、设备忙、后端不支持或启动失败。 | + +### 启动严格 frame-aligned FIFO stream + +```c +rt_err_t rt_adc_stream_start_frame_aligned_fifo(rt_adc_device_t device, rt_uint32_t channels, const struct rt_adc_stream_cfg *cfg); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备句柄。 | +| `channels` | stream 采样使用的 channel mask。 | +| `cfg` | FIFO stream 配置。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 启动成功。 | +| 其他错误码 | 参数错误、DMA/FIFO buffer 不满足 frame-aligned 要求、后端不支持或启动失败。 | + +该接口适合连续采样必须保持严格帧边界的场景。 + +### 读取 stream 数据 + +```c +rt_ssize_t rt_adc_stream_read(rt_adc_device_t device, rt_uint32_t *buffer, rt_size_t sample_count, rt_int32_t timeout_ms); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备句柄。 | +| `buffer` | 输出 sample buffer。 | +| `sample_count` | 期望读取的 sample 数量。 | +| `timeout_ms` | 读取超时时间,负数表示一直等待。 | + +| 返回值 | 描述 | +| --- | --- | +| `>= 0` | 实际读取 sample 数量。 | +| `< 0` | 负数错误码。 | + +### 取消阻塞中的 stream 读取 + +```c +rt_err_t rt_adc_stream_cancel(rt_adc_device_t device); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备句柄。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 取消成功。 | +| 其他错误码 | 取消失败。 | + +### 停止 stream + +```c +rt_err_t rt_adc_stream_stop(rt_adc_device_t device); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备句柄。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 停止成功。 | +| 其他错误码 | 停止失败。 | + +### stream ISR 事件上报 + +```c +rt_err_t rt_hw_adc_stream_isr(rt_adc_device_t device, + enum rt_adc_stream_event event, + const rt_uint32_t *sample_buffer, + rt_size_t sample_count); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备句柄。 | +| `event` | stream 后端事件。 | +| `sample_buffer` | 完成的 sample buffer block。 | +| `sample_count` | `sample_buffer` 中的 sample 数量。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 上报成功。 | +| 其他错误码 | 上报失败。 | + +该接口供 BSP/backend 在 ADC 或 DMA ISR 中调用,应用代码通常不直接使用。 + +### stream 事件枚举 + +| 枚举值 | 描述 | +| --- | --- | +| `RT_ADC_STREAM_EVENT_DMA_HALF` | DMA half-transfer event。 | +| `RT_ADC_STREAM_EVENT_DMA_DONE` | DMA transfer-complete event。 | +| `RT_ADC_STREAM_EVENT_ERROR` | 后端错误事件。 | + +## trigger API + +trigger API 需要启用 `RT_ADC_USING_TRIGGER`。 + +### trigger 类型 + +| 枚举值 | 描述 | +| --- | --- | +| `RT_ADC_TRIGGER_TIMER_UPDATE` | 使用 timer update/TRGO 事件触发 ADC。 | +| `RT_ADC_TRIGGER_TIMER_COMPARE` | 使用 timer compare 事件触发 ADC。 | +| `RT_ADC_TRIGGER_PWM_EDGE` | 使用 PWM edge 事件触发 ADC。 | +| `RT_ADC_TRIGGER_EXTI_EDGE` | 使用外部中断 edge 事件触发 ADC。 | +| `RT_ADC_TRIGGER_ANALOG_COMPARE` | 使用 analog comparator 事件触发 ADC。 | +| `RT_ADC_TRIGGER_BACKEND` | 使用后端私有触发事件。 | + +### trigger edge + +| 枚举值 | 描述 | +| --- | --- | +| `RT_ADC_TRIGGER_EDGE_NONE` | 不需要边沿选择。 | +| `RT_ADC_TRIGGER_EDGE_RISING` | 上升沿触发。 | +| `RT_ADC_TRIGGER_EDGE_FALLING` | 下降沿触发。 | +| `RT_ADC_TRIGGER_EDGE_BOTH` | 双边沿触发。 | + +### trigger 配置结构体 + +```c +struct rt_adc_trigger_cfg +{ + enum rt_adc_trigger_type type; + rt_uint32_t flags; + union + { + struct rt_adc_trigger_timer_event timer; + struct rt_adc_trigger_pwm_event pwm; + struct rt_adc_trigger_exti_event exti; + struct rt_adc_trigger_compare_event compare; + struct rt_adc_trigger_backend_event backend; + } event; +}; +``` + +| 成员 | 描述 | +| --- | --- | +| `type` | trigger 类型。 | +| `flags` | 通用 trigger flags,当前作为预留字段。 | +| `event.timer` | timer trigger 参数。 | +| `event.pwm` | PWM trigger 参数。 | +| `event.exti` | EXTI trigger 参数。 | +| `event.compare` | analog comparator trigger 参数。 | +| `event.backend` | 后端私有 trigger 参数。 | + +### 设置 trigger + +```c +rt_err_t rt_adc_trigger_set(struct rt_adc_device *device, const struct rt_adc_trigger_cfg *cfg); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备句柄。 | +| `cfg` | trigger 配置。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 设置成功。 | +| 其他错误码 | 参数错误、类型不支持或后端不支持。 | + +说明:该接口缓存设备级硬件触发请求,不会立即写 ADC 触发寄存器。后端兼容性通常在下一次转换硬件配置阶段检查。 + +### 清除 trigger + +```c +rt_err_t rt_adc_trigger_clear(struct rt_adc_device *device); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备句柄。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 清除成功。 | +| 其他错误码 | 清除失败。 | + +### 查询 trigger 状态 + +```c +rt_bool_t rt_adc_trigger_is_set(struct rt_adc_device *device); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备句柄。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_TRUE` | 当前设备已缓存 trigger 请求。 | +| `RT_FALSE` | 当前设备没有缓存 trigger 请求。 | + +## STM32 内部通道 + +STM32 HAL 后端提供 backend-specific `special` 命令,用于读取 STM32 内部通道。当前 special item 包含: + +| special item | 含义 | 输出单位 | 描述 | +| --- | --- | --- | --- | +| `vref` | VREFINT | `mV` | 读取内部参考电压通道,并计算 VDDA。 | +| `temp` | 内部温度传感器 | `C` | 读取内部温度传感器 raw sample,并结合 VDDA 换算为摄氏度。 | +| `vbat` | VBAT | `raw` | 读取 VBAT 原始 ADC sample。当前 MSH 命令层输出 raw value。 | + +![STM32 ADC V2 内部通道访问关系](figures/stm32_internal_channels.png) + +### 查看支持的内部通道 + +```shell +msh > adc probe adc1 +msh > adc special list +``` + +`adc special list` 会打印当前 ADC 设备支持的 special item、对应 logical channel、输出单位,以及必要的 `adc config` 提示。 + +可能输出形式: + +```text +vref: channel=17 unit=mV +temp: channel=18 unit=C needs=vref channel=17 +vbat: channel=19 unit=raw +``` + +上述通道号仅用于说明。实际 logical channel 由 STM32 后端根据芯片系列和 ADC instance 映射,必须以 `adc special list` 打印结果为准。 + +### 读取 VREFINT / VDDA + +```shell +msh > adc probe adc1 +msh > adc special list +msh > adc config 17 +msh > adc special read vref 1000 +``` + +说明: + +- `vref` 读取前,当前 session 必须包含 `vref` 对应的 logical channel。 +- MSH 输出单位为 mV。 +- 如果后端不支持 VREF 换算能力,命令会返回不支持或失败。 + +### 读取内部温度传感器 + +```shell +msh > adc probe adc1 +msh > adc special list +msh > adc config 18 17 +msh > adc special read temp 1000 +``` + +说明: + +- `temp` 读取前,当前 session 必须包含温度传感器 logical channel。 +- 温度换算需要 VDDA。VDDA 优先来自同一 session 中的 VREFINT;如果没有 VREFINT,则需要提前配置 `default_vref_mv`。 +- 温度精度受芯片数据手册中的采样时间、校准参数和实际 VDDA 精度影响。 + +### 读取 VBAT + +```shell +msh > adc probe adc1 +msh > adc special list +msh > adc config 19 +msh > adc special read vbat 1000 +``` + +说明: + +- `vbat` 读取前,当前 session 必须包含 VBAT 对应的 logical channel。 +- 当前 MSH special 命令输出 raw sample。 +- 是否需要再乘以硬件内部 VBAT 分压系数,取决于具体芯片系列和后端是否提供进一步换算接口。 + +### special 命令约束 + +| 约束 | 描述 | +| --- | --- | +| 必须先 `adc probe` | special 命令作用于当前选中的 ADC 设备。 | +| 建议先 `adc special list` | 该命令会打印当前设备真正支持的 item 和 logical channel。 | +| `special read` 前必须 `adc config` | 后端读取使用当前 framework session;session 必须包含目标 special channel。 | +| `temp` 需要 VDDA 来源 | 优先使用 VREFINT;否则需要配置 `default_vref_mv`。 | +| item 支持与芯片系列有关 | 不同 STM32 系列的内部通道、校准数据和换算能力不完全一致。 | + +## 示例代码 + +### 示例 1:配置 session 后读取原始值 + +对应 MSH: + +```shell +adc probe adc1 +adc config 0 1 +adc read +``` + +代码: + +```c +#include +#include + +#define ADC_DEV_NAME "adc1" + +static int adc_v2_read_sample(void) +{ + rt_adc_device_t adc_dev; + rt_uint32_t channels; + rt_uint32_t values[2]; + rt_ssize_t size; + + adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME); + if (adc_dev == RT_NULL) + { + rt_kprintf("can't find %s\n", ADC_DEV_NAME); + return -RT_ERROR; + } + + if (rt_device_open((rt_device_t)adc_dev, RT_DEVICE_FLAG_RDWR) != RT_EOK) + { + rt_kprintf("open %s failed\n", ADC_DEV_NAME); + return -RT_ERROR; + } + + channels = RT_ADC_CHANNEL_MASK(0) | RT_ADC_CHANNEL_MASK(1); + if (rt_device_control((rt_device_t)adc_dev, RT_ADC_CMD_SET_SESSION, &channels) != RT_EOK) + { + rt_kprintf("config adc session failed\n"); + rt_device_close((rt_device_t)adc_dev); + return -RT_ERROR; + } + + size = rt_device_read((rt_device_t)adc_dev, 0, values, RT_ARRAY_SIZE(values)); + if (size != RT_ARRAY_SIZE(values)) + { + rt_kprintf("read adc failed: %d\n", (int)size); + rt_device_close((rt_device_t)adc_dev); + return -RT_ERROR; + } + + rt_kprintf("ch0=%lu ch1=%lu\n", values[0], values[1]); + rt_device_close((rt_device_t)adc_dev); + return RT_EOK; +} +MSH_CMD_EXPORT(adc_v2_read_sample, read adc v2 session sample); +``` + +### 示例 2:单次 sequence 读取 + +对应 MSH: + +```shell +adc probe adc1 +adc seq 1000 0 1 +``` + +代码: + +```c +#include +#include + +#define ADC_DEV_NAME "adc1" + +static int adc_v2_sequence_sample(void) +{ + rt_adc_device_t adc_dev; + struct rt_adc_sequence_cfg cfg; + rt_uint32_t channels; + rt_uint32_t values[2]; + rt_size_t read_count; + rt_err_t result; + + adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME); + if (adc_dev == RT_NULL) + { + rt_kprintf("can't find %s\n", ADC_DEV_NAME); + return -RT_ERROR; + } + + if (rt_device_open((rt_device_t)adc_dev, RT_DEVICE_FLAG_RDWR) != RT_EOK) + { + rt_kprintf("open %s failed\n", ADC_DEV_NAME); + return -RT_ERROR; + } + + channels = RT_ADC_CHANNEL_MASK(0) | RT_ADC_CHANNEL_MASK(1); + rt_memset(&cfg, 0, sizeof(cfg)); + cfg.buffer = values; + cfg.buffer_length = RT_ARRAY_SIZE(values); + cfg.timeout_ms = 1000; + + result = rt_adc_read_sequence(adc_dev, channels, &cfg, &read_count); + if ((result != RT_EOK) || (read_count != RT_ARRAY_SIZE(values))) + { + rt_kprintf("sequence read failed: %d, count=%u\n", result, (unsigned int)read_count); + rt_device_close((rt_device_t)adc_dev); + return -RT_ERROR; + } + + rt_kprintf("ch0=%lu ch1=%lu\n", values[0], values[1]); + rt_device_close((rt_device_t)adc_dev); + return RT_EOK; +} +MSH_CMD_EXPORT(adc_v2_sequence_sample, read adc v2 sequence sample); +``` + +### 示例 3:读取电压值 + +对应 MSH: + +```shell +adc probe adc1 +adc config 0 +adc voltage 1000 +``` + +代码: + +```c +#include +#include + +#define ADC_DEV_NAME "adc1" + +static int adc_v2_voltage_sample(void) +{ + rt_adc_device_t adc_dev; + rt_uint32_t channels; + rt_uint32_t vref_mv; + rt_uint32_t voltage_mv; + rt_err_t result; + + adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME); + if (adc_dev == RT_NULL) + { + rt_kprintf("can't find %s\n", ADC_DEV_NAME); + return -RT_ERROR; + } + + if (rt_device_open((rt_device_t)adc_dev, RT_DEVICE_FLAG_RDWR) != RT_EOK) + { + rt_kprintf("open %s failed\n", ADC_DEV_NAME); + return -RT_ERROR; + } + + vref_mv = 3300; + rt_device_control((rt_device_t)adc_dev, RT_ADC_CMD_SET_VREF, &vref_mv); + + channels = RT_ADC_CHANNEL_MASK(0); + result = rt_device_control((rt_device_t)adc_dev, RT_ADC_CMD_SET_SESSION, &channels); + if (result != RT_EOK) + { + rt_kprintf("config adc session failed: %d\n", result); + rt_device_close((rt_device_t)adc_dev); + return -RT_ERROR; + } + + result = rt_adc_voltage(adc_dev, &voltage_mv, 1, 1000); + if (result != RT_EOK) + { + rt_kprintf("read voltage failed: %d\n", result); + rt_device_close((rt_device_t)adc_dev); + return -RT_ERROR; + } + + rt_kprintf("ch0=%lu mV\n", voltage_mv); + rt_device_close((rt_device_t)adc_dev); + return RT_EOK; +} +MSH_CMD_EXPORT(adc_v2_voltage_sample, read adc v2 voltage sample); +``` + +### 示例 4:latest stream + +对应 MSH: + +```shell +adc probe adc1 +adc stream start 0 1 +adc stream read 1000 +adc stream stop +``` + +代码: + +```c +#include +#include + +#define ADC_DEV_NAME "adc1" + +static int adc_v2_latest_stream_sample(void) +{ + rt_adc_device_t adc_dev; + struct rt_adc_stream_cfg cfg; + rt_uint32_t channels; + rt_uint32_t dma_buffer[2]; + rt_uint32_t values[2]; + rt_ssize_t read_count; + + adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME); + if (adc_dev == RT_NULL) + { + rt_kprintf("can't find %s\n", ADC_DEV_NAME); + return -RT_ERROR; + } + + if (rt_device_open((rt_device_t)adc_dev, RT_DEVICE_FLAG_RDWR) != RT_EOK) + { + rt_kprintf("open %s failed\n", ADC_DEV_NAME); + return -RT_ERROR; + } + + channels = RT_ADC_CHANNEL_MASK(0) | RT_ADC_CHANNEL_MASK(1); + rt_memset(&cfg, 0, sizeof(cfg)); + cfg.policy = RT_ADC_STREAM_POLICY_LATEST; + cfg.dma_event_mode = RT_ADC_STREAM_DMA_EVENT_AUTO; + cfg.dma_buffer = dma_buffer; + cfg.dma_buffer_length = RT_ARRAY_SIZE(dma_buffer); + + if (rt_adc_stream_start(adc_dev, channels, &cfg) != RT_EOK) + { + rt_kprintf("start latest stream failed\n"); + rt_device_close((rt_device_t)adc_dev); + return -RT_ERROR; + } + + read_count = rt_adc_stream_read(adc_dev, values, RT_ARRAY_SIZE(values), 1000); + if (read_count > 0) + { + rt_kprintf("stream: ch0=%lu ch1=%lu\n", values[0], values[1]); + } + + rt_adc_stream_stop(adc_dev); + rt_device_close((rt_device_t)adc_dev); + return RT_EOK; +} +MSH_CMD_EXPORT(adc_v2_latest_stream_sample, read adc v2 latest stream sample); +``` + +### 示例 5:timer trigger + stream + +对应 MSH: + +```shell +adc probe adc1 +adc trigger set timer_update timer2 1000 +adc stream start 0 +adc stream read 1000 +adc stream stop +adc trigger clear +``` + +代码骨架: + +```c +#include +#include + +static rt_err_t adc_v2_set_timer_trigger(rt_adc_device_t adc_dev, const char *timer_name, rt_uint32_t freq_hz) +{ + struct rt_adc_trigger_cfg trigger_cfg; + rt_device_t timer_dev; + + timer_dev = rt_device_find(timer_name); + if (timer_dev == RT_NULL) + { + return -RT_ERROR; + } + + rt_memset(&trigger_cfg, 0, sizeof(trigger_cfg)); + trigger_cfg.type = RT_ADC_TRIGGER_TIMER_UPDATE; + trigger_cfg.event.timer.timer = timer_dev; + trigger_cfg.event.timer.freq_hz = freq_hz; + trigger_cfg.event.timer.channel = 0; + + return rt_adc_trigger_set(adc_dev, &trigger_cfg); +} +``` + +## 适配新的芯片与 BSP + +ADC V2 framework 位于通用驱动层,芯片厂商或 BSP 需要提供 backend driver。backend driver 负责将 ADC V2 的 session、sequence、stream、trigger 请求转换为具体芯片的 ADC 外设配置。 + +![新增芯片/BSP 的 ADC V2 适配层次](figures/adc_v2_backend_layers.png) + +### 适配文件建议 + +```text +bsp//libraries//drivers/drv_adc_v2.c +bsp//libraries//drivers/drv_adc_v2.h +bsp//libraries//drivers/config//adc_config_v2.h +``` + +### BSP/backend 需要关注的 API + +| 函数 / 结构 | 描述 | +| --- | --- | +| `rt_hw_adc_register()` | 注册 ADC V2 设备。 | +| `struct rt_adc_ops` | ADC backend 操作表集合。 | +| `struct rt_adc_core_ops` | open、close、session_config、control、trigger_prepare。 | +| `struct rt_adc_sequence_ops` | finite sequence 的 start、read、stop。 | +| `struct rt_adc_stream_ops` | stream 的 start、sync、stop。 | +| `rt_hw_adc_stream_isr()` | DMA/ADC ISR 向 framework 上报 stream 事件。 | + +### 注册 ADC 设备 + +```c +rt_err_t rt_hw_adc_register(rt_adc_device_t device, const char *name, const struct rt_adc_ops *ops, const void *user_data); +``` + +| 参数 | 描述 | +| --- | --- | +| `device` | ADC 设备对象。 | +| `name` | 注册到 RT-Thread 设备系统中的名称,例如 `"adc1"`。 | +| `ops` | ADC V2 后端操作表。 | +| `user_data` | 后端私有数据。 | + +| 返回值 | 描述 | +| --- | --- | +| `RT_EOK` | 注册成功。 | +| 其他错误码 | 注册失败。 | + +### core ops + +```c +struct rt_adc_core_ops +{ + rt_err_t (*open)(struct rt_adc_device *device); + rt_err_t (*close)(struct rt_adc_device *device); + rt_err_t (*session_config)(struct rt_adc_device *device, rt_uint32_t channels); + rt_err_t (*trigger_prepare)(struct rt_adc_device *device, const struct rt_adc_trigger_cfg *cfg); + rt_err_t (*control)(struct rt_adc_device *device, int cmd, void *args); +}; +``` + +| 回调 | 描述 | +| --- | --- | +| `open` | 初始化 ADC 硬件资源。 | +| `close` | 关闭或释放 ADC 硬件资源。 | +| `session_config` | 根据 channel mask 配置本次 session 的通道和 rank。 | +| `trigger_prepare` | 根据已缓存的 trigger 请求预配置后端触发状态。启用 `RT_ADC_USING_TRIGGER` 时需要。 | +| `control` | 处理 ADC V2 control 命令。 | + +### sequence ops + +```c +struct rt_adc_sequence_ops +{ + rt_err_t (*start)(struct rt_adc_device *device, rt_uint32_t channels, const struct rt_adc_sequence_cfg *cfg); + rt_err_t (*read)(struct rt_adc_device *device, rt_uint32_t *value, rt_int32_t timeout_ms); + rt_err_t (*stop)(struct rt_adc_device *device); +}; +``` + +| 回调 | 描述 | +| --- | --- | +| `start` | 启动一次有限 sequence 采样。 | +| `read` | 从 active sequence 中读取一个 sample。 | +| `stop` | 停止或清理本次 sequence。 | + +### stream ops + +```c +struct rt_adc_stream_ops +{ + rt_err_t (*start)(struct rt_adc_device *device, rt_uint32_t channels, const struct rt_adc_stream_cfg *cfg); + rt_err_t (*sync)(struct rt_adc_device *device, const rt_uint32_t *sample_buffer, rt_size_t sample_count); + rt_err_t (*stop)(struct rt_adc_device *device, rt_bool_t *hardware_stopped); +}; +``` + +| 回调 | 描述 | +| --- | --- | +| `start` | 启动 stream 采样,通常需要配置 ADC scan、DMA、触发源。 | +| `sync` | 在 CPU 访问 DMA sample buffer 前执行缓存同步或平台相关同步。 | +| `stop` | 停止 stream 硬件路径,并通过 `hardware_stopped` 返回硬件是否已经停止。 | + +### 适配步骤 + +| 步骤 | 说明 | +| --- | --- | +| 1. 新增后端文件 | 新增 `drv_adc_v2.c/.h`,定义设备对象、私有数据、配置表。 | +| 2. 接入构建系统 | 在 BSP 的 Kconfig、SConscript 中增加 ADC V2 后端配置和源文件。 | +| 3. 注册设备 | 调用 `rt_hw_adc_register()` 注册 `adc1`、`adc2` 等设备。 | +| 4. 实现 core ops | 完成 open、close、session_config、control。 | +| 5. 实现 sequence ops | 支持有限 sequence 采样。 | +| 6. 实现 stream ops | 如需连续采样,接入 DMA,并在 ISR 中调用 `rt_hw_adc_stream_isr()`。 | +| 7. 实现 trigger_prepare | 如需硬件触发,将 framework trigger cfg 映射到芯片 ADC 触发选择。 | +| 8. 增加 special 命令 | 如芯片存在内部通道,可提供 backend-specific MSH 命令。 | +| 9. 使用 MSH 验证 | 通过 `adc probe/config/read/voltage/seq/stream/trigger/special` 验证接口路径。 | + +### STM32 后端适配要点 + +新增芯片或 BSP 可对照 STM32 HAL 后端的组织方式: + +| 适配点 | STM32 后端对应思路 | +| --- | --- | +| ADC instance | 将 `adc1`、`adc2` 等实例注册为 RT-Thread ADC 设备。 | +| logical channel | 将 ADC V2 channel mask 映射为 STM32 ADC channel 和 rank。 | +| sequence | 使用 HAL regular conversion 路径完成有限采样。 | +| stream | 使用 ADC + DMA 完成 latest 或 FIFO stream。 | +| trigger | 将 timer update、timer compare、comparator 等 trigger cfg 映射为 ADC external trigger。 | +| internal channel | 将 VREFINT、内部温度、VBAT 暴露为 backend-specific special item。 | + +## 常见问题 + +### 为什么 `adc voltage` 失败? + +常见原因: + +| 原因 | 处理方式 | +| --- | --- | +| 没有先执行 `adc config` | 先配置当前 session。 | +| 输出 buffer 数量与 session 通道数不一致 | 确保读取数量与 channel mask 中的通道数量一致。 | +| 没有有效 VREF | 设置 `default_vref_mv`,或将后端 VREF logical channel 加入 session。 | +| 后端不支持电压换算 | 检查 BSP/backend 是否实现相关 control 命令。 | + +### 为什么 `adc special read temp` 失败? + +常见原因: + +| 原因 | 处理方式 | +| --- | --- | +| 当前设备不支持 `temp` item | 先执行 `adc special list` 查看支持项。 | +| session 未包含温度 logical channel | 按 `adc special list` 提示执行 `adc config`。 | +| 没有 VDDA 来源 | session 加入 VREFINT,或配置 `default_vref_mv`。 | +| 当前芯片系列缺少所需校准/换算能力 | 由 STM32 后端按不支持处理。 | + +### latest stream 和 FIFO stream 如何选择? + +| 场景 | 建议策略 | +| --- | --- | +| 电池电压、温度、电位器、状态监测 | latest-frame | +| 波形、电流、振动、类音频、控制环 | FIFO | +| 只关心最新值,允许旧值被覆盖 | latest-frame | +| 关心采样顺序和丢样统计 | FIFO | diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/adc_v2_api_map.png b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/adc_v2_api_map.png new file mode 100644 index 0000000000000000000000000000000000000000..c2e618ff01eeef98c0b2458895ef57dae3e0b2f4 Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/adc_v2_api_map.png differ diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/adc_v2_backend_layers.png b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/adc_v2_backend_layers.png new file mode 100644 index 0000000000000000000000000000000000000000..3606943efb1a5339b7b88ecda776c223ec33fc42 Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/adc_v2_backend_layers.png differ diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/adc_v2_msh_api_flow.png b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/adc_v2_msh_api_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..78753d3e5e0353e61bb99f412d22e065fc8dfc09 Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/adc_v2_msh_api_flow.png differ diff --git a/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/stm32_internal_channels.png b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/stm32_internal_channels.png new file mode 100644 index 0000000000000000000000000000000000000000..54e6704984f63b052fa637d4257c136086c5082c Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v2/figures/stm32_internal_channels.png differ diff --git "a/rt-thread-version/rt-thread-standard/tutorial/make-bsp/renesas-ra/RA\347\263\273\345\210\227\351\251\261\345\212\250\344\273\213\347\273\215.md" "b/rt-thread-version/rt-thread-standard/tutorial/make-bsp/renesas-ra/RA\347\263\273\345\210\227\351\251\261\345\212\250\344\273\213\347\273\215.md" index 5c1b7c4d3b4e9dc892ffbc57663768a5b394146e..fcba76b203f5c695fe336ff27eb0dcd74715b773 100644 --- "a/rt-thread-version/rt-thread-standard/tutorial/make-bsp/renesas-ra/RA\347\263\273\345\210\227\351\251\261\345\212\250\344\273\213\347\273\215.md" +++ "b/rt-thread-version/rt-thread-standard/tutorial/make-bsp/renesas-ra/RA\347\263\273\345\210\227\351\251\261\345\212\250\344\273\213\347\273\215.md" @@ -34,7 +34,7 @@ | [UART](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart_v1/uart) | 通过串口收发数据 | | [I2C](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/i2c) | 通过软件 I2C 收发数据 | | [SPI](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/spi/spi) | 通过 SPI 收发数据 | -| [ADC](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc) | 测量管脚上的模拟量 | +| [ADC](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1) | 测量管脚上的模拟量 | | SDIO | 通过 SDIO 读写数据 | | [TIMER](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer) | 使用硬件定时器实现测量时间和定时执行回调函数功能 | | [PWM](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/pwm) | 在特定的管脚输出 PWM 波形 | diff --git "a/rt-thread-version/rt-thread-standard/tutorial/make-bsp/stm32-bsp/STM32\347\263\273\345\210\227\351\251\261\345\212\250\344\273\213\347\273\215.md" "b/rt-thread-version/rt-thread-standard/tutorial/make-bsp/stm32-bsp/STM32\347\263\273\345\210\227\351\251\261\345\212\250\344\273\213\347\273\215.md" index cf1e073cce8d68cf7534ac4eeff5e2334425adbd..bd485dd7c6f6b2db9716aaa7ef549be0389c1e6d 100644 --- "a/rt-thread-version/rt-thread-standard/tutorial/make-bsp/stm32-bsp/STM32\347\263\273\345\210\227\351\251\261\345\212\250\344\273\213\347\273\215.md" +++ "b/rt-thread-version/rt-thread-standard/tutorial/make-bsp/stm32-bsp/STM32\347\263\273\345\210\227\351\251\261\345\212\250\344\273\213\347\273\215.md" @@ -34,7 +34,7 @@ | 2 | [UART](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart_v1/uart) | 通过串口收发数据 | | 3 | [soft I2C](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/i2c) | 通过软件 I2C 收发数据 | | 4 | [SPI](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/spi/spi) | 通过 SPI 收发数据 | -| 5 | [ADC](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc) | 测量管脚上的模拟量 | +| 5 | [ADC](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc_v1) | 测量管脚上的模拟量 | | 6 | SDIO | 通过 SDIO 读写数据 | | 7 | [TIMER](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/hwtimer/hwtimer) | 使用硬件定时器实现测量时间和定时执行回调函数功能 | | 8 | [PWM](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/pwm/pwm) | 在特定的管脚输出 PWM 波形 |