API GUIDE / ADC / Middleware

G32S10 ADC Middleware 接口

Middleware 层接口:code/middleware/adc/CMakeLists.txt 在 CONFIG_MIDDLEWARE_ADC 生效时编译 src/im_adc.c,并把 include/ 加入包含路径。

1. 构建配置与文件组织

1.1 文件职责

文件说明
code/middleware/adc/include/im_adc.hMID 公开宏、类型、设备对象定义与接口声明
code/middleware/adc/src/im_adc.cMID 初始化、序列/DMA 编排、轮询/中断/连续 DMA 路径、状态与诊断实现
code/middleware/adc/KconfigMIDDLEWARE_ADC 选项
code/middleware/adc/example/main/main.c示例入口:选择 ADC 实例和一个示例
code/middleware/adc/example/main/im_adc_example_*.cPolling/IRQ/DMA 的 SINGLE、FINITE、CONTINUOUS 独立示例
code/middleware/adc/example/main/im_adc_example_functional_test.cMID API 与硬件路径回归测试

code/middleware/adc/CMakeLists.txt 在 CONFIG_MIDDLEWARE_ADC 生效时编译 src/im_adc.c,并把 include/ 加入包含路径。

2. 资源模型与公开类型

2.1 实例与基地址

MID 以逻辑实例号选择 ADC 硬件:

逻辑实例硬件实例基地址宏基地址
IM_ADC_INSTANCE_0ADC0SADC0_IOBASE0x13720000UL
IM_ADC_INSTANCE_1ADC1SADC1_IOBASE0x13730000UL

说明:

  • 实例基地址和寄存器布局由 base.h / adc_regs.h 定义,MID 只做逻辑实例到 ADC0/ADC1 的映射。

  • 一个逻辑实例在同一时刻只能被一个已初始化的 MID 对象绑定,重复绑定返回 IM_ERR_ALREADY_EXISTS。

2.2 MID 公开类型

本节按照 HAL/LL 文档“每个公开类型独立小节、类型定义与约束相邻”的组织方式说明 MID 类型。完整定义以 code/middleware/adc/include/im_adc.h 为准。

2.2.1 im_adc_t

typedef struct im_adc im_adc_t;

im_adc_t 是调用方持有的 MID 设备对象。对象必须零初始化,并在 im_adc_init() 到 im_adc_deinit() 期间保持地址不变;其内部字段由 MID 管理,应用不得读取、写入或复制活动对象。同一 ADC 实例同一时刻只能绑定一个已初始化的 MID 对象。

2.2.2 im_adc_instance_t

typedef enum {
    IM_ADC_INSTANCE_0 = 0,
    IM_ADC_INSTANCE_1,
} im_adc_instance_t;
值说明
IM_ADC_INSTANCE_0逻辑 ADC0。
IM_ADC_INSTANCE_1逻辑 ADC1。

2.2.3 im_adc_run_mode_t

typedef enum {
    IM_ADC_RUN_MODE_POLLING = 0,
    IM_ADC_RUN_MODE_IRQ,
    IM_ADC_RUN_MODE_DMA,
} im_adc_run_mode_t;
值说明
IM_ADC_RUN_MODE_POLLING在调用线程内完成采样。
IM_ADC_RUN_MODE_IRQ通过 ADC 中断推进采样。
IM_ADC_RUN_MODE_DMA通过 DMA 把样本写入调用方缓冲区。

2.2.4 im_adc_acquisition_mode_t

typedef enum {
    IM_ADC_ACQUISITION_SINGLE = 0,
    IM_ADC_ACQUISITION_FINITE,
    IM_ADC_ACQUISITION_CONTINUOUS,
} im_adc_acquisition_mode_t;
值说明
IM_ADC_ACQUISITION_SINGLE单帧;Polling/IRQ 在 channel_count <= 4 时使用 SEQ0,否则使用 SEQ1;DMA 使用 SEQ2。
IM_ADC_ACQUISITION_FINITE采满 sample_capacity 后结束;各运行模式均使用 SEQ2。
IM_ADC_ACQUISITION_CONTINUOUS循环覆盖调用方缓冲区;仅支持 DMA + SEQ1。

具体运行模式组合见 2.3 运行模式与采集模式组合。

2.2.5 im_adc_trigger_t

typedef enum {
    IM_ADC_TRIGGER_SOFTWARE = 0,
    IM_ADC_TRIGGER_TCU,
    IM_ADC_TRIGGER_PWM0,
    IM_ADC_TRIGGER_PWM1,
} im_adc_trigger_t;
值说明
IM_ADC_TRIGGER_SOFTWARE软件/寄存器触发,当前 MID 唯一支持的触发源。
IM_ADC_TRIGGER_TCUTCU 外部触发;保留编码,当前 im_adc_start() 返回 IM_ERR_UNSUPPORTED。
IM_ADC_TRIGGER_PWM0PWM0 外部触发;保留编码,当前 im_adc_start() 返回 IM_ERR_UNSUPPORTED。
IM_ADC_TRIGGER_PWM1PWM1 外部触发;保留编码,当前 im_adc_start() 返回 IM_ERR_UNSUPPORTED。

2.2.6 im_adc_sample_time_t

typedef enum {
    IM_ADC_SAMPLE_TIME_EXTRA_0 = 0,
    IM_ADC_SAMPLE_TIME_EXTRA_5,
    IM_ADC_SAMPLE_TIME_EXTRA_10,
    IM_ADC_SAMPLE_TIME_EXTRA_15,
    IM_ADC_SAMPLE_TIME_EXTRA_30,
    IM_ADC_SAMPLE_TIME_EXTRA_45,
    IM_ADC_SAMPLE_TIME_EXTRA_60,
    IM_ADC_SAMPLE_TIME_EXTRA_210,
} im_adc_sample_time_t;
值增加的采样窗口周期
IM_ADC_SAMPLE_TIME_EXTRA_00
IM_ADC_SAMPLE_TIME_EXTRA_55
IM_ADC_SAMPLE_TIME_EXTRA_1010
IM_ADC_SAMPLE_TIME_EXTRA_1515
IM_ADC_SAMPLE_TIME_EXTRA_3030
IM_ADC_SAMPLE_TIME_EXTRA_4545
IM_ADC_SAMPLE_TIME_EXTRA_6060
IM_ADC_SAMPLE_TIME_EXTRA_210210

2.2.7 im_adc_state_t

typedef enum {
    IM_ADC_STATE_RESET = 0,
    IM_ADC_STATE_READY,
    IM_ADC_STATE_BUSY,
    IM_ADC_STATE_ERROR,
} im_adc_state_t;
值说明
IM_ADC_STATE_RESET未初始化、已 deinit 或对象无效。
IM_ADC_STATE_READY已初始化且空闲,可以接受请求。
IM_ADC_STATE_BUSY有采样请求正在进行。
IM_ADC_STATE_ERROR运行期失败,需要通过 stop 或 deinit 恢复。

状态迁移和恢复流程见 6.6 超时、错误与恢复。

2.2.8 im_adc_event_t

typedef enum {
    IM_ADC_EVENT_COMPLETE = 0,
    IM_ADC_EVENT_FRAME_READY,
    IM_ADC_EVENT_STOPPED,
    IM_ADC_EVENT_MONITOR_LOW,
    IM_ADC_EVENT_MONITOR_HIGH,
    IM_ADC_EVENT_ERROR,
} im_adc_event_t;
值说明
IM_ADC_EVENT_COMPLETESINGLE 或 FINITE 请求成功完成。
IM_ADC_EVENT_FRAME_READYCONTINUOUS DMA 已写满一个完整缓冲区;缓冲区仍可能被循环 DMA 覆盖。仅 DMA 进度通知不会单独产生该事件。
IM_ADC_EVENT_STOPPED活动请求被应用提前停止。
IM_ADC_EVENT_MONITOR_LOW模拟看门狗检测到一个或多个低阈值越限。
IM_ADC_EVENT_MONITOR_HIGH模拟看门狗检测到一个或多个高阈值越限。
IM_ADC_EVENT_ERROR活动请求因运行期失败而终结。

2.2.9 im_adc_error_t

