ADCLP使用参考¶
REVISION HISTORY¶
| Revision No. | Description |
Date |
|---|---|---|
| 1.0 | 01/24/2024 | |
| 1.1 | 07/24/2024 | |
| 1.2 | 03/10/2025 | |
| 1.3 | 11/27/2025 |
1. 概述¶
逐次逼近型模数转换器(Successive Approximation ADC)采用的是一种反馈比较型电路结构。实现方式简要概述为:取一个数字量加到DAC上,可得到一个对应的输出模拟电压,将这个模拟电压和输入的模拟电压信号相比较,如果两者不相等,则调整所取的数字量,直到两个模拟电压相等为止,最后所取的这个数字量就是所求的转换结果。
2. 关键字说明¶
-
ADCLP
Analog-to-digital converter Low Precision,低精度(10bit)模数转换器
-
ADCMP
Analog-to-digital converter Medium Precision,中精度(12bit)模数转换器
-
upper/lower bound
ADCLP可设置外部输入电压数字量的上下限,超出范围则触发中断
-
基准电压
用于模数转换计算时使用的参考电压,也是最大量程,若基准电压设定为1.8v,当外部输入电压>=1.8v时,数字量达到最大值1023
3. 功能描述¶
-
SAR ADCLP共计5个Channel可支持外部输入电压的模数转换
-
采样精度为10bit,因此获取到的寄存器数值范围在0~0x3ff之间
-
支持两个档位的基准电压(即满量程)切换,分别为1.8V和1.0V
-
支持外部输入电压的阈值设定
-
采样频率 = 12Mhz / 104 = 115384hz(相当于硬件上每点的采样间隔为8667ns)
4. 硬件连接介绍¶
如下图,可将外部电压接入引脚PAD_PM_SAR0-4

5. 计算说明¶
SAR ADCLP的主要功能是将模拟信号转换为相应的数字信号,即可以将输入电压转换为数字量存储于寄存器中,通过公式计算出输入电压,
计算公式:电压 = ( 寄存器数值 / 满量程 )* 基准电压
即如果读到的数值是0x1D2,可得电压为0x1D2/0x3FF *1.8=0.82v左右
6. Uboot用法介绍¶
6.1. Uboot Config配置¶
查看Kconfig可以看到支持ADCLP需要配置CONFIG_SSTAR_ADCLP、CONFIG_ADC、CONFIG_CMD_ADC,其中开启CONFIG_CMD_ADC需要先配置DM_REGULATOR
-
配置CONFIG_SSTAR_ADCLP

-
配置CONFIG_ADC

-
配置DM_REGULATOR

-
配置CONFIG_CMD_ADC

6.2. DTS配置¶
SAR ADCLP的DTS配置只需要在iford.dtsi中配置如下信息:
adclp: adclp {
compatible = "sstar,adclp";
reg = <0x1F002800 0x200>;
chan-num = <5>;
ref-voltage = <1800>;
status = "okay";
};
SAR ADCLP DTS配置说明:
| 属性 | 描述 | 设定值 | 备注 |
|---|---|---|---|
| compatible | 匹配驱动进行驱动注册 | "sstar,adclp" | 禁止修改 |
| reg | 设定寄存器bank地址 | <0x1F002800 0x200> | 禁止修改 |
| chan-num | 设定支持的通道数量 | 5 | 禁止修改 |
| ref-voltage | 设定基准电压档位 | 以mv为单位,可选1800和1000 | 可根据需要修改 |
| status | 选择是否使能驱动 | "okay" or "disable" | 可根据需要修改 |
6.3. Uboot cmd参数说明及使用实例¶
命令行输入adc:

①adc list → 可查看sar adclp是否有绑定

②adc info adclp → 可查看sar adclp支持的channel数量及data精度
如下图,目前支持的channel数量为5(注意区分mask为0x1f),最大data值为0x3ff

③adc single adclp [channel] → 可查看具体某个channel的data
如下图,channel 0的数值为605
根据公式:电压 = ( data / 0x3ff)* 基准电压
此时电压为605 / 1023 * 1.8 V = 1.065V

④adc scan adclp [channel mask] → 可查看多个channel的data
adc scan adclp :查看所有channel的data

adc scan adclp 0x3:查看channel 0和channel 1的data

