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.h | MID 公开宏、类型、设备对象定义与接口声明 |
code/middleware/adc/src/im_adc.c | MID 初始化、序列/DMA 编排、轮询/中断/连续 DMA 路径、状态与诊断实现 |
code/middleware/adc/Kconfig | MIDDLEWARE_ADC 选项 |
code/middleware/adc/example/main/main.c | 示例入口:选择 ADC 实例和一个示例 |
code/middleware/adc/example/main/im_adc_example_*.c | Polling/IRQ/DMA 的 SINGLE、FINITE、CONTINUOUS 独立示例 |
code/middleware/adc/example/main/im_adc_example_functional_test.c | MID API 与硬件路径回归测试 |
code/middleware/adc/CMakeLists.txt 在 CONFIG_MIDDLEWARE_ADC 生效时编译 src/im_adc.c,并把 include/ 加入包含路径。
2. 资源模型与公开类型
2.1 实例与基地址
MID 以逻辑实例号选择 ADC 硬件:
| 逻辑实例 | 硬件实例 | 基地址宏 | 基地址 |
|---|---|---|---|
IM_ADC_INSTANCE_0 | ADC0 | SADC0_IOBASE | 0x13720000UL |
IM_ADC_INSTANCE_1 | ADC1 | SADC1_IOBASE | 0x13730000UL |
说明:
实例基地址和寄存器布局由
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_TCU | TCU 外部触发;保留编码,当前 im_adc_start() 返回 IM_ERR_UNSUPPORTED。 |
IM_ADC_TRIGGER_PWM0 | PWM0 外部触发;保留编码,当前 im_adc_start() 返回 IM_ERR_UNSUPPORTED。 |
IM_ADC_TRIGGER_PWM1 | PWM1 外部触发;保留编码,当前 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_0 | 0 |
IM_ADC_SAMPLE_TIME_EXTRA_5 | 5 |
IM_ADC_SAMPLE_TIME_EXTRA_10 | 10 |
IM_ADC_SAMPLE_TIME_EXTRA_15 | 15 |
IM_ADC_SAMPLE_TIME_EXTRA_30 | 30 |
IM_ADC_SAMPLE_TIME_EXTRA_45 | 45 |
IM_ADC_SAMPLE_TIME_EXTRA_60 | 60 |
IM_ADC_SAMPLE_TIME_EXTRA_210 | 210 |
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_COMPLETE | SINGLE 或 FINITE 请求成功完成。 |
IM_ADC_EVENT_FRAME_READY | CONTINUOUS 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_OVERRUN | ADC 数据存储或 FIFO 溢出。 |
IM_ADC_ERROR_EVENT_OVERRUN | 转换事件溢出。 |
IM_ADC_ERROR_PHY | ADC PHY 转换结束异常。 |
IM_ADC_ERROR_DMA | ADC DMA 传输或进度统计异常。 |
IM_ADC_ERROR_INTERNAL | MID 检测到活动请求执行失败。 |
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_flags | IM_ADC_ERROR_* 标志快照,对 ERROR 事件有效。 |
monitor_low_flags | bit n 对应通道 n 低于低阈值,对 MONITOR_LOW 事件有效。 |
monitor_high_flags | bit 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_count | channels[] 中有效项数,取值范围 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 运行模式与采集模式组合
| 采集模式 | 运行模式 | 转换序列 | 说明 |
|---|---|---|---|
SINGLE | Polling / IRQ | channel_count <= 4 时 SEQ0,否则 SEQ1 | 在调用线程或中断中采一帧后完成 |
SINGLE | DMA | SEQ2 | 单 descriptor 循环 DMA 搬运一帧,DMA 完成后停止 |
FINITE | Polling / IRQ / DMA | SEQ2 | 采满 sample_capacity 个样本后完成;IRQ 逐帧重启,DMA 逐帧停止并按偏移重配 |
CONTINUOUS | DMA | SEQ1(连续组,1 组) | 一个覆盖整缓冲区的循环 descriptor;保持 BUSY 直到 stop、deinit 或错误 |
CONTINUOUS | Polling / 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。
应用输入与可选配置
可选 event_callback 与 user_ctx
输出缓冲区与容量
MID 对象生命周期与所有权
占用一个 ADC 实例
HAL deinit; 释放占用; 清零对象
请求执行
CONTINUOUS 被拒绝
ADC IRQ 承载 DMA_FIN 进度与错误
完成、错误与终结路径
发布 COMPLETE; 唤醒 waiter; 释放请求字段
发布 ERROR; 唤醒 waiter; 释放请求字段
应用侧的恢复与释放
ERROR: stop 成功时恢复到 READY
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字节计数计算本次增量并累计,只有累计到整个调用方缓冲区长度时才:- 把
sample_count增加一个sample_capacity(饱和于UINT32_MAX); - 发布一次
IM_ADC_EVENT_FRAME_READY; - 保持
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()超时或 HALHAL_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_TIMEOUT | Polling 轮询或 im_adc_wait() 等待超时 |
IM_ERR_HARDWARE / IM_ERR_IO | ADC 运行期硬件或数据路径错误,以及 HAL 停止、HAL AWD 配置失败 |
| 运行期错误位 | 说明 |
|---|---|
IM_ADC_ERROR_TIMEOUT | 采样超时(im_adc_wait() 超时或 HAL EOC 超时) |
IM_ADC_ERROR_DATA_OVERRUN | ADC 数据存储或 FIFO 溢出 |
IM_ADC_ERROR_EVENT_OVERRUN | 转换事件溢出 |
IM_ADC_ERROR_PHY | ADC PHY 转换结束异常 |
IM_ADC_ERROR_DMA | ADC DMA 错误,或连续 DMA 字节进度增量异常 |
IM_ADC_ERROR_INTERNAL | MID 内部路径失败(样本搬运/重启失败、HAL 停止失败、启动阶段硬件或 IO 失败) |