
清空记录
历史记录
取消
清空记录
历史记录

ADC是嵌入式开发中绕不开的外设——按键检测、电池电压监测、温感与各类模拟传感器接入,几乎每一步都会用到。本系列环境基于触觉智能RK3576开发板Purple Pi OH2 ( Android14系统/Linux6.1内核)进行讲解与实机调试,从概念、设备树、驱动到用户空间调试逐步展开。
大家如需真机验证开发,获取资料可关注公众号「触觉智能」,回复关键词"客服"联系客服领取:瑞芯微RK开发板/核心板相关资料,可技术支持对接、项目选型与需求咨询。前20位申领资料,可获赠开发板大额购买折扣。机会有限,先到先得!
一、ADC基本概念
ADC(Analog-to-Digital Converter,模数转换器)将模拟电压信号转换为数字值,常用于:按键检测(ADC按键)、电池电压监测、温度传感器读取、模拟传感器接口。
参数 | 说明 | 影响 |
分辨率 | 输出位数(如 10bit、12bit) | 精度 |
采样率 | 每秒采样次数 | 速度 |
输入范围 | 可测量的电压范围 | 量程 |
精度 | 实际值与理想值的偏差 | 准确度 |
•10-bit分辨率
•多路输入通道
•输入电压范围:0~1.8V
•采样率可达1MSPS
二、ADC转换原理
数字值 = (Vin / Vref) × (2^n - 1)
其中:
- Vin: 输入电压
- Vref: 参考电压 (1.8V)
- n: 分辨率 (10bit)
Vin = (数字值 / 1023) × 1.8V三、ADC设备树配置
本板设备树入口文件为kernel-6.1/arch/arm64/boot/dts/rockchip/ido-evb7609-v1a-mipi.dts,包含关系如下:
ido-evb7609-v1a-mipi.dts
└── ido-evb7609-v1a.dtsi
├── rk3576.dtsi
├── rk3576-linux.dtsi
├── ido-som7608.dtsi
│ └── ido-evb7609-v1a-mipi.dtsi ← SARADC 使能处
└── 其他 dtsi...SARADC在ido-som7608.dtsi(设备树)中使能:
&saradc {
status = "okay";
vref-supply = <&vcca_1v8_s0>;
};ADC按键定义在ido-evb7609-v1a-mipi.dtsi中,使用SARADC通道1:
/ {
adc_keys: adc-keys {
compatible = "adc-keys";
io-channels = <&saradc 1>; /* 使用通道 1 */
io-channel-names = "buttons";
keyup-threshold-microvolt = <1800000>; /* 按键释放阈值 1.8V */
poll-interval = <100>; /* 轮询间隔 100ms */
vol-up-key {
label = "volume up";
linux,code = <KEY_VOLUMEUP>;
press-threshold-microvolt = <17000>; /* ~17mV */
};
vol-down-key {
label = "volume down";
linux,code = <KEY_VOLUMEDOWN>;
press-threshold-microvolt = <417000>; /* ~417mV */
};
menu-key {
label = "menu";
linux,code = <KEY_MENU>;
press-threshold-microvolt = <890000>; /* ~890mV */
};
back-key {
label = "back";
linux,code = <KEY_BACK>;
press-threshold-microvolt = <1235000>; /* ~1.235V */
};
};
};工作原理:多个按键通过电阻分压连接到同一个ADC通道,不同按键按下时产生不同的电压值。驱动通过比较检测电压与预设阈值来判断哪个按键被按下。
如果需要添加电池电压检测等ADC传感器,可在设备树中添加:
/ {
battery: battery {
compatible = "simple-battery";
voltage-monitored-battery {
compatible = "voltage-monitored-battery";
io-channels = <&saradc 2>; /* 使用通道 2 */
io-channel-names = "voltage";
/* OCV 容量曲线 */
ocv-capacity-celsius = <20>;
ocv-capacity-table-0 = <4200 100>, <4000 80>,
<3800 50>, <3600 20>,
<3400 5>, <3200 0>;
};
};
};四、ADC驱动开发
#include <linux/iio/consumer.h>
#include <linux/platform_device.h>
struct my_adc_dev {
struct iio_channel *chan;
struct device *dev;
};
/* 读取 ADC 电压值(单位:mV) */
static int read_adc_value(struct my_adc_dev *priv, int *val)
{
int ret;
int raw_val, scale_val;
/* 读取原始 ADC 值 */
ret = iio_read_channel_raw(priv->chan, &raw_val);
if (ret < 0) {
dev_err(priv->dev, "Failed to read ADC: %d\n", ret);
return ret;
}
/* 读取比例因子(单位:mV) */
ret = iio_read_channel_scale(priv->chan, &scale_val);
if (ret < 0) {
dev_err(priv->dev, "Failed to read scale: %d\n", ret);
return ret;
}
/* 计算实际电压:raw * scale */
*val = raw_val * scale_val;
return 0;
}
static int my_adc_probe(struct platform_device *pdev)
{
struct my_adc_dev *priv;
int val, ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = &pdev->dev;
/* 获取 IIO 通道(名称需与设备树 io-channel-names 匹配) */
priv->chan = devm_iio_channel_get(&pdev->dev, "voltage");
if (IS_ERR(priv->chan)) {
ret = PTR_ERR(priv->chan);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Failed to get IIO channel: %d\n", ret);
return ret;
}
/* 测试读取 */
ret = read_adc_value(priv, &val);
if (ret == 0)
dev_info(&pdev->dev, "ADC value: %d mV\n", val);
platform_set_drvdata(pdev, priv);
return 0;
}
static const struct of_device_id my_adc_of_match[] = {
{ .compatible = "mycompany,my-adc-device" },
{ }
};
static struct platform_driver my_adc_driver = {
.probe = my_adc_probe,
.driver = {
.name = "my-adc",
.of_match_table = my_adc_of_match,
},
};
module_platform_driver(my_adc_driver);
MODULE_LICENSE("GPL");#include <linux/io.h>
#define SARADC_CTRL 0x00
#define SARADC_DATA 0x04
#define SARADC_STAS 0x08
#define SARADC_EN BIT(0)
#define SARADC_START BIT(4)
struct saradc_regs {
void __iomem *base;
struct clk *clk;
};
static int saradc_read_channel(struct saradc_regs *regs, int channel)
{
u32 ctrl;
int timeout = 1000;
/* 使能 ADC */
writel(SARADC_EN, regs->base + SARADC_CTRL);
/* 配置通道并启动转换 */
ctrl = SARADC_EN | SARADC_START | (channel << 6);
writel(ctrl, regs->base + SARADC_CTRL);
/* 等待转换完成 */
while (!(readl(regs->base + SARADC_STAS) & BIT(0))) {
if (--timeout == 0)
return -ETIMEDOUT;
udelay(1);
}
/* 读取结果(10-bit) */
return readl(regs->base + SARADC_DATA) & 0x3ff;
}五、用户空间操作&调试方法
# 查看 ADC 设备
ls /sys/bus/iio/devices/
# 读取原始值(10-bit)
cat /sys/bus/iio/devices/iio:device0/in_voltage0_raw
# 读取比例因子
cat /sys/bus/iio/devices/iio:device0/in_voltage0_scale
# 计算实际电压
# V = raw * scale / 1000000#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define ADC_PATH "/sys/bus/iio/devices/iio:device0"
int read_adc_raw(int channel)
{
char path[256];
char buf[32];
int fd, val;
snprintf(path, sizeof(path), "%s/in_voltage%d_raw",
ADC_PATH, channel);
fd = open(path, O_RDONLY);
if (fd < 0) {
perror("Failed to open ADC");
return -1;
}
if (read(fd, buf, sizeof(buf)) < 0) {
perror("Failed to read ADC");
close(fd);
return -1;
}
close(fd);
return atoi(buf);
}
int read_adc_scale(void)
{
char path[256];
char buf[32];
int fd;
snprintf(path, sizeof(path), "%s/in_voltage_scale", ADC_PATH);
fd = open(path, O_RDONLY);
if (fd < 0)
return -1;
if (read(fd, buf, sizeof(buf)) < 0) {
close(fd);
return -1;
}
close(fd);
return (int)(atof(buf) * 1000000); /* 转换为微伏 */
}
int main(void)
{
int raw, scale, channel = 0;
float voltage;
printf("Reading ADC channel %d...\n", channel);
raw = read_adc_raw(channel);
if (raw < 0) {
printf("Failed to read ADC\n");
return -1;
}
scale = read_adc_scale();
if (scale < 0) {
printf("Failed to read scale\n");
return -1;
}
voltage = (float)raw * scale / 1000000.0;
printf("Raw value: %d\n", raw);
printf("Scale: %d uV/LSB\n", scale);
printf("Voltage: %.3f V\n", voltage);
return 0;
}# 查看 IIO 设备
ls /sys/bus/iio/devices/
cat /sys/bus/iio/devices/iio:device0/name
# 查看所有通道
ls /sys/bus/iio/devices/iio:device0/ | grep in_voltage
# 读取所有通道值
for i in 0 1 2 3; do
echo "Channel $i: $(cat /sys/bus/iio/devices/iio:device0/in_voltage${i}_raw)"
done# 查看按键事件
evtest /dev/input/event0
# 查看按键映射
cat /proc/bus/input/devices
# 单独读取 ADC 按键通道值
cat /sys/bus/iio/devices/iio:device0/in_voltage1_raw•[RKDocs/common/SARADC/](RKDocs/common/SARADC/)
•[Documentation/iio/](kernel-6.1/Documentation/iio/)
产品简介