typedef enum {
    IM_ADC_ERROR_NONE = 0U,
    IM_ADC_ERROR_TIMEOUT = (1UL << 0U),
    IM_ADC_ERROR_DATA_OVERRUN = (1UL << 1U),
    IM_ADC_ERROR_EVENT_OVERRUN = (1UL << 2U),
    IM_ADC_ERROR_PHY = (1UL << 3U),
    IM_ADC_ERROR_DMA = (1UL << 4U),
    IM_ADC_ERROR_INTERNAL = (1UL << 5U),
} im_adc_error_t;
值说明
IM_ADC_ERROR_NONE无运行期错误。
IM_ADC_ERROR_TIMEOUT采样超时。
IM_ADC_ERROR_DATA_OVERRUNADC 数据存储或 FIFO 溢出。
IM_ADC_ERROR_EVENT_OVERRUN转换事件溢出。
IM_ADC_ERROR_PHYADC PHY 转换结束异常。
IM_ADC_ERROR_DMAADC DMA 传输或进度统计异常。
IM_ADC_ERROR_INTERNALMID 检测到活动请求执行失败。

IM_ADC_ERROR_* 为可按位或的运行期错误标志。IM_ERR_INVALID_PARAM、IM_ERR_NOT_READY、IM_ERR_BUSY、IM_ERR_UNSUPPORTED 等参数或状态错误只通过 error_code_t 返回,不写入 error_flags;只有请求已被 MID 接受后的运行期异常才锁存错误位。

2.2.10 im_adc_event_info_t

typedef struct {
    uint32_t sample_count;
    uint32_t error_flags;
    uint32_t monitor_low_flags;
    uint32_t monitor_high_flags;
} im_adc_event_info_t;
字段说明
sample_count本次请求已交付的有效样本数;对 COMPLETE、FRAME_READY 和 ERROR 事件有效。CONTINUOUS 按完整缓冲区累计,可超过单个缓冲区容量。
error_flagsIM_ADC_ERROR_* 标志快照,对 ERROR 事件有效。
monitor_low_flagsbit n 对应通道 n 低于低阈值,对 MONITOR_LOW 事件有效。
monitor_high_flagsbit n 对应通道 n 高于高阈值,对 MONITOR_HIGH 事件有效。

im_adc_event_info_t 只在当前回调调用期间有效。应用需要在其它上下文使用这些字段时必须先复制;MID 在每次回调中重新构造快照,不保存历史事件。

2.2.11 im_adc_event_callback_t

typedef void (*im_adc_event_callback_t)(im_adc_t *dev,
                                        im_adc_event_t event,
                                        const im_adc_event_info_t *info,
                                        void *user_ctx);

Polling 完成回调在调用 im_adc_start() 或 im_adc_read() 的线程上下文执行;IRQ、DMA 和 monitor 回调在 ADC 中断上下文执行。回调必须满足 ISR 安全约束,不得阻塞、睡眠、等待锁、执行大块解码或输出大量日志,也不得在回调内调用 im_adc_start()、im_adc_read()、im_adc_wait()、im_adc_stop() 或 im_adc_deinit()。详见 3.2 回调上下文约束。

2.2.12 im_adc_config_t

typedef struct {
    im_adc_instance_t instance;
    im_adc_run_mode_t run_mode;
    uint8_t adc_clock_div;
    im_adc_sample_time_t sample_time;
    im_adc_event_callback_t event_callback;
    void *user_ctx;
} im_adc_config_t;
字段说明
instance绑定的逻辑 ADC 实例;同一实例同时只能被一个已初始化对象绑定。
run_mode结果交付方式;CONTINUOUS 请求要求 IM_ADC_RUN_MODE_DMA。
adc_clock_div初始化时传给 HAL 的 ADC 时钟分频值。
sample_time应用于每次转换的额外采样窗口周期。
event_callback可选事件回调;启用阈值比较时必须有回调,除非只使用 fail 输出。
user_ctx原样传给 event_callback 的应用上下文。

2.2.13 im_adc_request_t

typedef struct {
    im_adc_acquisition_mode_t mode;
    im_adc_trigger_t trigger;
    uint8_t channel_count;
    uint8_t channels[IM_ADC_MAX_CHANNELS_PER_REQUEST];
    uint32_t *sample_buffer_out;
    uint32_t sample_capacity;
} im_adc_request_t;
字段说明
mode请求生命周期:SINGLE 写一帧,FINITE 填满容量,CONTINUOUS 循环覆盖缓冲区直到 stop。
trigger转换触发源;im_adc_start() 当前只接受 IM_ADC_TRIGGER_SOFTWARE。
channel_countchannels[] 中有效项数,取值范围 1~8。
channels[]通道序列,channels[0..channel_count-1] 每项取值 0~7;允许重复但建议避免。
sample_buffer_out调用方提供的输出数组,必须保持有效且不被改写直到采样结束或 im_adc_stop() 返回;DMA 模式要求 4 字节对齐,cache 维护由 HAL 完成。
sample_capacity输出容量,单位是 uint32_t 样本项而不是字节;至少为 channel_count。SINGLE 只写 channel_count 项;FINITE 与 CONTINUOUS 要求为 channel_count 的整数倍,且 CONTINUOUS 还要求 DMA 运行模式。

sample_buffer_out 中每项只包含 12-bit ADC 值,对应通道为 channels[index % channel_count]。

2.2.14 im_adc_monitor_config_t

typedef struct {
    uint8_t channel;
    uint16_t low_threshold;
    uint16_t high_threshold;
    bool low_threshold_enable;
    bool high_threshold_enable;
    bool fail_enable;
} im_adc_monitor_config_t;
字段说明
channel被监测的 ADC 输入通道,取值 0~7。
low_threshold包含在内的低阈值,取值 0~4095,且不大于 high_threshold。
high_threshold包含在内的高阈值,取值 0~4095,且不小于 low_threshold。
low_threshold_enable使能低阈值比较和 MONITOR_LOW 通知。
high_threshold_enable使能高阈值比较和 MONITOR_HIGH 通知。
fail_enable使能硬件模拟看门狗 fail 输出。

2.3 运行模式与采集模式组合

采集模式运行模式转换序列说明
SINGLEPolling / IRQchannel_count <= 4 时 SEQ0,否则 SEQ1在调用线程或中断中采一帧后完成
SINGLEDMASEQ2单 descriptor 循环 DMA 搬运一帧,DMA 完成后停止
FINITEPolling / IRQ / DMASEQ2采满 sample_capacity 个样本后完成;IRQ 逐帧重启,DMA 逐帧停止并按偏移重配
CONTINUOUSDMASEQ1(连续组,1 组)一个覆盖整缓冲区的循环 descriptor;保持 BUSY 直到 stop、deinit 或错误
CONTINUOUSPolling / IRQ—非法组合,im_adc_start() 返回 IM_ERR_INVALID_PARAM

上表中 SINGLE 之外的采集模式都要求 sample_capacity 是 channel_count 的整数倍。

2.4 全生命周期与资源流程

下图描述 im_adc.c / im_adc.h 的对象生命周期。左侧是应用需要准备或可选择的输入,中间是 MID 的状态与请求执行,右侧是 ADC/线程事件。图中的 im_adc_finish() 是 MID 内部 helper,不是应用可直接调用的 API。

应用输入与可选配置

NODE  im_adc_t: 零初始化  地址在 init 到 deinit 之间保持固定
NODE  im_adc_config_t: instance, run_mode  adc_clock_div, sample_time
可选 event_callback 与 user_ctx
NODE  im_adc_request_t: mode, 软件触发  通道列表与 channel_count
输出缓冲区与容量
NODE  DMA 请求: 输出缓冲区 4 字节对齐  FINITE/CONTINUOUS 容量为通道数整数倍
NODE  可选监测配置:  channel, 阈值, fail 输出
NODE  线程接口: start 或 read, wait,  stop, deinit, 只读查询

MID 对象生命周期与所有权

NODE  RESET  不占用 HAL 资源或 ADC 实例
NODE  im_adc_init
NODE  init 是否成功?
NODE  清理 HAL; 释放 ADC 占用;  清零对象; 返回错误
NODE  READY  HAL 已初始化; 回调已注册;
占用一个 ADC 实例
NODE  可选 im_adc_monitor_config  仅 READY
NODE  im_adc_deinit  BUSY/ERROR 先 stop; 关闭 IRQ; 唤醒 waiter;
HAL deinit; 释放占用; 清零对象

请求执行