7. Kernel用法介绍¶
7.1. Kernel Config配置¶
SAR ADCLP关联的驱动模块Config:
- CONFIG_IIO
SAR ADCLP 驱动使能开启的配置:
Device Drivers --->
[*] Sstar SoC platform drivers --->
<*> Sstar ADCLP driver
7.2. DTS配置¶
SAR ADCLP的DTS配置只需要在iford.dtsi中配置如下信息:
adclp0: adclp0@1f002800 {
compatible = "sstar,adclp";
interrupt-parent = <&sstar_pm_main_intc>;
interrupts = <INT_PMSLEEP_IRQ_SAR_KP>;
reg = <0x1F002800 0x200>;
clocks = <&CLK_sar>;
interrupt-enable;
channel = <0>;
ref-voltage = <1800>;
upper-bound = <0x3FF>;
lower-bound = <0>;
status = "ok";
};
| 属性 | 描述 | 设定值 | 备注 |
|---|---|---|---|
| compatible | 匹配驱动进行驱动注册 | "sstar,adclp" | 禁止修改 |
| interrupt-parent | 绑定中断控制器 | &sstar_pm_main_intc | 禁止修改 |
| interrupts | 绑定中断号 | INT_PMSLEEP_IRQ_SAR_KP | 禁止修改 |
| reg | 设定寄存器bank地址 | 0x1F002800 | 禁止修改 |
| clocks | 设定时钟源 | &CLK_sar | 禁止修改 |
| interrupt-enable | 使能中断 | bool类型,注释即disable | 可根据需要修改 |
| channel | 设定通道 | 0~4 | 禁止修改 |
| ref-voltage | 设定基准电压档位 | 以mv为单位,可选1800和1000 | 可根据需要修改 |
| upper-bound | 设定阈值上限电压 | 0~0x3FF,使能中断有效 | 可根据需要修改 |
| lower-bound | 设定阈值下限电压 | 0~0x3FF,使能中断有效 | 可根据需要修改 |
| status | 是否使能驱动 | "ok" or "disable" | 可根据需要修改 |
7.3. Padmux配置¶
SAR-ADCLP无需进行padmux的配置,PAD_PM_SAR0-4默认配置为ADC采样功能
7.4. 模块使用介绍¶
7.4.1. SYSFS接口¶
-
进入SAR ADCLP采样的通道Channel 0
cd /sys/class/sstar/adclp0/ -
设定SAR ADCLP0的阈值(不设定阈值也不影响采样)
echo 800 400 > /sys/class/sstar/adclp0/threshold -
使能SAR ADCLP0
echo 1 > enable -
获取SAR ADCLP0采样后的数字量
cat /sys/class/sstar/adclp0/value
7.4.2. IOCTL使用方法¶
头文件<drv_adclp.h>位于kernel/driver/sstar/sar目录下,
-
IOCTL_ADCLP_SET_BOUND
设定采样电压阈值
-
IOCTL_ADCLP_READ_VALUE
获取电压转换后的数字量
...
struct adclp_bound
{
unsigned short upper_bound;
unsigned short lower_bound;
};
#define ADCLP_IOC_MAXNR 2
#define ADCLP_IOC_MAGIC 'a'
#define IOCTL_ADCLP_SET_BOUND _IO(ADCLP_IOC_MAGIC, 0)
#define IOCTL_ADCLP_READ_VALUE _IO(ADCLP_IOC_MAGIC, 1)
...
Sample code
源码位于kernel/driver/sstar/sar/ut/ut_adclp.c
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <drv_adclp.h>
void sample_warn(int num)
{
printf("adclp data exceeding the threshold\n");
}
int main(int argc, char **argv)
{
int fd;
char cmd;
int flags;
unsigned short value;
unsigned int channel;
char path[64];
struct adclp_bound adclp_bd;
if (argc == 2)
{
channel = atoi(argv[1]);
}
else if (argc == 4)
{
channel = atoi(argv[1]);
adclp_bd.upper_bound = atoi(argv[2]);
adclp_bd.lower_bound = atoi(argv[3]);
}
else
{
printf("format: ut_adclp [channel] <upper> <lower>\n");
return -1;
}
snprintf(path, sizeof(path), "/dev/adclp%u", channel);
fd = open((const char *)(char *)path, O_RDWR);
if (fd < 0)
{
printf("open device fail\n");
return -1;
}
if (argc == 4)
{
ioctl(fd, IOCTL_ADCLP_SET_BOUND, &adclp_bd);
}
signal(SIGIO, sample_warn);
fcntl(fd, F_SETOWN, getpid());
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | FASYNC);
while (1)
{
cmd = getchar();
if (cmd == 'q' || cmd == 'Q')
{
break;
}
ioctl(fd, IOCTL_ADCLP_READ_VALUE, &value);
printf("adclp%u data[%hu]\n", channel, value);
}
close(fd);
return 0;
}
7.4.3. IIO SYSFS接口¶
由于adclp每个通道独立一个节点,因此在iio sysfs下也是如此分布:

以ii0:device0举例:

cat in_voltage_raw:获取原始的adc code值
cat in_voltage_scale:获取系数

此时电压为714 * 3.225806451 = 2303 mv
7.4.4. ADC KEY的使用方法¶
7.4.4.1 CONFIG配置¶
CONFIG_SSTAR_ADCLP=Y
CONFIG_IIO=Y
CONFIG_INPUT=Y
CONFIG_INPUT_EVDEV=Y
CONFIG_INPUT_KEYBOARD=Y
CONFIG_KEYBOARD_ADC=Y
7.4.4.2 DTS配置¶
#include "../../../../drivers/sstar/include/iford/irqs.h"
#include "../../../../drivers/sstar/include/iford/gpio.h"
#include <dt-bindings/interrupt-controller/irq.h>
#include "../../../../include/uapi/linux/input-event-codes.h" //一般`linux,code`使用input event code,包含此头文件才可通过编译
#include <dt-bindings/interrupt-controller/arm-gic.h>
adc-keys0 {
compatible = "adc-keys";
io-channels = <&adclp0 0>; //adclp0对应10bit adc的通道0,第二个参数默认用0
io-channel-names = "buttons";
poll-interval = <900>; //单位ms
keyup-threshold-microvolt = <1800000>; //按键抬起的电压阈值
esc-key {
linux,code = <KEY_MICMUTE>;
label = "micmute";
press-threshold-microvolt = <1200000>; //当adc电压为1200mv时,上报KEY_MICMUTE按下事件
};
home-key {
linux,code = <KEY_MODE>;
label = "mode";
press-threshold-microvolt = <1880000>;
};
menu-key {
linux,code = <KEY_PLAY>;
label = "play";
press-threshold-microvolt = <1650000>;
};
vol-down-key {
linux,code = <KEY_VOLUMEDOWN>;
label = "volume down";
press-threshold-microvolt = <1320000>;
};
vol-up-key {
linux,code = <KEY_VOLUMEUP>;
label = "volume up";
press-threshold-microvolt = <825000>;
};
};
adc-keys1 {
compatible = "adc-keys";
io-channels = <&adclp1 0>; //adclp1对应10bit adc的通道1,第二个参数默认用0
io-channel-names = "buttons";
poll-interval = <1000>;
keyup-threshold-microvolt = <3300000>;
esc-key {
linux,code = <KEY_MICMUTE>;
label = "micmute";
press-threshold-microvolt = <2060000>;
};
home-key {
linux,code = <KEY_MODE>;
label = "mode";
press-threshold-microvolt = <1880000>;
};
menu-key {
linux,code = <KEY_PLAY>;
label = "play";
press-threshold-microvolt = <1650000>;
};
vol-down-key {
linux,code = <KEY_VOLUMEDOWN>;
label = "volume down";
press-threshold-microvolt = <1320000>;
};
vol-up-key {
linux,code = <KEY_VOLUMEUP>;
label = "volume up";
press-threshold-microvolt = <825000>;
};
};
7.4.4.3 验证adc key按键事件¶
按照上述dts配置,adc-key0绑定adclp通道0,从启动Log中可以看出注册为input0:

因此,hexdump /dev/input/event0,同时调节电压,可得如下信息:
第6列是事件类型,1表示EV_KEY,0表示EV_SYN
第7列是按键键值,0x175表示KEY_MODE
第8列是按键状态,1表示按下,0表示抬起

