ADC
ADC 驱动 | sample: adc
学习目标
- 掌握 ADC 单次采样的完整流程:
uapi_adc_init()→adc_port_read()读取电压值 - 理解 ADC 分辨率(12-bit)和参考电压的含义
- 能够在 WS63 上读取模拟传感器的电压值
基本概念
ADC 做什么
ADC(Analog-to-Digital Converter)将模拟电压转换为数字值——温度传感器、光敏电阻、电位器等模拟信号都需要 ADC 才能被 CPU 理解。
flowchart LR
A[模拟电压 0~3.3V] --> ADC[ADC 转换器]
ADC --> D[数字值 0~4095<br/>12-bit 分辨率]
D --> C[CPU 读取]
分辨率和参考电压
WS63 的 ADC 为 12-bit 分辨率——0~4095 共 4096 个量化等级。参考电压(Vref)决定测量范围:
- 输入电压 =
adc_value / 4095 × Vref - 如
Vref = 3.3V,adc_value = 2048→ 电压 =2048/4095 × 3.3 = 1.65V
开发板外部可能有分压电阻——如果测量值和实际值不符,确认是否有分压电路。
单次采样 vs 连续采样
| 模式 | 行为 | 适用场景 |
|---|---|---|
| 单次 | 触发一次转换,完成后 CPU 读结果 | 温度、光照等慢变信号 |
| 连续 | 硬件持续转换,DMA 搬数据 | 音频采样、快速波形捕捉 |
涉及 API
| API | 用途 | 头文件 |
|---|---|---|
uapi_adc_init(clock) |
初始化 ADC 模块 | adc.h |
adc_port_read(channel, &voltage) |
读取指定通道的电压值(mV) | adc_porting.h |
uapi_adc_deinit() |
去初始化 ADC | adc.h |
案例说明
做什么
初始化 ADC,每 10 秒采样一次指定通道的电压值并打印。循环 10 次后退出。
规格与功能
| 规格项 | 说明 |
|---|---|
| ADC 通道 | CONFIG_ADC_CHANNEL(Kconfig 可配) |
| 采样间隔 | 10000ms(10 秒) |
| 采样次数 | 10 次 |
| 输出单位 | mV(毫伏) |
| 任务优先级 | 26 |
程序运行流程:uapi_adc_init → 循环 10 次 adc_port_read → 打印电压 → osal_msleep(10000) → uapi_adc_deinit。
案例流程
flowchart TD
I[uapi_adc_init] --> L
L[循环 10 次] --> R[adc_port_read channel, voltage]
R --> P[printf voltage mV]
P --> S[osal_msleep 10000ms]
S --> D{10 次?}
D -->|否| R
D -->|是| E[uapi_adc_deinit]
案例操作指导
第一步:编译
第二步:烧录和接线
将待测模拟信号接入 ADC 通道引脚,注意共地。
第三步:验证
串口每 10 秒输出一次电压值:
关键配置
| 参数 | 值 | 说明 |
|---|---|---|
| ADC 通道 | Kconfig 可配 | 确认硬件引脚对应哪个 ADC 通道 |
| Vref | 通常 3.3V | 参考电压——影响计算精度 |
| 采样间隔 | 10000ms | 慢变信号(如温度),无需高频采样 |
| 分压电阻 | 注意 | 有分压则测量值需按比例还原 |
代码详解
完整代码参考 src/application/samples/peripheral/adc/adc_demo_inc.c:
#include "adc.h"
#include "adc_porting.h"
#include "soc_osal.h"
#include "app_init.h"
#define DELAY_10000MS 10000
#define CYCLES 10
#define ADC_TASK_PRIO 26
#define ADC_TASK_STACK_SIZE 0x1000
static void *adc_task(const char *arg)
{
(void)arg;
osal_printk("start adc sample\r\n");
/* 初始化 ADC——clock 参数传入 ADC_CLOCK_NONE */
uapi_adc_init(ADC_CLOCK_NONE);
uint8_t adc_channel = CONFIG_ADC_CHANNEL;
uint16_t voltage = 0;
uint32_t cnt = 0;
while (cnt++ < CYCLES) {
/* 读取指定通道电压——返回单位 mV */
adc_port_read(adc_channel, &voltage);
osal_printk("voltage: %d mv\r\n", voltage);
osal_msleep(DELAY_10000MS);
}
/* 如果测量值和实际值有较大差别——确认是否有分压电阻 */
uapi_adc_deinit();
return NULL;
}
static void adc_entry(void)
{
osal_task *task = osal_kthread_create(
(osal_kthread_handler)adc_task, 0,
"AdcTask", ADC_TASK_STACK_SIZE);
if (task != NULL) {
osal_kthread_set_priority(task, ADC_TASK_PRIO);
}
}
app_run(adc_entry);
adc_port_read()直接返回毫伏值——底层已经完成了adc_value / 4095 × Vref × 1000的转换。如果测量值明显不对,检查开发板是否有分压电阻。