NODE  im_adc_start
NODE  im_adc_read  启动请求; 仅 IRQ/DMA 才等待
CONTINUOUS 被拒绝
NODE  状态为 READY; 请求合法;  触发源受支持?
NODE  复制请求; 清零样本计数与错误;  选择序列; 进入 BUSY
NODE  运行模式与采集模式
NODE  Polling SINGLE/FINITE  逐帧 start, poll, 读取
NODE  IRQ 或 DMA 的 SINGLE/FINITE  申请 ADC IRQ; start_it
NODE  仅 DMA CONTINUOUS  SEQ1 连续组 + 循环 DMA;
ADC IRQ 承载 DMA_FIN 进度与错误
NODE  BUSY  采样请求活动
NODE  im_adc_wait  同时只允许一个等待者
NODE  返回参数、状态或能力错误  已接受的请求不受影响
NODE  启动或配置失败

完成、错误与终结路径

NODE  ADC DMA_FIN  FIFO/DMA 进度通知
NODE  用 DMA 字节计数累计进度;  满一个完整缓冲区才计入样本
NODE  发布 FRAME_READY;  保持 BUSY 并继续循环 DMA
NODE  ADC 序列完成或 DMA 完成  帧数据已交付
NODE  FINITE 仍有剩余容量
NODE  重配下一帧 DMA  或重启下一帧 IRQ
NODE  wait 超时
NODE  ADC PHY、data/event overrun、  DMA 或内部失败
NODE  im_adc_finish 成功  原子领取终结权; 停止序列;
发布 COMPLETE; 唤醒 waiter; 释放请求字段
NODE  im_adc_finish 错误或超时  原子领取终结权; 停止序列;
发布 ERROR; 唤醒 waiter; 释放请求字段
NODE  ERROR  保留已初始化对象以便恢复

应用侧的恢复与释放

NODE  应用调用 im_adc_stop
NODE  停止序列; 关闭 ADC IRQ; 释放请求字段  BUSY: 发布 STOPPED 并回到 READY
ERROR: stop 成功时恢复到 READY
NODE  stop 是否成功?
NODE  HAL deinit 是否成功?
NODE  get_state, get_last_status,  get_error, get_sample_count,
get_dropped_frame_count
只读, 不改变状态

im_adc_t: 零初始化 → im_adc_init

im_adc_config_t: instance, run_mode → im_adc_init

im_adc_init → init 是否成功?

init 是否成功? → 清理 HAL; 释放 ADC 占用; · 否

init 是否成功? → READY · 是

可选监测配置: → 可选 im_adc_monitor_config

READY → 可选 im_adc_monitor_config

im_adc_request_t: mode, 软件触发 → im_adc_start

DMA 请求: 输出缓冲区 4 字节对齐 → im_adc_start

线程接口: start 或 read, wait, → im_adc_read

READY → im_adc_start

im_adc_read → im_adc_start

im_adc_start → 状态为 READY; 请求合法;

状态为 READY; 请求合法; → 返回参数、状态或能力错误 · 否

状态为 READY; 请求合法; → 复制请求; 清零样本计数与错误; · 是

启动或配置失败 → im_adc_finish 错误或超时

运行模式与采集模式 → Polling SINGLE/FINITE · Polling SINGLE/FINITE

Polling SINGLE/FINITE → ADC 序列完成或 DMA 完成

Polling SINGLE/FINITE → 启动或配置失败 · 轮询或读取失败

运行模式与采集模式 → IRQ 或 DMA 的 SINGLE/FINITE · IRQ 或 DMA SINGLE/FINITE

IRQ 或 DMA 的 SINGLE/FINITE → BUSY

IRQ 或 DMA 的 SINGLE/FINITE → 启动或配置失败 · 序列、IRQ、DMA 或启动失败

运行模式与采集模式 → 仅 DMA CONTINUOUS · DMA CONTINUOUS

仅 DMA CONTINUOUS → BUSY

仅 DMA CONTINUOUS → 启动或配置失败 · 配置或启动失败

BUSY → im_adc_wait

im_adc_wait → wait 超时 · 超时

IRQ 或 DMA 的 SINGLE/FINITE → ADC 序列完成或 DMA 完成

ADC 序列完成或 DMA 完成 → im_adc_finish 成功 · SINGLE 完成或 FINITE 已满

ADC 序列完成或 DMA 完成 → FINITE 仍有剩余容量 · FINITE 仍有剩余

重配下一帧 DMA → IRQ 或 DMA 的 SINGLE/FINITE

仅 DMA CONTINUOUS → ADC DMA_FIN

ADC DMA_FIN → 用 DMA 字节计数累计进度;

发布 FRAME_READY; → BUSY

BUSY → ADC PHY、data/event overrun、

ADC PHY、data/event overrun、 → im_adc_finish 错误或超时

im_adc_finish 成功 → READY

im_adc_finish 错误或超时 → ERROR

BUSY → 应用调用 im_adc_stop

ERROR → 应用调用 im_adc_stop

READY → 应用调用 im_adc_stop

应用调用 im_adc_stop → 停止序列; 关闭 ADC IRQ; 释放请求字段

stop 是否成功? → READY · 是

stop 是否成功? → ERROR · 否

READY → im_adc_deinit

BUSY → im_adc_deinit

ERROR → im_adc_deinit

im_adc_deinit → HAL deinit 是否成功?

HAL deinit 是否成功? → RESET · 是

HAL deinit 是否成功? → ERROR · 否

READY → get_state, get_last_status,

BUSY → get_state, get_last_status,

ERROR → get_state, get_last_status,

阅读约定:

  • READY 表示对象已初始化并仍占有对应 ADC 实例;只有 im_adc_deinit() 成功才会解除占用并回到 RESET。

  • FRAME_READY 不是终结事件。CONTINUOUS 每发布一帧仍保持 BUSY,并且不会唤醒 im_adc_wait() 的等待者;应用必须在线程上下文调用 im_adc_stop() 或 im_adc_deinit()。

  • im_adc_finish() 使用原子终结权解决 ISR 完成/错误回调与线程 im_adc_wait() 超时之间的竞争:只有领取成功的一方会停止硬件、写入最终状态并发终结事件。

  • im_adc_stop() 用于应用主动取消或从 ERROR 恢复;它不释放 HAL handle,也不解除实例占用。

  • get_state、get_last_status、get_error、get_sample_count、get_dropped_frame_count 都是只读查询,不改变状态,也不构成跨线程同步屏障。

3. 行为约束与实现说明

3.1 输出缓冲区格式与对齐

  • sample_buffer_out 的每个 uint32_t 项只包含一个 12-bit 采样值(有效位 [11:0],取值 0~4095);第 index 项对应的通道是 channels[index % channel_count]。应用读取时应按 12-bit 有效位解释(例如 value & 0x0FFF),不要依赖高位内容。

  • MID 不向应用暴露 HAL 的 packed word:Polling/IRQ 路径直接取解包后的 value;DMA 的 SINGLE/FINITE 路径由 HAL 写入带通道号的 packed 数据,MID 在完成回调中剥离通道号并只保留 12-bit 值;CONTINUOUS 路径在 HAL 配置中关闭通道号嵌入,DMA 直接把值写入调用方缓冲区,MID 不做二次处理。

  • 样本顺序按帧重复:每帧依次写入 channels[0..channel_count-1](允许重复通道,重复时对应多项采样值)。

  • DMA 模式要求缓冲区地址 4 字节对齐,并要求 sample_capacity * 4 不超过 UINT32_MAX;其它模式没有额外的对齐要求。

  • 缓冲区必须保持有效,直到收到 COMPLETE/ERROR/STOPPED 事件或 im_adc_wait() 返回;CONTINUOUS 模式下必须保持到 im_adc_stop() 返回。

  • cache 维护由 HAL 负责:启动前清理,DMA 完成时失效,停止/重配时再维护一次,因此 stop 返回后调用方可以直接读取缓冲区。

3.2 回调上下文约束

  • Polling 的完成回调在调用 im_adc_start() / im_adc_read() 的线程上下文执行;IRQ、DMA 和 monitor 回调在 ADC 中断上下文执行。

  • 所有回调都必须满足 ISR 约束:不得阻塞、sleep、等待锁、执行大块解码或输出大量日志。

  • 不得从回调中调用 im_adc_start()、im_adc_read()、im_adc_wait()、im_adc_stop() 或 im_adc_deinit();应向线程投递消息后再操作。

  • info 只在本次回调期间有效;需要跨上下文使用时必须复制字段。

  • event_callback 为空时 MID 不发布任何事件,但状态、last_status、error_flags 和 sample_count 仍会正常更新。