8. 内核态API使用方法¶
头文件<drv_adclp.h>位于kernel/driver/sstar/sar目录下,
...
typedef int (*adclp_cb_t)(u8 channel);
int sstar_adclp_enable(u8 channel, u8 enable);
int sstar_adclp_get_data(u8 channel, u16 *data);
int sstar_adclp_set_bound(u8 channel, u16 max, u16 min);
int sstar_adclp_register_callback(u8 channel, adclp_cb_t cb_t);
int sstar_adclp_unregister_callback(u8 channel, adclp_cb_t cb_t);
...
sstar_adclp_enable
-
目的
使能指定通道的采样功能
-
语法
int sstar_adclp_enable(u8 channel, u8 enable); -
参数
参数名称 描述 channel 采样通道 enable 使能与否 -
返回值
返回值 描述 0 成功 EINVAL 通道不支持
sstar_adclp_get_data
-
目的
获取指定通道的外部输入电压数字量
-
语法
int sstar_adclp_get_data(u8 channel, u16 *data) -
参数
参数名称 描述 channel 采样通道 data 获取采样结果的指针地址 -
返回值
返回值 描述 0 成功 EINVAL 通道不支持
sstar_adclp_set_bound
-
目的
设定指定通道的阈值
-
语法
int sstar_adclp_set_bound(u8 channel, u16 max, u16 min) -
参数
参数名称 描述 channel 采样通道 max 阈值上限 min 阈值下限 -
返回值
返回值 描述 0 成功 EINVAL 通道不支持
sstar_adclp_register_callback
-
目的
注册指定通道的回调函数(同一通道支持注册多个回调函数),当采样结果超出阈值后可在回调函数里作出相应的处理
-
语法
int sstar_adclp_register_callback(u8 channel, adclp_cb_t cb_t) -
参数
参数名称 描述 channel 采样通道 cb_t 函数指针 -
返回值
返回值 描述 0 注册成功 EINVAL 注册失败
sstar_adclp_unregister_callback
-
目的
释放指定通道的回调函数和注册时申请的内存
-
语法
int sstar_adclp_unregister_callback(u8 channel, adclp_cb_t cb_t) -
参数
参数名称 描述 channel 采样通道 cb_t 函数指针 -
返回值
返回值 描述 0 取消注册成功 EINVAL 取消注册失败
9. FAQ¶
Q1:SAR ADCLP接口不存在
-
检查DTS ADCLP节点的
status是否为ok -
检查kernel config是否配置,详见[7.1. Kernel Config配置]
Q2:外部输入电压变化,SAR ADCLP采样数据没有变化
-
当PIN脚处于GPIO MODE时,采样数据不会变化,可读取寄存器的数值判断PIN脚是否被切为GPIO MODE:
0x14 0x11 BIT0-BIT5: BIT0~BIT5的每个BIT对应一个通道,当value=0时,该通道PIN脚处于GPIO MODE,当value=1时,该通道PIN脚处于ADC MODE,比如BIT0=0时,通道0处于GPIO MODE,BIT1=1时,通道1处于ADC MODE 0x14 0x11 BIT8-BIT13: BIT8~BIT13的每个BIT对应一个通道,前提条件是PIN脚已处于GPIO MODE,当value=0时,PIN脚切换为output,当value=1时,PIN脚切换为input 0x14 0x12 BIT0-BIT5: BIT0~BIT5的每个BIT对应一个通道,前提条件是PIN脚已处于GPIO MODE并且设定为ouput,当value=0时,该通道PIN脚切换为低电平,当value=1时,该通道PIN脚切换为高电平 例如:riu_r 0x14 0x11 返回值:0x3F3F -> 全部通道为ADC MODE 返回值:0x3E3E -> 通道0为GPIO MODE,其余通道为ADC MODE 返回值:0x0000 -> 全部通道为GPIO MODE且切换为output -
当PIN脚寄存器设定非GPIO MODE时,采样数据仍然没有变化,可将PIN脚设为GPIO MODE并进行output high/low的试验,如果PIN脚电平无法拉高拉低则可判断为硬件问题
Q3:第一次或者前几次采样数据与实际输入电压偏差大
此问题大概率与采样时机有关,可通过操作GPIO作为触发源,获取每次采样时元件的电压情况,
如下图所示,触发ADC采样前,GPIO从高电平切换为低电平,ADC采样时机刚好处于电压的下降过程,并不是在电压稳定时去采样,因此被判定为采样异常,此时需要等元件稳定时再去采样
