RK3308在linux内核的ko驱动模块里面通过IIO读取ADS1115 ADC的数据
在linux内核中的ko驱动代码里面,可以用传统I2C的方式配置ADS1115,然后读ADS1115的寄存器,得到各个ADC通道的数据,但是代码量比较大。
还有一种更简单的方法,就是在设备树里面把I2C下的ADS1115配置成IIO模式(Industrial I/O),然后把驱动程序由i2c_driver改成普通的platform_driver,直接用linux内核提供的iio_read_channel_raw函数去读ADC通道的值,这样一来代码就非常简单了。
先来看看设备树。在笔者的板子上,ADS1115是挂在PCA9546 4-Channel I2C Multiplexer下方的,所以i2c0节点下面多了i2c-switch@70和i2c@2这两层节点。正常情况下是没有这两层节点的,i2c0下面直接就是ads1115_bottom节点。
ads1115_bottom: adc@48这个节点表示一个ADS1115 I2C芯片,ads1115_bottom这个名字是随便起的,@48表示ADS1115的7位I2C地址是0x48。
ads1115_bottom的compatible必须改成"ti,ads1115",表示要用ADS1115的IIO驱动。只要内核make menuconfig里面勾选了 * Device Drivers ---> <*> Industrial I/O support ---> Analog to digital converters ---> <*> Texas Instruments ADS1015 ADC选项,linux内核里面就可以用ti,ads1115这个驱动,该驱动的设备树配置说明文档是Luckfox_Nova_SDK_250430/kernel/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml,里面写得很详细。
reg = <0x48>表示I2C地址,和@后面的值保持一致。
#address-cells = <1>这个必须为1。
#size-cells = <0>这个必须为0。
#io-channel-cells = <1>这个要么写0,要么写1。如果ADC芯片只有一个ADC通道就写0,如果有多个通道就写1。ADS1115这款ADC芯片有多个ADC通道,所以要写1。注意这里说的是ADC芯片本身有没有多个通道,不是说是否实际使用了多个通道。
里面的channel@4,channel@5子节点是具体的通道配置,可以写出来,也可以省略不写。省略不写就是采用默认的±2.048V 128SPS的配置。
channel子节点里面有reg,ti,gain和ti,datarate这三个属性,可以看看ti,ads1015.yaml文档里面的介绍。reg和@后面的数字保持一致,ti,gain是增益,ti,datarate是数据输出速率。看看ads1115.pdf芯片手册第25-26页的表 8-3. Config Register Field Descriptions。
Input multiplexer configuration (ADS1115 only)
These bits configure the input multiplexer.
These bits serve no function on the ADS1113 and ADS1114. ADS1113 and
ADS1114 always use inputs AINP = AIN0 and AINN = AIN1.
000b : AINP = AIN0 and AINN = AIN1 (default)
001b : AINP = AIN0 and AINN = AIN3
010b : AINP = AIN1 and AINN = AIN3
011b : AINP = AIN2 and AINN = AIN3
100b : AINP = AIN0 and AINN = GND
101b : AINP = AIN1 and AINN = GND
110b : AINP = AIN2 and AINN = GND
111b : AINP = AIN3 and AINN = GND
这个是通道号,也就是channel@和reg里面的数字,4是AIN0,5是AIN1,6是AIN2,7是AIN3。前面的0-3都是差分通道。
Programmable gain amplifier configuration
These bits set the FSR of the programmable gain amplifier.
These bits serve no function on the ADS1113. ADS1113 always uses FSR =
±2.048V.
000b : FSR = ±6.144V(1)
001b : FSR = ±4.096V(1)
010b : FSR = ±2.048V (default)
011b : FSR = ±1.024V
100b : FSR = ±0.512V
101b : FSR = ±0.256V
110b : FSR = ±0.256V
111b : FSR = ±0.256V
这个是增益,2对应的是±2.048V。
Data rate
These bits control the data rate setting.
000b : 8SPS
001b : 16SPS
010b : 32SPS
011b : 64SPS
100b : 128SPS (default)
101b : 250SPS
110b : 475SPS
111b : 860SPS
这个是数据输出速率,4表示128SPS。
好了,i2c设备节点ads1115_bottom的compatible已经改成"ti,ads1115"了,那我们自己的myiiotest驱动该怎么办呢?那就在根节点下再建立一个myiiotestnode节点,里面的compatible = "testboard,myiiotest"绑定自己的驱动。记得要把自己的驱动由i2c_driver改成普通的platform_driver。
用io-channels属性声明一下我们自己的驱动要用到哪些IIO通道。#io-channel-cells为<1>时io-channels的<>里面才需要带数字参数(通道号),#io-channel-cells为<0>时ADC芯片只有一个ADC通道,io-channels的<>里面不需要带数字参数(不需要写通道号)。
io-channels = <&ads1115_bottom 4>, <&ads1115_bottom 5>, <&ads1115_bottom 6>, <&ads1115_bottom 7>;
用io-channel-names属性声明一下每个通道的名称,这个属性是可选的,可以写,也可以不写。
io-channel-names = "sensor_21", "sensor_22", "sensor_23", "sensor_24"; /* 指定每个通道的名称(这个属性是可选的) */
/*
* ./build.sh kernel-config里面需要勾选:
* Device Drivers --->
<*> Industrial I/O support --->
Analog to digital converters --->
<*> Texas Instruments ADS1015 ADC
*
* 参考文档:Luckfox_Nova_SDK_250430/kernel/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml
*/
&i2c0 {
status = "okay";
i2c-switch@70 {
compatible = "nxp,pca9546";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x70>;
i2c@2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;
ads1115_bottom: adc@48 {
compatible = "ti,ads1115";
reg = <0x48>;
#address-cells = <1>;
#size-cells = <0>;
#io-channel-cells = <1>; /* 0表示该ADC芯片只有一个测量端口,1表示有多个测量端口 */
/* 这决定了io-channels的每个<>里面是否带数字参数 */
channel@4 {
reg = <4>; /* AIN0 */
ti,gain = <2>; /* ±2.048V */
ti,datarate = <4>; /* 128SPS */
};
};
};
};
};
/* 参考文档:Luckfox_Nova_SDK_250430/kernel/Documentation/devicetree/bindings/iio/iio-bindings.txt */
/ {
myiiotestnode {
compatible = "testboard,myiiotest";
io-channels = <&ads1115_bottom 4>, <&ads1115_bottom 5>, <&ads1115_bottom 6>, <&ads1115_bottom 7>;
io-channel-names = "sensor_21", "sensor_22", "sensor_23", "sensor_24"; /* 指定每个通道的名称(这个属性是可选的) */
};
};
如果写了io-channel-names属性,那么在自己的驱动程序里面可以用iio_channel_get(&pdev->dev, "名称")来获取通道。
如果没写io-channel-names属性,那就先用channels = iio_channel_get_all(&pdev->dev)获取通道数组,然后用channels[下标]来获取指定通道。数组的长度是“通道总数+1”=5,最后一个下标4是空元素,channels[4].indio_dev为NULL。
获取通道后用iio_read_channel_raw读取通道的ADC值。
最后用完记得用iio_channel_release或iio_channel_release_all函数释放通道。
iio_channel_get传入的是通道名称的字符串,获取到的是单个通道,用iio_channel_release函数释放。
iio_channel_get_all函数获取到的是所有通道的数组,所以要用iio_channel_release_all函数释放。
#include <linux/iio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
static int myiiotest_probe(struct platform_device *pdev)
{
const char *str;
char name[10];
int i, ret, value;
struct iio_channel *channel;
struct iio_channel *channels;
dev_info(&pdev->dev, "probe\n");
for (i = 21; i <= 26; i++)
{
snprintf(name, sizeof(name), "sensor_%d", i);
channel = iio_channel_get(&pdev->dev, name);
if (IS_ERR(channel)) // 失败时返回含错误码的指针
{
dev_err(&pdev->dev, "failed to get channel %s, ret=%ld", name, PTR_ERR(channel));
continue;
}
ret = iio_read_channel_raw(channel, &value);
if (ret == IIO_VAL_INT)
dev_info(&pdev->dev, "channel %s: %d\n", name, value);
else
dev_err(&pdev->dev, "failed to read %s, ret=%d\n", name, ret);
iio_channel_release(channel); // 用完记得释放
}
channels = iio_channel_get_all(&pdev->dev);
if (IS_ERR(channels)) // 失败时返回含错误码的指针
{
dev_err(&pdev->dev, "failed to get the iio channels\n");
return PTR_ERR(channels);
}
for (i = 0; channels[i].indio_dev != NULL; i++)
{
ret = of_property_read_string_index(pdev->dev.of_node, "io-channel-names", i, &str);
if (ret != 0)
str = "";
ret = iio_read_channel_raw(&channels[i], &value);
if (ret == IIO_VAL_INT)
dev_info(&pdev->dev, "io-channels[%d]: name=%s, value=%d\n", i, str, value);
else
dev_err(&pdev->dev, "failed to read io-channels[%d], ret=%d\n", i, ret);
}
iio_channel_release_all(channels); // 用完记得释放
return 0;
}
static int myiiotest_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "remove\n");
return 0;
}
static const struct of_device_id myiiotest_match_ids[] = {
{.compatible = "testboard,myiiotest"},
{}
};
MODULE_DEVICE_TABLE(of, myiiotest_match_ids);
static struct platform_driver myiiotest_driver = {
.driver = {
.name = "myiiotest",
.of_match_table = myiiotest_match_ids
},
.probe = myiiotest_probe,
.remove = myiiotest_remove
};
module_platform_driver(myiiotest_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Oct1158");
程序运行结果:
root@rk3308b-buildroot:/root# insmod myiiotest.ko
[ 35.358130] myiiotest myiiotestnode: probe
[ 35.511460] myiiotest myiiotestnode: channel sensor_21: 0
[ 35.536039] myiiotest myiiotestnode: channel sensor_22: 32767
[ 35.689858] myiiotest myiiotestnode: channel sensor_23: 3169
[ 35.972504] myiiotest myiiotestnode: channel sensor_24: 3167
[ 35.978290] myiiotest myiiotestnode: failed to get channel sensor_25, ret=-19
[ 35.985518] myiiotest myiiotestnode: failed to get channel sensor_26, ret=-19
[ 36.140859] myiiotest myiiotestnode: io-channels[0]: name=sensor_21, value=-1
[ 36.167081] myiiotest myiiotestnode: io-channels[1]: name=sensor_22, value=32767
[ 36.322516] myiiotest myiiotestnode: io-channels[2]: name=sensor_23, value=3168
[ 36.606750] myiiotest myiiotestnode: io-channels[3]: name=sensor_24, value=3167
root@rk3308b-buildroot:/root# rmmod myiiotest.ko
[ 39.192373] myiiotest myiiotestnode: remove
root@rk3308b-buildroot:/root#
如果觉得手动释放ADC通道很麻烦,不想手动释放的话,可以调用devm_iio_channel_get或者devm_iio_channel_get_all函数,就不用release函数手动释放了。linux内核会在platform_device remove的时候自动释放。
static int myiiotest_probe(struct platform_device *pdev)
{
const char *str;
char name[10];
int i, ret, value;
struct iio_channel *channel;
struct iio_channel *channels;
dev_info(&pdev->dev, "probe\n");
for (i = 21; i <= 26; i++)
{
snprintf(name, sizeof(name), "sensor_%d", i);
channel = devm_iio_channel_get(&pdev->dev, name);
if (IS_ERR(channel)) // 失败时返回含错误码的指针
{
dev_err(&pdev->dev, "failed to get channel %s, ret=%ld", name, PTR_ERR(channel));
continue;
}
ret = iio_read_channel_raw(channel, &value);
if (ret == IIO_VAL_INT)
dev_info(&pdev->dev, "channel %s: %d\n", name, value);
else
dev_err(&pdev->dev, "failed to read %s, ret=%d\n", name, ret);
// 不用手动释放
}
channels = devm_iio_channel_get_all(&pdev->dev);
if (IS_ERR(channels)) // 失败时返回含错误码的指针
{
dev_err(&pdev->dev, "failed to get the iio channels\n");
return PTR_ERR(channels);
}
for (i = 0; channels[i].indio_dev != NULL; i++)
{
ret = of_property_read_string_index(pdev->dev.of_node, "io-channel-names", i, &str);
if (ret != 0)
str = "";
ret = iio_read_channel_raw(&channels[i], &value);
if (ret == IIO_VAL_INT)
dev_info(&pdev->dev, "io-channels[%d]: name=%s, value=%d\n", i, str, value);
else
dev_err(&pdev->dev, "failed to read io-channels[%d], ret=%d\n", i, ret);
}
// 不用手动释放
return 0;
}
在iio:device文件夹中查看8个ADC通道的测量值
列出开发板上的所有IIO设备:
root@rk3308b-buildroot:/root# ls -l /sys/bus/iio/devices
total 0
lrwxrwxrwx 1 root root 0 Jan 1 1970 iio:device0 -> ../../../devices/platform/ff1e0000.saradc/iio:device0
lrwxrwxrwx 1 root root 0 Jan 1 1970 iio:device1 -> ../../../devices/platform/ff040000.i2c/i2c-0/i2c-6/6-0048/iio:device1
root@rk3308b-buildroot:/root#
列出iio:device1文件夹里面的所有文件:
root@rk3308b-buildroot:/root# ls /sys/bus/iio/devices/iio:device1
buffer
current_timestamp_clock
dev
events
in_voltage0-voltage1_raw
in_voltage0-voltage1_sampling_frequency
in_voltage0-voltage1_scale
in_voltage0-voltage3_raw
in_voltage0-voltage3_sampling_frequency
in_voltage0-voltage3_scale
in_voltage0_raw
in_voltage0_sampling_frequency
in_voltage0_scale
in_voltage1-voltage3_raw
in_voltage1-voltage3_sampling_frequency
in_voltage1-voltage3_scale
in_voltage1_raw
in_voltage1_sampling_frequency
in_voltage1_scale
in_voltage2-voltage3_raw
in_voltage2-voltage3_sampling_frequency
in_voltage2-voltage3_scale
in_voltage2_raw
in_voltage2_sampling_frequency
in_voltage2_scale
in_voltage3_raw
in_voltage3_sampling_frequency
in_voltage3_scale
name
of_node
power
sampling_frequency_available
scale_available
scan_elements
subsystem
trigger
uevent
root@rk3308b-buildroot:/root#
查看设备驱动名称:
root@rk3308b-buildroot:/root# cat /sys/bus/iio/devices/iio:device1/name
ads1015
root@rk3308b-buildroot:/root#
查看设备树路径:
root@rk3308b-buildroot:/root# ls -l /sys/bus/iio/devices/iio:device1/of_node
lrwxrwxrwx 1 root root 0 Apr 23 01:01 /sys/bus/iio/devices/iio:device1/of_node -> ../../../../../../../firmware/devicetree/base/i2c@ff040000/i2c-switch@70/i2c@2/adc@48
root@rk3308b-buildroot:/root#
查看ADS1115 ADC芯片8个通道的测量值,其中前4个通道是差分通道:
cat /sys/bus/iio/devices/iio:device1/in_voltage0-voltage1_raw
cat /sys/bus/iio/devices/iio:device1/in_voltage0-voltage3_raw
cat /sys/bus/iio/devices/iio:device1/in_voltage1-voltage3_raw
cat /sys/bus/iio/devices/iio:device1/in_voltage2-voltage3_raw
cat /sys/bus/iio/devices/iio:device1/in_voltage0_raw
cat /sys/bus/iio/devices/iio:device1/in_voltage1_raw
cat /sys/bus/iio/devices/iio:device1/in_voltage2_raw
cat /sys/bus/iio/devices/iio:device1/in_voltage3_raw
root@rk3308b-buildroot:/root# cat /sys/bus/iio/devices/iio:device1/in_voltage0-voltage1_raw
-17492
root@rk3308b-buildroot:/root# cat /sys/bus/iio/devices/iio:device1/in_voltage0-voltage3_raw
-3146
root@rk3308b-buildroot:/root# cat /sys/bus/iio/devices/iio:device1/in_voltage1-voltage3_raw
14100
root@rk3308b-buildroot:/root# cat /sys/bus/iio/devices/iio:device1/in_voltage2-voltage3_raw
0
root@rk3308b-buildroot:/root# cat /sys/bus/iio/devices/iio:device1/in_voltage0_raw
0
root@rk3308b-buildroot:/root# cat /sys/bus/iio/devices/iio:device1/in_voltage1_raw
32767
root@rk3308b-buildroot:/root# cat /sys/bus/iio/devices/iio:device1/in_voltage2_raw
3146
root@rk3308b-buildroot:/root# cat /sys/bus/iio/devices/iio:device1/in_voltage3_raw
3145
root@rk3308b-buildroot:/root#
另外,还可以通过i2c找到对应的iio设备文件夹。先打开ADS1115 I2C设备所在的文件夹(/sys/bus/i2c/drivers/I2C驱动名称/I2C总线号-I2C设备地址),里面就有一个iio:device1文件夹,除此之外还有I2C的设备名(name)。
root@rk3308b-buildroot:/# ls /sys/bus/i2c/drivers/ads1015
6-0048 bind uevent unbind
root@rk3308b-buildroot:/# ls /sys/bus/i2c/drivers/ads1015/6-0048
driver iio:device1 modalias name of_node power subsystem uevent
root@rk3308b-buildroot:/# ls -l /sys/bus/i2c/drivers/ads1015/6-0048/driver
lrwxrwxrwx 1 root root 0 Apr 22 11:06 /sys/bus/i2c/drivers/ads1015/6-0048/driver -> ../../../../../../bus/i2c/drivers/ads1015
root@rk3308b-buildroot:/# cat /sys/bus/i2c/drivers/ads1015/6-0048/name
ads1115
root@rk3308b-buildroot:/# ls -l /sys/bus/i2c/drivers/ads1015/6-0048/of_node
lrwxrwxrwx 1 root root 0 Apr 22 11:06 /sys/bus/i2c/drivers/ads1015/6-0048/of_node -> ../../../../../../firmware/devicetree/base/i2c@ff040000/i2c-switch@70/i2c@2/adc@48
root@rk3308b-buildroot:/#
列出iio:device1文件夹里面的文件:
root@rk3308b-buildroot:/# ls /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1
buffer
current_timestamp_clock
dev
events
in_voltage0-voltage1_raw
in_voltage0-voltage1_sampling_frequency
in_voltage0-voltage1_scale
in_voltage0-voltage3_raw
in_voltage0-voltage3_sampling_frequency
in_voltage0-voltage3_scale
in_voltage0_raw
in_voltage0_sampling_frequency
in_voltage0_scale
in_voltage1-voltage3_raw
in_voltage1-voltage3_sampling_frequency
in_voltage1-voltage3_scale
in_voltage1_raw
in_voltage1_sampling_frequency
in_voltage1_scale
in_voltage2-voltage3_raw
in_voltage2-voltage3_sampling_frequency
in_voltage2-voltage3_scale
in_voltage2_raw
in_voltage2_sampling_frequency
in_voltage2_scale
in_voltage3_raw
in_voltage3_sampling_frequency
in_voltage3_scale
name
of_node
power
sampling_frequency_available
scale_available
scan_elements
subsystem
trigger
uevent
root@rk3308b-buildroot:/#
查看IIO设备驱动名称和ADS1115 8个ADC通道的测量值:
cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1/name
cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1/in_voltage0-voltage1_raw
cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1/in_voltage0-voltage3_raw
cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1/in_voltage1-voltage3_raw
cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1/in_voltage2-voltage3_raw
cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1/in_voltage0_raw
cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1/in_voltage1_raw
cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1/in_voltage2_raw
cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1/in_voltage3_raw
命令太长了,串口控制台显示不下。
1/namek3308b-buildroot:/root# cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1
ads1015
1/in_voltage0-voltage1_rawot# cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1
-17538
1/in_voltage0-voltage3_rawot# cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1
-3111
1/in_voltage1-voltage3_rawot# cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1
14171
1/in_voltage2-voltage3_rawot# cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1
0
1/in_voltage0_rawdroot:/root# cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1
-1
1/in_voltage1_rawdroot:/root# cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1
32767
1/in_voltage2_rawdroot:/root# cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1
3111
1/in_voltage3_rawdroot:/root# cat /sys/bus/i2c/drivers/ads1015/6-0048/iio:device1
3111
root@rk3308b-buildroot:/root#
通过IIO接收ADS1115的中断信号
在设备树中定义中断引脚:
ads1115_bottom: adc@48 {
compatible = "ti,ads1115";
reg = <0x48>;
#address-cells = <1>;
#size-cells = <0>;
#io-channel-cells = <1>;
// 将ADS1115的ALERT/RDY中断引脚连接到开发板的GPIO0_B6引脚上
interrupt-parent = <&gpio0>;
interrupts = <RK_PB6 IRQ_TYPE_LEVEL_LOW>;
channel@4 {
reg = <4>;
ti,gain = <1>; // 将通道4(AIN0)的量程设置为±4.096V
ti,datarate = <4>;
};
};
当量程为±4.096V时,in_voltage0_raw的每一刻度表示125μV。
cat /sys/bus/iio/devices/iio:device1/in_voltage0_raw
输出2352,表示电压为2352×0.000125V=0.294V。
输出25024,表示电压为25024×0.000125V=3.128V。
输出25040,表示电压为25040×0.000125V=3.13V。
开启通道0的中断,当采样值高于20000或者低于100时触发中断:
echo 20000 > /sys/bus/iio/devices/iio:device1/events/in_voltage0_thresh_rising_value
echo 100 > /sys/bus/iio/devices/iio:device1/events/in_voltage0_thresh_falling_value
echo 1 > /sys/bus/iio/devices/iio:device1/events/in_voltage0_thresh_either_en
用于监测中断是否触发的用户态C程序:
#include <fcntl.h>
#include <linux/iio/events.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
int main(void)
{
int fd, event_fd, ret;
struct iio_event_data event;
fd = open("/dev/iio:device1", O_RDWR);
if (fd == -1)
{
perror("open() failed");
return -1;
}
ret = ioctl(fd, IIO_GET_EVENT_FD_IOCTL, &event_fd);
if (ret == -1)
{
perror("ioctl(IIO_GET_EVENT_FD_IOCTL) failed");
close(fd);
return -1;
}
ret = read(event_fd, &event, sizeof(event));
if (ret == sizeof(event))
{
printf("Channel: %d\n", IIO_EVENT_CODE_EXTRACT_CHAN(event.id));
printf("Channel type: %d\n", IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event.id)); // IIO_VOLTAGE=0
printf("Modifier: %d\n", IIO_EVENT_CODE_EXTRACT_MODIFIER(event.id)); // IIO_NO_MOD=0
printf("Event type: %d\n", IIO_EVENT_CODE_EXTRACT_TYPE(event.id)); // IIO_EV_TYPE_THRESH=0
printf("Event direction: %d\n", IIO_EVENT_CODE_EXTRACT_DIR(event.id)); // IIO_EV_DIR_EITHER=0
printf("Timestamp: %llu\n", event.timestamp);
}
else
printf("read() failed: ret=%d\n", ret);
close(event_fd);
close(fd);
return 0;
}
程序运行结果:
没有中断触发时,程序一直阻塞,控制台无打印。
当采样值高于20000或者低于100时,触发中断,程序打印中断信息并退出。
root@rk3308b-buildroot:/root# ./iio_event_test
Channel: 4
Channel type: 0
Modifier: 0
Event type: 0
Event direction: 0
Timestamp: 1776946327180679968
root@rk3308b-buildroot:/root#
在ADS1115的Config Register寄存器中:
bit11:9 PGA[2:0](电压量程)由设备树的ti,gain属性配置。
bit8 MODE由Runtime PM管理,suspend时为1(Single-shot mode or power-down state),resume时为0(Continuous-conversion mode)。
bit7:5 DR[2:0](数据速率)由设备树的ti,datarate属性配置。
bit4 COMP_MODE由in_voltage0_thresh_either_en和in_voltage0_thresh_rising_en文件内容决定。
当echo 1 > in_voltage0_thresh_rising_en时,COMP_MODE=0(Traditional comparator)。
当echo 1 > in_voltage0_thresh_either_en时,COMP_MODE=1(Window comparator)。
bit3 COMP_POL由设备树的interrupts属性的电平设置决定。
配置为IRQ_TYPE_LEVEL_LOW时,COMP_POL=0(Active low)。
配置为IRQ_TYPE_LEVEL_HIGH时,COMP_POL=1(Active high)。
bit2 COMP_LAT恒为1(Latching comparator)。
bit1:0 COMP_QUE[1:0]由in_voltage0_thresh_either_period文件内容决定。
当设备树里面ti,datarate = <4>时,data->data_rate[dr]=128。
cat /sys/bus/iio/devices/iio:device1/events/in_voltage0_thresh_either_period
输出0.007812。
这是因为COMP_QUE=0(Assert after one conversion)。
1÷128=0.0078125
2÷128=0.015625
echo 0.015625 > /sys/bus/iio/devices/iio:device1/events/in_voltage0_thresh_either_period
cat /sys/bus/iio/devices/iio:device1/events/in_voltage0_thresh_either_period
输出0.015625。
这就把COMP_QUE设为了1(Assert after two conversions)。
4÷128=0.03125
echo 0.03125 > /sys/bus/iio/devices/iio:device1/events/in_voltage0_thresh_either_period
cat /sys/bus/iio/devices/iio:device1/events/in_voltage0_thresh_either_period
输出0.031250。
这就把COMP_QUE设为了2(Assert after four conversions)。
使用内核自带的iio-hwmon驱动,在hwmon里面查看ADC通道的测量值
设备树:
/*
* ./build.sh kernel-config里面需要勾选:
* Device Drivers --->
<*> Hardware Monitoring support --->
<*> Hwmon driver that uses channels specified via iio maps
*/
/ {
my-test-iio-hwmon {
compatible = "iio-hwmon";
io-channels = <&ads1115_bottom 4>, <&ads1115_bottom 5>, <&ads1115_bottom 6>;
};
my-test-iio-hwmon2 {
compatible = "iio-hwmon";
io-channels = <&ads1115_bottom 6>, <&ads1115_bottom 7>;
};
};
iio-hwmon驱动是内核自带的,所以板子开机后不需要用insmod命令插入任何驱动模块,可以直接使用hwmon功能。
root@rk3308b-buildroot:/# ls /sys/class/hwmon
hwmon0 hwmon1 hwmon2 hwmon3
root@rk3308b-buildroot:/# cat /sys/class/hwmon/hwmon0/name
soc_thermal
root@rk3308b-buildroot:/# cat /sys/class/hwmon/hwmon1/name
gpu_thermal
root@rk3308b-buildroot:/# cat /sys/class/hwmon/hwmon2/name
my_test_iio_hwmon
root@rk3308b-buildroot:/# cat /sys/class/hwmon/hwmon3/name
my_test_iio_hwmon2
root@rk3308b-buildroot:/#
可以看到hwmon2和hwmon3是刚才在设备树里面定义的两个iio-hwmon设备。
my-test-iio-hwmon里面定义了3个ADC通道:通道4、通道5和通道6,分别用in1_input、in2_input和in3_input查看其测量值。
root@rk3308b-buildroot:/# ls /sys/class/hwmon/hwmon2
device in2_input name power uevent
in1_input in3_input of_node subsystem
root@rk3308b-buildroot:/# cat /sys/class/hwmon/hwmon2/in1_input
0
root@rk3308b-buildroot:/# cat /sys/class/hwmon/hwmon2/in2_input
2047
root@rk3308b-buildroot:/# cat /sys/class/hwmon/hwmon2/in3_input
583
root@rk3308b-buildroot:/#
my-test-iio-hwmon2里面定义了两个ADC通道:通道6和通道7,分别用in1_input和in2_input查看其测量值。
root@rk3308b-buildroot:/# ls /sys/class/hwmon/hwmon3
device in1_input in2_input name of_node power subsystem uevent
root@rk3308b-buildroot:/# cat /sys/class/hwmon/hwmon3/in1_input
583
root@rk3308b-buildroot:/# cat /sys/class/hwmon/hwmon3/in2_input
583
root@rk3308b-buildroot:/#
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)