4. 典型用法

4.1 轮询单帧同步采样

error_code_t ret;
im_adc_t adc = {0};
im_adc_config_t config;
im_adc_request_t request;
uint32_t samples[3];

im_adc_config_struct_init(&config);
config.instance = IM_ADC_INSTANCE_0;
config.run_mode = IM_ADC_RUN_MODE_POLLING;

im_adc_request_struct_init(&request);
request.mode = IM_ADC_ACQUISITION_SINGLE;
request.trigger = IM_ADC_TRIGGER_SOFTWARE;
request.channel_count = 3U;
request.channels[0] = 0U;
request.channels[1] = 3U;
request.channels[2] = 7U;
request.sample_buffer_out = samples;
request.sample_capacity = 3U;

ret = im_adc_init(&adc, &config);
if (ret == IM_OK) {
    ret = im_adc_read(&adc, &request, 10U);
}
(void)im_adc_deinit(&adc);

samples[0]、samples[1]、samples[2] 分别对应通道 0、3、7,每项只有 12-bit 采样值。

4.2 中断有限采样

static im_adc_t adc = {0};
static uint32_t samples[8] __attribute__((aligned(4)));

static void adc_event_callback(im_adc_t *dev,
                               im_adc_event_t event,
                               const im_adc_event_info_t *info,
                               void *user_ctx)
{
    if (event == IM_ADC_EVENT_ERROR) {
        /* 只记录快照,不在 ISR 中阻塞或调用 MID 控制接口。 */
        volatile uint32_t flags = info->error_flags;
        (void)flags;
    }
}

error_code_t adc_irq_finite_sample(void)
{
    error_code_t ret;
    im_adc_config_t config;
    im_adc_request_t request;

    im_adc_config_struct_init(&config);
    config.instance = IM_ADC_INSTANCE_0;
    config.run_mode = IM_ADC_RUN_MODE_IRQ;
    config.event_callback = adc_event_callback;

    ret = im_adc_init(&adc, &config);
    if (ret != IM_OK) {
        return ret;
    }

    im_adc_request_struct_init(&request);
    request.mode = IM_ADC_ACQUISITION_FINITE;
    request.channel_count = 2U;
    request.channels[0] = 0U;
    request.channels[1] = 1U;
    request.sample_buffer_out = samples;
    request.sample_capacity = 8U;   /* 4 帧 × 2 通道 */

    ret = im_adc_start(&adc, &request);
    if (ret == IM_OK) {
        ret = im_adc_wait(&adc, 1000U);
    }
    return ret;   /* 成功时 samples[] 已按 [CH0, CH1] 交错填充 8 项 */
}

im_adc_wait() 返回 IM_OK 时输出缓冲区已填满;返回 IM_ERR_TIMEOUT、IM_ERR_HARDWARE 或其它错误时,应根据 im_adc_get_error() 与 im_adc_get_state() 判断是否需要 stop 或 deinit 恢复。

4.3 DMA 连续采样

static im_adc_t adc = {0};
static uint32_t sample_buffer[256] __attribute__((aligned(4)));

error_code_t ret;
im_adc_config_t config;
im_adc_request_t request;

im_adc_config_struct_init(&config);
config.instance = IM_ADC_INSTANCE_0;
config.run_mode = IM_ADC_RUN_MODE_DMA;
config.event_callback = adc_event_callback;   /* 只记录 FRAME_READY/ERROR 快照 */
config.user_ctx = &application_context;

im_adc_request_struct_init(&request);
request.mode = IM_ADC_ACQUISITION_CONTINUOUS;
request.channel_count = 2U;
request.channels[0] = 1U;
request.channels[1] = 2U;
request.sample_buffer_out = sample_buffer;
request.sample_capacity = 256U;

ret = im_adc_init(&adc, &config);
if (ret == IM_OK) {
    ret = im_adc_start(&adc, &request);
}
/* 线程等待 FRAME_READY 通知后再复制或处理完整缓冲区;
 * 结束时在同一个线程中调用 im_adc_stop() 和 im_adc_deinit()。 */

sample_buffer 必须 4 字节对齐,并保持有效直到 im_adc_stop() 返回。每个完整缓冲区对应 128 帧 [通道 1, 通道 2] 样本;IM_ADC_EVENT_FRAME_READY 只表示一整轮缓冲区已经写入,DMA 会继续循环覆盖该缓冲区。

4.4 模拟看门狗监测

im_adc_monitor_config_t monitor;

im_adc_monitor_config_struct_init(&monitor);
monitor.channel = 0U;
monitor.low_threshold = 0x100U;
monitor.high_threshold = 0xE00U;
monitor.low_threshold_enable = true;
monitor.high_threshold_enable = true;
monitor.fail_enable = false;

(void)im_adc_monitor_config(&adc, &monitor);

该配置要求设备已初始化且处于 READY,且 im_adc_config_t.event_callback 非空;越限时应用会收到 IM_ADC_EVENT_MONITOR_LOW / IM_ADC_EVENT_MONITOR_HIGH,事件快照的 monitor_low_flags / monitor_high_flags 给出对应的通道位。

5. 接口规范

5.1 初始化与默认值

5.1.1 im_adc_config_struct_init

void im_adc_config_struct_init(im_adc_config_t *config);

功能说明: 把设备配置结构体填充为默认值,不访问硬件。

参数表:

参数 类型 方向 说明
config im_adc_config_t * 输出 待初始化的配置;NULL 时直接返回

返回值表:

返回值 说明
无 接口不返回状态码

补充说明:

  • 只允许在线程上下文调用,不访问硬件,也不修改 im_adc_t 对象。
  • 默认值:instance = IM_ADC_INSTANCE_0、run_mode = IM_ADC_RUN_MODE_POLLING、adc_clock_div = HAL_ADC_DEFAULT_CLK_DIV、sample_time = IM_ADC_SAMPLE_TIME_EXTRA_0、event_callback = NULL、user_ctx = NULL。
  • 应先调用本接口再覆盖所需字段,以兼容后续新增字段。

5.1.2 im_adc_request_struct_init

void im_adc_request_struct_init(im_adc_request_t *request);

功能说明: 把采样请求填充为默认值,不访问硬件。

参数表:

参数 类型 方向 说明
request im_adc_request_t * 输出 待初始化的采样请求;NULL 时直接返回

返回值表:

返回值 说明
无 接口不返回状态码

补充说明:

  • 只允许在线程上下文调用,不修改 im_adc_t 对象。
  • 默认值:mode = IM_ADC_ACQUISITION_SINGLE、trigger = IM_ADC_TRIGGER_SOFTWARE、channel_count = 1、channels[0] = 0、sample_buffer_out = NULL、sample_capacity = 0。
  • 默认请求不能直接启动;调用方必须提供输出缓冲区与容量。每次提交请求前建议重新初始化,避免复用上一次的缓冲区或采集模式。

5.1.3 im_adc_monitor_config_struct_init

void im_adc_monitor_config_struct_init(im_adc_monitor_config_t *config);

功能说明: 把模拟看门狗监测配置填充为默认值,不访问硬件。

参数表:

参数 类型 方向 说明
config im_adc_monitor_config_t * 输出 待初始化的监测配置;NULL 时直接返回

返回值表:

返回值 说明
无 接口不返回状态码

补充说明:

  • 只允许在线程上下文调用。
  • 默认值:channel = 0、low_threshold = 0、high_threshold = HAL_ADC_THRESHOLD_MAX(0x0FFF)、low_threshold_enable、high_threshold_enable、fail_enable 均为 false。
  • 默认配置等价于关闭该通道监测,不会产生 monitor 事件。

5.1.4 im_adc_init

error_code_t im_adc_init(im_adc_t *dev,
                         const im_adc_config_t *config);

功能说明: 初始化一个 ADC MID 对象并绑定逻辑 ADC 实例,使状态从 IM_ADC_STATE_RESET 进入 IM_ADC_STATE_READY。

参数表:

参数 类型 方向 说明
dev im_adc_t * 输入/输出 调用方提供的零初始化对象;成功后其内部字段由 MID 管理
config const im_adc_config_t * 输入 完整设备配置;MID 在成功路径复制配置,返回后调用方可修改或释放原对象

返回值表:

