5.11 ADC设备
2026/7/19大约 5 分钟设备驱动驱动ADC模数转换模拟采样
5.11 ADC设备
📚 本节导读
学习时长: 约 30 分钟
难度级别: ⭐⭐⭐☆☆
前置知识: C 语言基础、MCU ADC 基础知识、5.1 设备驱动框架
🎯 学习目标
- 理解 OneOS ADC 设备驱动框架的分层设计
- 掌握 ADC 设备的使能与禁用操作
- 掌握单通道和多通道 ADC 读取方法
- 了解 ADC 中断通知机制
- 理解参考电压与 ADC 值的换算关系
一、概述
ADC(Analog-to-Digital Converter)是 MCU 将模拟信号转换为数字信号的外设。OneOS 将 ADC 抽象为设备,提供统一的使能、读取、控制接口。ADC 设备驱动位于 drivers/misc/ 目录下,核心头文件为 adc.h,实现文件为 adc.c。
ADC 设备框架的特点:
- 统一抽象:将不同 MCU 平台的 ADC 统一封装
- 多通道支持:通过
channel参数指定采样通道 - 中断通知:支持
OS_ADC_EVENT_CONVERT_DONE事件通知 - 参考电压换算:通过
mult/shift系数将 ADC 值换算为电压值(mV)
二、核心数据结构
2.1 ADC 设备结构体 os_adc_device_t
struct os_adc_device
{
struct os_device parent; /* 继承自 os_device_t */
const struct os_adc_ops *ops; /* ADC 操作函数表 */
int32_t ref_hight; /* 参考电压高值(mV) */
int32_t ref_low; /* 参考电压低值(mV) */
uint32_t max_value; /* ADC 最大转换值(如 12bit ADC 为 4095) */
uint32_t mult; /* adc voltage(mV) = value * mult >> shift */
uint32_t shift;
};
typedef struct os_adc_device os_adc_device_t;| 字段 | 说明 |
|---|---|
parent | 继承自 os_device_t,实现设备统一管理 |
ops | ADC 操作函数表,指向 HAL 层实现 |
ref_hight | 参考电压高值,单位 mV(如 3300 表示 3.3V) |
ref_low | 参考电压低值,单位 mV(通常为 0) |
max_value | ADC 最大转换值(分辨率-1,如 12bit 为 4095) |
mult/shift | 电压换算系数,ADC 值转电压(mV)= value * mult >> shift |
2.2 ADC 操作函数表 struct os_adc_ops
struct os_adc_ops
{
os_err_t (*adc_enabled)(struct os_adc_device *dev, os_bool_t enable);
os_err_t (*adc_control)(struct os_adc_device *dev, int cmd, void *arg);
os_err_t (*adc_read)(struct os_adc_device *dev, uint32_t channel, int32_t *buffer);
};| 函数指针 | 说明 |
|---|---|
adc_enabled | 使能/禁用 ADC 外设 |
adc_control | 发送控制命令 |
adc_read | 读取指定通道的 ADC 转换值 |
三、API 详解
3.1 读取 ADC 值
os_err_t os_adc_read(struct os_adc_device *dev, uint32_t channel, int32_t *buffer);| 参数 | 说明 |
|---|---|
dev | ADC 设备指针 |
channel | ADC 通道号(0 ~ N) |
buffer | 输出缓冲区,存放读取的 ADC 值 |
| 返回值 | OS_SUCCESS 成功,其他值表示失败 |
3.2 使能与禁用
os_err_t os_adc_enable(struct os_adc_device *dev);
os_err_t os_adc_disable(struct os_adc_device *dev);3.3 控制命令
#define OS_ADC_CMD_ENABLE IOC_MISCELLANEOUS(1) /* 使能 ADC */
#define OS_ADC_CMD_DISABLE IOC_MISCELLANEOUS(2) /* 禁用 ADC */通过 os_device_control() 接口发送控制命令,最终调用 ops->adc_enabled。
3.4 中断通知(HAL 层使用)
void os_hw_adc_isr(struct os_adc_device *dev, int event);HAL 层在 ADC 转换完成中断中调用此函数,向等待的任务发送通知。事件类型为 OS_ADC_EVENT_CONVERT_DONE。
3.5 注册 ADC 设备
os_err_t os_hw_adc_register(struct os_adc_device *device, const char *name, void *data);四、电压换算公式
ADC 设备注册时自动计算电压换算系数:
实际电压(mV) = ADC值 × mult >> shift
其中:
ref_voltage = ref_hight - ref_low (参考电压范围,mV)
mult 和 shift 由 calc_mult_shift(&mult, &shift, max_value, ref_voltage, 1) 计算例如对于 12bit ADC,参考电压 3.3V:
max_value = 4095,ref_hight = 3300,ref_low = 0- 若 ADC 读数为 2048,则电压 ≈ 2048 × 3300 / 4095 ≈ 1650 mV
五、代码示例
5.1 基本使用:单通道读取
#include <oneos_config.h>
#include <device.h>
#include <adc.h>
void adc_basic_example(void)
{
os_device_t *adc_dev;
int32_t adc_value;
/* 查找 ADC 设备 */
adc_dev = os_device_open_s("adc1");
if (adc_dev == OS_NULL)
{
os_kprintf("ADC device not found!\r\n");
return;
}
/* 使能 ADC */
os_device_control(adc_dev, OS_ADC_CMD_ENABLE, OS_NULL);
while (1)
{
/* 读取通道 0 的 ADC 值 */
os_adc_read((os_adc_device_t *)adc_dev, 0, &adc_value);
/* 计算电压值(mV) */
os_adc_device_t *adc = (os_adc_device_t *)adc_dev;
int32_t voltage_mv = (int32_t)(((uint64_t)adc_value * adc->mult) >> adc->shift);
os_kprintf("ADC Channel 0: raw=%d, voltage=%d mV\r\n", adc_value, voltage_mv);
os_task_msleep(500);
}
}5.2 多通道循环读取
#define ADC_CHANNEL_COUNT 4
void adc_multi_channel_example(void)
{
os_device_t *adc_dev;
int32_t adc_values[ADC_CHANNEL_COUNT];
int i;
adc_dev = os_device_open_s("adc1");
if (adc_dev == OS_NULL)
return;
os_device_control(adc_dev, OS_ADC_CMD_ENABLE, OS_NULL);
while (1)
{
/* 循环读取多个通道 */
for (i = 0; i < ADC_CHANNEL_COUNT; i++)
{
os_adc_read((os_adc_device_t *)adc_dev, i, &adc_values[i]);
}
os_kprintf("ADC: CH0=%d CH1=%d CH2=%d CH3=%d\r\n",
adc_values[0], adc_values[1],
adc_values[2], adc_values[3]);
os_task_msleep(1000);
}
}5.3 使用标准设备接口读取(批量)
void adc_device_read_example(void)
{
os_device_t *adc_dev;
int32_t buffer[4]; /* 4 个通道的数据 */
int bytes_read;
adc_dev = os_device_open_s("adc1");
if (adc_dev == OS_NULL)
return;
os_device_control(adc_dev, OS_ADC_CMD_ENABLE, OS_NULL);
/* 使用 os_device_read 批量读取,pos 指定起始通道 */
bytes_read = os_device_read(adc_dev, 0, buffer, sizeof(buffer));
os_kprintf("Read %d bytes (%d channels)\r\n", bytes_read, bytes_read / sizeof(int32_t));
for (int i = 0; i < bytes_read / sizeof(int32_t); i++)
{
os_kprintf(" CH%d: %d\r\n", i, buffer[i]);
}
}5.4 中断方式读取(配合通知机制)
static os_device_t *gs_adc_dev = OS_NULL;
/* ADC 读取任务 */
void adc_task(void *parameter)
{
int32_t adc_value;
while (1)
{
/* 等待 ADC 转换完成通知 */
os_device_recv_notify_wait(gs_adc_dev);
/* 读取 ADC 数据 */
os_adc_read((os_adc_device_t *)gs_adc_dev, 0, &adc_value);
os_kprintf("ADC IRQ: value=%d\r\n", adc_value);
}
}
void adc_irq_example(void)
{
gs_adc_dev = os_device_open_s("adc1");
if (gs_adc_dev == OS_NULL)
return;
os_device_control(gs_adc_dev, OS_ADC_CMD_ENABLE, OS_NULL);
}六、注意事项
- 参考电压配置:
ref_hight和ref_low必须在 HAL 层注册时正确设置,否则电压换算结果错误 - 通道有效性:确保
channel参数不超过硬件支持的最大通道数 - 采样时间:不同 ADC 外设需要不同的采样时间,由 HAL 层配置
- 中断通知:HAL 层需在 ADC 转换完成中断中调用
os_hw_adc_isr()发送通知 - 使能控制:读取前需确保 ADC 已使能(调用
os_adc_enable或OS_ADC_CMD_ENABLE)
📝 本节小结
- OneOS ADC 设备通过
os_adc_device_t统一抽象,HAL 层实现os_adc_ops接口 os_adc_read()读取指定通道的 ADC 原始值,通过mult/shift换算为电压(mV)- 支持使能/禁用控制、多通道读取、中断通知等机制
- 既可调用专用 API(
os_adc_read),也可通过标准设备接口(os_device_read)访问