返回值 说明
IM_OK 初始化成功,对象状态为 IM_ADC_STATE_READY
IM_ERR_INVALID_PARAM dev 或 config 为空,或 instance、run_mode、sample_time 枚举值非法
IM_ERR_ALREADY_EXISTS 对象不是 RESET 状态,或所选 ADC 实例已被另一个 MID 对象占用
IM_ERR_NO_DEVICE 所选逻辑实例未映射到可用 ADC 外设
其他 HAL 返回值 HAL handle 初始化、ADC 初始化或回调注册失败时返回的第一个错误

补充说明:

  • 只能在线程上下文调用,会初始化同步资源和 ADC 硬件,禁止在 ISR 中调用。
  • 初始化顺序:初始化 waiter → hal_adc_handle_init()(内部已调用 hal_adc_init_config_default() 填充默认配置)→ 覆盖 adc_clk_div 与 soc_extra_cycle → 写入 hal_handle.user_data = dev → hal_adc_init() → 注册 SEQ0/SEQ1/SEQ2 完成、SEQ1/SEQ2 DMA 完成、错误和 AWD 回调。
  • adc_clock_div 的类型是 uint8_t,其全部取值都在 HAL 的 HAL_ADC_CLK_DIV_MAX(0xFF)范围内,因此 MID 不对该字段做额外范围校验;sample_time 必须是 im_adc_sample_time_t 中列出的枚举值。
  • IRQ 与 DMA 资源不在初始化阶段申请,而是在采样请求需要时申请。
  • 任一步失败时,MID 会反初始化已初始化的 HAL、释放实例占用、清零对象并恢复 RESET,返回第一个失败码。因此 im_adc_init() 返回失败后不需要再调用 im_adc_deinit()。
  • 同一对象重复调用不会隐式 deinit,而是返回 IM_ERR_ALREADY_EXISTS;修改配置必须通过 deinit 后再 init。

5.2 模拟看门狗监测

5.2.1 im_adc_monitor_config

error_code_t im_adc_monitor_config(
    im_adc_t *dev,
    const im_adc_monitor_config_t *config);

功能说明: 为一个 ADC 通道配置低/高阈值比较和可选的硬件 fail 输出。该接口可重复调用以配置多个通道;寄存器访问由 HAL 完成。

参数表:

参数 类型 方向 说明
dev im_adc_t * 输入/输出 已初始化且处于 IM_ADC_STATE_READY 的 ADC 对象
config const im_adc_monitor_config_t * 输入 通道监测配置,MID 内部转换为 hal_adc_awd_config_t

返回值表:

返回值 说明
IM_OK 配置成功
IM_ERR_INVALID_PARAM 空指针、通道越界、阈值超过 HAL_ADC_THRESHOLD_MAX、低阈值大于高阈值,或没有 event_callback 却启用了阈值比较且未启用 fail 输出
IM_ERR_NOT_READY 对象处于 RESET 或 IM_ADC_STATE_ERROR
IM_ERR_BUSY 设备正在采样,拒绝修改阈值
IM_ERR_HARDWARE HAL 的 AWD 配置失败

补充说明:

  • 配置了 MID event_callback 且启用低/高比较时,MID 会在 HAL 配置中使能 AWD 中断;event_callback 为空时只允许关闭监测或使用 fail 输出配置,否则返回 IM_ERR_INVALID_PARAM。
  • 低/高比较全部关闭且 fail_enable == false 时,HAL 会关闭该通道的阈值监测和中断,不会产生 monitor 事件。
  • HAL 的 AWD 回调在 ADC 中断上下文执行。MID 分别以 IM_ADC_EVENT_MONITOR_LOW 和 IM_ADC_EVENT_MONITOR_HIGH 通知非零的低/高标志;两类标志同时出现时会回调两次,事件快照中的 monitor_low_flags / monitor_high_flags 都携带本次的完整标志。
  • monitor 事件不改变 READY/BUSY 状态,也不写入 error_flags,可以在采样前后配置。
  • monitor 事件的交付依赖 ADC 中断通路,MID 只在 IRQ/DMA 采样启动时打开该通路(见 6.3)。只用 Polling 采样的对象不会收到 monitor 回调。

5.3 采样启动与等待

5.3.1 im_adc_start

error_code_t im_adc_start(im_adc_t *dev,
                          const im_adc_request_t *request);

功能说明: 启动一次 MID 采样请求。Polling 模式在调用线程内完成;IRQ/DMA 模式在成功启动后返回,结果由回调以及后续 im_adc_wait() / getter 接口交付。

参数表:

参数 类型 方向 说明
dev im_adc_t * 输入/输出 已初始化且处于 IM_ADC_STATE_READY 的 ADC 对象
request const im_adc_request_t * 输入 采样请求;MID 复制结构体,但不复制 sample_buffer_out 指向的缓冲区

返回值表:

返回值 说明
IM_OK Polling 请求已成功完成,或 IRQ/DMA 请求已成功启动
IM_ERR_INVALID_PARAM 空指针、模式/触发枚举越界、channel_count 为 0 或超过 8、通道号超过 7、输出缓冲区为空或容量为 0、FINITE/CONTINUOUS 容量不是通道数的整数倍、CONTINUOUS 未使用 DMA 运行模式,或 DMA 模式下缓冲区未 4 字节对齐 / sample_capacity * 4 超出 UINT32_MAX
IM_ERR_NOT_READY 对象处于 RESET 或 IM_ADC_STATE_ERROR
IM_ERR_BUSY 已有采样请求正在执行
IM_ERR_UNSUPPORTED trigger 不是 IM_ADC_TRIGGER_SOFTWARE
其他 HAL/IRQ 返回值 序列配置、启动、轮询或 ADC IRQ 申请失败时返回的第一个错误

补充说明:

  • 请求校验在修改对象之前完成,校验失败不会改写上一次请求的结果、样本计数或错误标志。
  • 请求被接受后,MID 清零 sample_count 与 error_flags,把 last_status 置为 IM_ERR_NOT_READY 作为“请求进行中”的初始值,复制请求,并使对象进入 IM_ADC_STATE_BUSY。
  • 公共接口不接受 sequence 参数,序列由运行模式与采集模式决定,见 6.1。
  • 输出缓冲区必须持续有效,直到收到 COMPLETE / ERROR / STOPPED 终结事件或 im_adc_wait() 返回。
  • Polling 路径:按帧调用 hal_adc_start() → hal_adc_poll_for_conversion()(每帧超时为 IM_ADC_DEFAULT_POLL_TIMEOUT_US)→ hal_adc_data_get() 并按 12-bit 值写入输出缓冲区,全部完成后发布 IM_ADC_EVENT_COMPLETE 并回到 READY。
  • IRQ 路径:申请并使能 ADC IRQ 后调用 hal_adc_start_it();SINGLE 在一帧完成后终结,FINITE 需要逐帧重启直到填满 sample_capacity。FINITE 的重启由 MID 的中断尾部服务在 HAL 完成本次中断清理之后执行,避免丢失紧随其后的帧完成事件。
  • DMA 的 SINGLE/FINITE 路径:使用 SEQ2 与单 descriptor 循环 DMA。每帧完成后停止该 descriptor,FINITE 再按 sample_count 偏移重新配置并启动下一帧。HAL 按 packed 形式把帧数据写入输出缓冲区,MID 在回调中剥离通道号并保留 12-bit 采样值。DMA 启动与停止路径的 cache 维护由 HAL 负责。
  • DMA CONTINUOUS 路径:使用 SEQ1 连续组与一个覆盖整个调用方缓冲区的循环 descriptor,保持 BUSY,按完整缓冲区发布 IM_ADC_EVENT_FRAME_READY(详见 6.4)。启动期间 MID 会短暂关闭 ADC 的中断入口,待 hal_adc_start_it() 返回后再打开,避免启动阶段重入中断。
  • 启动失败时,MID 停止已启动的 HAL 序列、关闭本次申请的 ADC IRQ、记录 last_status、置对象为 ERROR,并按是否注册了回调决定是否发布 IM_ADC_EVENT_ERROR,同时唤醒可能存在的等待者。

5.3.2 im_adc_wait

error_code_t im_adc_wait(im_adc_t *dev, uint32_t timeout_ms);

功能说明: 等待当前 IRQ/DMA 采样结束,并返回当前或最近一次请求的最终状态。

参数表:

参数 类型 方向 说明
dev im_adc_t * 输入/输出 已初始化的 ADC MID 对象
timeout_ms uint32_t 输入 最大等待时间,单位毫秒;0 表示立即检查,IM_ADC_WAIT_FOREVER 表示永久等待

返回值表:

返回值 说明
IM_OK 当前或最近一次请求成功完成
IM_ERR_INVALID_PARAM dev 为空
IM_ERR_NOT_READY 对象处于 RESET;或请求被 im_adc_stop() 取消(停止路径把 last_status 置为 IM_ERR_NOT_READY)
IM_ERR_BUSY 已有另一个线程正在等待同一设备
IM_ERR_UNSUPPORTED 设备为 BUSY 的 CONTINUOUS 请求;该模式没有自然终结点,应使用 FRAME_READY 与 im_adc_stop()
IM_ERR_TIMEOUT 等待超时,MID 已停止活动序列、锁存 IM_ADC_ERROR_TIMEOUT 并进入 ERROR
IM_ERR_HARDWARE / IM_ERR_IO / 其他 异步路径已经记录的最终状态(对象处于 ERROR 时直接返回该状态)

补充说明:

  • 仅在线程上下文调用,禁止在 ISR 或 event callback 中调用。
  • 对象处于 READY 或 ERROR 时不阻塞,直接返回 last_status;Polling 请求通常在 im_adc_start() 返回前已经完成,因此再次 wait 会立即返回 IM_OK。
  • 同一设备同时只允许一个等待者;第二个等待线程返回 IM_ERR_BUSY。
  • 超时返回前会再次检查完成竞态:只有成功领取终结权的一方才会停止序列、锁存超时标志、发布 IM_ADC_EVENT_ERROR 并进入 ERROR;领取失败的一方返回赢家已经发布的最终状态。

5.3.3 im_adc_read

error_code_t im_adc_read(im_adc_t *dev,
                         const im_adc_request_t *request,
                         uint32_t timeout_ms);

功能说明: 同步便利接口。Polling 模式直接执行 im_adc_start() 并同步完成;IRQ/DMA 模式在成功启动后调用 im_adc_wait(),成功返回时输出缓冲区已填充。

参数表:

参数 类型 方向 说明
dev im_adc_t * 输入/输出 已初始化且处于 READY 的 ADC 对象
request const im_adc_request_t * 输入 SINGLE 或 FINITE 采样请求
timeout_ms uint32_t 输入 IRQ/DMA 同步操作的最大等待时间,单位毫秒;可使用 IM_ADC_WAIT_FOREVER

返回值表:

返回值 说明
IM_OK 请求成功完成,sample_buffer_out[0..sample_count-1] 均有效
IM_ERR_INVALID_PARAM dev 或 request 为空
IM_ERR_UNSUPPORTED request->mode 为 IM_ADC_ACQUISITION_CONTINUOUS
im_adc_start() 的返回值 请求校验、状态、能力或启动阶段的错误
im_adc_wait() 的返回值 IRQ/DMA 等待阶段的最终状态

补充说明:

  • 仅在线程上下文调用,禁止在 ISR 或 ADC event callback 中调用。
  • 该接口不支持连续采样:没有单一终结结果时调用方应改用 im_adc_start()、FRAME_READY 和 im_adc_stop()。
  • IRQ/DMA 模式下 timeout_ms 直接传给 im_adc_wait(),当前实现不扣减启动阶段已经消耗的时间。
  • 同步调用不会抑制 COMPLETE、ERROR 或 monitor 事件回调。

5.4 停止与释放

5.4.1 请求终结、主动停止与对象释放的区别

MID 内部的 im_adc_finish() 不是公开 API,它与公开的 im_adc_stop()、im_adc_deinit() 处于不同层次:

项目 im_adc_finish()(内部) im_adc_stop()(公开) im_adc_deinit()(公开)
触发者 完成回调、错误回调、等待超时等内部终结路径 应用线程主动取消或恢复 应用线程结束对象生命周期
作用范围 一次已接受的采样请求 当前活动请求或 ERROR 恢复 整个 im_adc_t 对象
核心行为 原子领取终结权、停止序列、记录最终状态、发布终结事件、唤醒等待者、释放请求字段 停止序列、关闭 ADC IRQ;BUSY 时发布 STOPPED,ERROR 时尝试恢复 必要时先 stop,再关闭 IRQ、唤醒等待者、HAL deinit、解除实例占用、清零对象
成功后的状态 READY(成功完成)或 ERROR(其它终结结果) READY RESET
后续操作 可再次 start;ERROR 终结后需要 stop 或 deinit 恢复 可再次 start 仅可重新 init

im_adc_finish() 通过 finish_claimed 的原子 compare-and-exchange 领取一次请求的终结权。这样 ADC 中断的完成/错误回调与线程 im_adc_wait() 超时竞争时,只有成功把该标志从 0 改为 1 的上下文能够停止硬件、写入最终状态并发布终结事件;失败方读取赢家已经发布的最终状态,避免重复事件或用 TIMEOUT 覆盖已完成结果。

5.4.2 im_adc_stop

error_code_t im_adc_stop(im_adc_t *dev);

功能说明: 停止当前采样,关闭 ADC 中断,释放调用方缓冲区的所有权,并尝试把对象恢复到 READY。

参数表:

参数 类型 方向 说明
dev im_adc_t * 输入/输出 已初始化的 ADC MID 对象

返回值表:

返回值 说明
IM_OK 停止成功,或对象原本已处于 READY
IM_ERR_INVALID_PARAM dev 为空
IM_ERR_NOT_READY 对象仍处于 RESET,尚未初始化
其他 HAL 返回值 HAL 无法停止序列;对象保持 ERROR

补充说明:

  • 仅在线程上下文调用,禁止从 event callback 中调用。
  • BUSY 时先原子领取终结权(避免已挂起的 DMA 进度回调再发布 FRAME_READY),再按启动路径选择 hal_adc_stop_it()(异步)或 hal_adc_stop()(Polling)停止序列,随后释放 ADC IRQ;成功后 last_status 置为 IM_ERR_NOT_READY、状态回到 READY、发布 IM_ADC_EVENT_STOPPED 并唤醒等待者,等待线程据此识别“被取消”而不是“已完成”。
  • READY 时接口幂等,不访问硬件也不产生 STOPPED 事件。
  • ERROR 时尝试停止可能残留的序列;成功则清理 MID 运行态并回到 READY,失败则记录 IM_ADC_ERROR_INTERNAL 并保持 ERROR。
  • 停止路径不会修改初始化配置和已配置的模拟看门狗阈值,之后可以再次 start。
  • im_adc_stop() 不解除 ADC 实例占用,也不反初始化 HAL;这些工作只由 im_adc_deinit() 完成。
  • 在 BUSY 状态下停止 CONTINUOUS 请求是结束循环采样的正式方式。

5.4.3 im_adc_deinit

error_code_t im_adc_deinit(im_adc_t *dev);

功能说明: 结束 MID 对象生命周期。活动采样先按 stop 语义终止,再释放 IRQ/等待者资源、反初始化 HAL 并解除逻辑 ADC 实例占用。

参数表:

参数 类型 方向 说明
dev im_adc_t * 输入/输出 已初始化或处于 ERROR 的 ADC MID 对象

返回值表:

返回值 说明
IM_OK 所有资源已释放,对象恢复 RESET
IM_ERR_INVALID_PARAM dev 为空
IM_ERR_NOT_READY 对象已经处于 RESET
其他 HAL 返回值 停止或 HAL deinit 遇到的第一个底层错误

补充说明:

  • 仅在线程上下文调用,不得在 ISR 或 event callback 中调用。
  • READY、BUSY 和 ERROR 均可调用。BUSY/ERROR 先按 stop 语义停止;即使该步骤失败,MID 仍会继续关闭 IRQ、以 IM_ERR_NOT_READY 唤醒等待者并执行 hal_adc_deinit(),最终返回第一个错误。
  • HAL deinit 成功后,MID 解除 ADC0/ADC1 占用并清零整个 im_adc_t;对象可立即重新调用 im_adc_init()。
  • 若 HAL deinit 失败,MID 保留最小 ERROR 标记和实例占用,禁止把该内存当作 RESET 对象直接重新初始化;应用必须先处理底层失败。
  • 只要 im_adc_init() 曾成功返回,应用就应在离开使用范围前调用 im_adc_deinit();后续 start/read/wait/stop 失败不改变这一要求。

5.5 状态读取与诊断

5.5.1 im_adc_get_state

im_adc_state_t im_adc_get_state(const im_adc_t *dev);

功能说明: 以无锁原子快照读取当前 MID 状态。

参数表:

参数 类型 方向 说明
dev const im_adc_t * 输入 ADC 对象;允许为 NULL

返回值表:

返回值 说明
IM_ADC_STATE_RESET dev 为 NULL、对象未通过 MID 内部 magic 校验,或状态值无效
IM_ADC_STATE_READY 已初始化且可接收请求
IM_ADC_STATE_BUSY 当前有活动采样请求
IM_ADC_STATE_ERROR 最近一次终结结果为失败,需要 stop 或 deinit 恢复

补充说明:

  • 可在任意线程上下文使用;不获取可能阻塞的锁。
  • ISR 中仅在平台保证对象字段原子读取时使用。

5.5.2 im_adc_get_last_status

error_code_t im_adc_get_last_status(const im_adc_t *dev);

功能说明: 读取最近一次 init、monitor_config、start/read/wait/stop 等操作记录的最终公共错误码。

参数表:

参数 类型 方向 说明
dev const im_adc_t * 输入 ADC 对象

返回值表:

返回值 说明
IM_OK 或 IM_ERR_* 最近一次已记录的状态
IM_ERR_INVALID_PARAM dev 为空或对象无效

补充说明:

  • last_status 只用于判断最近一次操作是否成功,不代替 im_adc_get_error() 提供的硬件原因位。
  • 请求刚被接受、尚未终结时 last_status 为 IM_ERR_NOT_READY;因此不能仅凭该值判定失败,应结合 im_adc_get_state()。
  • im_adc_deinit() 成功后会清零对象,此后本接口(以及 5.5.3 和 5.5.4)按无效对象返回 IM_ERR_INVALID_PARAM / IM_ADC_ERROR_INTERNAL / 0。
  • 只读、不访问硬件,也不构成跨线程同步屏障。

5.5.3 im_adc_get_error

uint32_t im_adc_get_error(const im_adc_t *dev);

功能说明: 读取本次采样锁存的 IM_ADC_ERROR_* 运行期错误位。

参数表:

参数 类型 方向 说明
dev const im_adc_t * 输入 ADC 对象;允许为 NULL

返回值表:

返回值 说明
IM_ADC_ERROR_* bitmask 本次采样锁存的运行期错误位
IM_ADC_ERROR_INTERNAL dev 为空、未初始化或对象无效

补充说明:

  • 多个错误位可按位组合,例如 DMA 错误和 data overrun 可以同时存在。
  • 标志在请求被接受时清零,参数校验失败不会覆盖上一笔运行期错误;请求成功后标志保持到下一次被接受的 start。
  • 只读、不访问硬件,也不构成跨线程同步屏障。可在错误回调中读取,但事件回调的 info 快照更直接。

5.5.4 im_adc_get_sample_count

uint32_t im_adc_get_sample_count(const im_adc_t *dev);

功能说明: 读取自最近一次被接受的 im_adc_start() 请求起,已经交付到调用方缓冲区的有效样本数。

参数表:

参数 类型 方向 说明
dev const im_adc_t * 输入 ADC 对象;允许为 NULL

返回值表:

返回值 说明
样本计数 有效样本数;查询本身不会失败

补充说明:

  • SINGLE/FINITE 完成时不超过 sample_capacity;出错时表示缓冲区中已确认有效的前缀长度。
  • CONTINUOUS 按完整缓冲区累计(每发布一个 FRAME_READY 增加 sample_capacity),可以超过单个缓冲区容量;被丢弃的帧不计入,计数在 UINT32_MAX 饱和。
  • dev 无效或尚未接受过采样请求时返回 0;参数校验失败不会覆盖上一笔计数。
  • 只读、不访问硬件;返回值为原子快照,可与事件回调并发读取,但不构成跨线程同步屏障。

5.5.5 im_adc_get_dropped_frame_count

uint32_t im_adc_get_dropped_frame_count(const im_adc_t *dev);

功能说明: 读取自最近一次被接受的 im_adc_start() 请求起,因内部连续 DMA 积压上限而未能发布的完整缓冲区数量。

参数表:

参数 类型 方向 说明
dev const im_adc_t * 输入 ADC 对象;允许为 NULL

返回值表:

返回值 说明
丢弃帧计数 未能发布的完整缓冲区数量
0 dev 无效、未接受过采样请求,或没有发生丢弃

补充说明:

  • 只对 CONTINUOUS DMA 请求有意义;其它模式(以及没有发生积压的情况)保持为 0,每次被接受的 start 会清零。
  • 连续采样默认把完成的缓冲区按序顺延发布;只有一次服务期间累计完成的缓冲区超过内部积压上限(当前为 8 个完整缓冲区)时,超出部分才会被计入该计数。
  • 该计数反映 MID 的发布能力,不代表样本丢失:循环 DMA 仍在持续覆盖调用方缓冲区,缓冲区内容始终是最近写入的采样数据。
  • 只读、不访问硬件;返回值为原子快照。

5.5.6 im_adc_regs_dump

error_code_t im_adc_regs_dump(im_adc_instance_t instance);

功能说明: 请求指定 ADC 实例执行 HAL 寄存器 dump。MID 把 instance 映射为 ADC0 或 ADC1,仅创建一个临时 HAL 软件 handle 后调用 hal_adc_regs_dump();详细寄存器内容由 HAL 通过 SDK 日志后端输出。

参数表:

参数 类型 方向 说明
instance im_adc_instance_t 输入 要转储的逻辑实例,只支持 IM_ADC_INSTANCE_0 和 IM_ADC_INSTANCE_1

返回值表:

返回值 说明
IM_OK HAL 已完成该实例的寄存器 dump
IM_ERR_INVALID_PARAM instance 不是支持的逻辑 ADC 实例,或 HAL 无法初始化临时软件 handle
IM_ERR_NO_DEVICE 指定实例未映射到可用 ADC 硬件
IM_ERR_UNSUPPORTED 未同时启用 CONFIG_SDK_ENABLE_DEBUG_HELPERS 和 CONFIG_ADC_ENABLE_DEBUG_HELPERS,HAL dump 未编译
其他 HAL 返回值 HAL 访问寄存器或执行 dump 时的错误

补充说明:

  • 这是线程上下文诊断接口;它不调用 im_adc_init(),不获取 MID 实例所有权,也不调用 hal_adc_init(),因此不会重新配置、停止或抢占正在被其它业务使用的 ADC。
  • HAL 只读取寄存器快照,默认不读取 SEQ1_FIFO / SEQ2_FIFO,避免 FIFO 读操作消耗待处理采样数据;如确需该破坏性读取,需按 HAL 的 HAL_ADC_DUMP_READ_FIFO 配置处理。
  • dump 输出走 SDK 日志后端而非函数返回缓冲区,调用方应确保 INFO 级日志可见。
  • 该快照不对正在运行的 ADC 加锁:硬件持续采样时,多个寄存器值可能来自不同时间点。

6. 行为约束与实现说明

6.1 序列选择规则

序列由运行模式和采集模式共同决定,应用不能直接指定:

运行模式 采集模式 使用序列 判定依据
Polling SINGLE channel_count <= 4 时为 SEQ0,否则 SEQ1 HAL_ADC_SEQ0_MAX_LENGTH 为 4
Polling FINITE SEQ2 有限采样需要逐帧重启
IRQ SINGLE channel_count <= 4 时为 SEQ0,否则 SEQ1 同 Polling
IRQ FINITE SEQ2 每帧完成中断后重启下一帧
DMA SINGLE / FINITE SEQ2 单 descriptor 循环 DMA,每帧后可停止并重配
DMA CONTINUOUS SEQ1(1 个连续组) SEQ1 是唯一支持连续转换的序列

6.3 中断与 DMA 资源的申请和释放

  • MID 使用 ADC 自身的 IRQ(IRQ_SADC0 / IRQ_SADC1)作为唯一中断入口,im_adc_irq_entry() 调用 hal_adc_irq_handler() 处理 ADC 中断状态与回调分发。
  • IRQ 与 DMA 资源在采样请求需要时申请:Polling 请求不申请 IRQ;IRQ/DMA 请求在 im_adc_start() 中申请并使能 ADC IRQ,DMA 通道由 HAL 在序列配置时绑定。
  • MID 不注册也不接管 PDMA 中断。连续 DMA 的 ADC DMA_FIN 是 ADC 级中断(HAL 在 hal_adc_irq_handler() 中处理并调用 DMA 完成回调),因此不需要 PDMA IRQ,也不依赖 MIDDLEWARE_DMA。
  • ADC IRQ 在以下路径释放:im_adc_stop()、im_adc_deinit(),以及启动失败清理。IRQ/DMA 模式的 SINGLE/FINITE 成功完成后 IRQ 仍保持打开,直到应用调用 stop 或 deinit。
  • 由于 monitor 事件依赖 ADC IRQ 通路,只用 Polling 采样的对象即使已按配置使能 AWD 中断,也不会收到 monitor 回调。

6.4 连续 DMA 的交付语义

连续采样(IM_ADC_ACQUISITION_CONTINUOUS + IM_ADC_RUN_MODE_DMA)的交付规则:

  • MID 用 SEQ1 连续组配置 ADC,使用一个覆盖调用方整个缓冲区的循环 descriptor(dma_len = sample_capacity * 4)。
  • ADC 的 DMA_FIN 只是 FIFO/DMA 进度通知,不是一帧完成的标志。MID 在每次进度回调中用 HAL 提供的 SEQ1_DMA_RCNT 字节计数计算本次增量并累计,只有累计到整个调用方缓冲区长度时才:
    1. 把 sample_count 增加一个 sample_capacity(饱和于 UINT32_MAX);
    2. 发布一次 IM_ADC_EVENT_FRAME_READY;
    3. 保持 BUSY 并继续循环采样。
  • 每次进度服务最多发布 4 个 FRAME_READY;未能发布的部分保留为积压并顺延到后续服务,积压上限为 8 个完整缓冲区,超出该上限的完成量会失去发布机会并计入 im_adc_get_dropped_frame_count()。
  • MID 关心的是 DMA 字节计数的增量,因此两次进度服务之间不能跨过 64 KiB 的 DMA 进度(内部按计数宽度做掩码处理,见 6.7)。只要 ADC 中断能及时得到服务,单次增量远小于该上限。
  • 若某次进度增量不是 4 字节(HAL_ADC_DMA_UNIT_SIZE)的整数倍,MID 认为 DMA 搬运异常,以 IM_ADC_ERROR_DMA 终结请求并进入 ERROR。
  • 连续采样没有自然终结点:不产生 COMPLETE,im_adc_wait() 在 BUSY 时返回 IM_ERR_UNSUPPORTED,im_adc_read() 直接拒绝该模式。结束方式是在线程上下文调用 im_adc_stop()(随后可再次 start)或 im_adc_deinit()。
  • FRAME_READY 回调触发时循环 DMA 可能已经覆盖同一缓冲区,因此回调内应尽快复制数据或向线程投递任务,不应假定缓冲区停留在回调那一刻的内容。

6.6 超时、错误与恢复

  • 运行期错误映射:IM_ADC_ERROR_TIMEOUT(im_adc_wait() 超时或 HAL HAL_ADC_ERROR_EOC_TIMEOUT)、IM_ADC_ERROR_DATA_OVERRUN(HAL 数据/ FIFO 溢出)、IM_ADC_ERROR_EVENT_OVERRUN(HAL 事件溢出)、IM_ADC_ERROR_PHY(HAL PHY EOC 异常)、IM_ADC_ERROR_DMA(HAL DMA 错误或连续 DMA 进度统计异常)、IM_ADC_ERROR_INTERNAL(MID 内部路径失败,例如样本搬运/重启失败、HAL 停止失败、启动阶段的 HAL 硬件或 IO 错误)。
  • 进入 ERROR 的方式:采样过程中的硬件/内部错误、im_adc_wait() 超时、以及启动阶段 HAL 调用失败。参数或状态校验失败(IM_ERR_INVALID_PARAM、IM_ERR_NOT_READY、IM_ERR_BUSY、IM_ERR_UNSUPPORTED)不改变对象状态,也不产生错误标志或事件。
  • 恢复方式:im_adc_stop() 成功可从 ERROR 回到 READY 并保留初始化配置和实例占用;im_adc_deinit() 用于彻底结束对象。ERROR 状态下 im_adc_start() 返回 IM_ERR_NOT_READY,im_adc_monitor_config() 同样返回 IM_ERR_NOT_READY。
  • error_flags 在请求被接受时清零,之后累加本次请求遇到的运行期错误;last_status 记录最近一次操作的最终公共状态。

6.7 已知限制与注意事项

  • 触发源:当前 im_adc_start() 只接受 IM_ADC_TRIGGER_SOFTWARE。HAL 可以写入 TCU/PWM 触发选择,但没有公开的“仅 arm 外部触发”接口,因此 TCU/PWM 枚举被拒绝并返回 IM_ERR_UNSUPPORTED。
  • 连续采样:仅支持 DMA 运行模式、单 descriptor 循环 DMA,且 sample_capacity 必须是 channel_count 的整数倍;其它组合在请求校验阶段返回 IM_ERR_INVALID_PARAM。
  • 采样率与组间隔:MID 不提供公开的采样率、连续组间隔或 FIFO 水位配置,连续路径使用固定的内部常量(当前为 FIFO 水位 HAL_ADC_DRT_FIFO_GE_16 与组间隔 0x800,即 im_adc.c 中的 IM_ADC_SEQ1_CONTINUOUS_INTERVAL,均为源码内部实现,不是公开配置项)。
  • DMA 进度统计:内部按 16 位计数宽度处理 SEQ1_DMA_RCNT 增量,因此两次进度服务之间的 DMA 进度差不能达到或超过 64 KiB,否则该次进度会被误判为没有推进。该约束要求 ADC 中断能被及时服务。
  • monitor 事件:阈值比较由硬件在采样转换时完成;MID 只在启用 event_callback、且 ADC IRQ 通路已打开(IRQ/DMA 采样期间或之后未 stop)时才能上报事件。仅使用 Polling 采样时不会收到 monitor 回调。
  • 引脚复用:ADC 模拟输入引脚必须由板级 BSP/pinmux 预先配置,MID 与 HAL 都不处理 GPIO。
  • 未在本文档中承诺的行为:im_adc_t 内部字段布局、上述内部常量取值、以及一次设备服务内可发布的事件次数上限,都属于实现细节,后续版本可能调整。

7. MID 错误码与错误标志

MID 接口使用 SDK 公共错误码类型 error_code_t,并通过 im_adc_event_info_t.error_flags / im_adc_get_error() 提供运行期错误位。

公共错误码出现场景
IM_OK操作成功
IM_ERR_INVALID_PARAM空指针、枚举非法、通道/阈值越界、缓冲区或容量不合法、非法的模式组合
IM_ERR_NOT_READY对象未初始化、处于 ERROR、请求被 stop 取消,或 last_status 记录到的初始状态
IM_ERR_BUSY已有采样请求在执行,或已有另一个线程在等待同一设备
IM_ERR_ALREADY_EXISTS对象不是 RESET,或所选 ADC 实例已被占用
IM_ERR_NO_DEVICE所选逻辑实例未映射到可用 ADC 外设
IM_ERR_UNSUPPORTED非软件触发;im_adc_read() 收到 CONTINUOUS 请求;BUSY 的 CONTINUOUS 请求调用 im_adc_wait();HAL dump 未编译
IM_ERR_TIMEOUTPolling 轮询或 im_adc_wait() 等待超时
IM_ERR_HARDWARE / IM_ERR_IOADC 运行期硬件或数据路径错误,以及 HAL 停止、HAL AWD 配置失败
运行期错误位说明
IM_ADC_ERROR_TIMEOUT采样超时(im_adc_wait() 超时或 HAL EOC 超时)
IM_ADC_ERROR_DATA_OVERRUNADC 数据存储或 FIFO 溢出
IM_ADC_ERROR_EVENT_OVERRUN转换事件溢出
IM_ADC_ERROR_PHYADC PHY 转换结束异常
IM_ADC_ERROR_DMAADC DMA 错误,或连续 DMA 字节进度增量异常
IM_ADC_ERROR_INTERNALMID 内部路径失败(样本搬运/重启失败、HAL 停止失败、启动阶段硬件或 IO 失败)