专栏文章目录传送门:返回专栏目录
目录
本文实操是基于Android11 系统下i.MX8MQ环境下:
-
cpu: i.mx8mq
-
OS:Android 11
-
Kernel version:kernel 5.10
Linux IIO(Industrial I/O)子系统是Linux内核中的一个子系统,用于处理与工业领域相关的数据输入和输出设备。它支持各种传感器和数据采集设备,如加速度计、陀螺仪、温度传感器等。IIO子系统提供了统一的接口和框架,允许用户空间应用程序通过sysfs和设备文件来访问和配置这些传感器数据。这使得开发者可以方便地采集和处理来自各种传感器的数据,并实现更复杂的应用和算法。
1. Linux IIO子系统介绍
Linux IIO子系统是为了工业输入输出所涉及的子系统,主要用于ADC或者DAC或者两者都有的设备提供的一种设备驱动的支持。主要很好规范各种不同设备具有统一接口提供给开发人员使用的。在目前使用各种传感器大多都是ADC的,有通过IIC,SPI等接口进行获取数据或者配置,种类非常繁多,有IIO子系统就很好对这些传感器进行使用起来。
目前IIO常见的管理的设备:
-
加速度计:用于测量加速度和倾斜,广泛应用于工业自动化、移动设备和汽车领域等。
-
陀螺仪:用于测量角速度和转动,常见于飞行器、导航系统和姿态控制应用。
-
温度传感器:用于测量环境温度,广泛应用于气象、环境监测和智能家居等。
-
压力传感器:用于测量气体或液体压力,常见于气象观测、工业自动化和医疗设备。
-
光传感器:用于测量环境光照强度,广泛应用于显示器自动调光和照明控制等。
-
湿度传感器:用于测量空气湿度,常见于气象、农业和温室应用。
-
....
以上这些常用的传感器设备一般通过IIC/SPI总线接入到Linux中,在Linux可以看到IIO设备,然后在应用上进行操作。
2. Linux IIO子系统框架
从IIO系统框架来看,从底部网上查看
Bus Drivers: 这是一种特殊类型驱动程序,负责管理和处理与传感器相关的总线通信,主要是作为与传感器进行数据交互,包括数据的配置与数据传输。IIO 支持多种不同总线类型,比如SPI/I2C/UART等等;
Device Driver: 这里指的是特定的传感器或者数据采集设备的驱动程序,每个设备都需要响应的Device Driver与IIO进行交互,实现数据采集控制;
IIO Subsystem:包含iio_ring/iio_core/iio_trigger等这些属于IIO Framwrok部分,iio_ring相当于连续采集的数据缓存buffer,iio_ring将提供字符设备文件的注册,所以在应用上可以通过字符设备读取该设备的数据;iio_core主要实现iio device的创建,iio_trigger是一种触发,当收到该触发后将数据放入buffer。
3. IIO 子系统的实现
iio 目录结构
.
├── accel
├── adc
├── afe
├── amplifiers
├── buffer
├── built-in.a
├── chemical
├── common
├── dac
├── dummy
├── frequency
├── gyro
├── health
├── humidity
├── iio_core.h
├── iio_core_trigger.h
├── imu
├── industrialio-buffer.c
├── industrialio-buffer.o
├── industrialio-configfs.c
├── industrialio-core.c
├── industrialio-core.o
├── industrialio-event.c
├── industrialio-event.o
├── industrialio-sw-device.c
├── industrialio-sw-trigger.c
├── industrialio-trigger.c
├── industrialio-triggered-event.c
├── industrialio-trigger.o
├── inkern.c
├── inkern.o
├── Kconfig
├── light
├── magnetometer
├── Makefile
├── modules.order
├── multiplexer
├── orientation
├── position
├── potentiometer
├── potentiostat
├── pressure
├── proximity
├── resolver
├── temperature
├── TODO
└── trigger
从目录结构来看,可以明显看到具体相关类的传感器,加速度传感器accel, 模数转换器adc, 温度传感器addac,还有其他就不再一一讲述。
主要实现函数
ls ./drivers/iio/industrialio-*.c
./drivers/iio/industrialio-buffer.c
./drivers/iio/industrialio-core.c
./drivers/iio/industrialio-sw-device.c
./drivers/iio/industrialio-trigger.c
./drivers/iio/industrialio-configfs.c
./drivers/iio/industrialio-event.c
./drivers/iio/industrialio-sw-trigger.c
./drivers/iio/industrialio-triggered-event.c
这几个函数文件都是围绕iio_dev/ iio_info/ 还有iio_buffer, iio_trigger的功能实现,具体的调用可以看到下图相关的关系。
所有的可以通过IIO子系统实现的传感器都是在./drivers/iio/,如果是没有该驱动可以根据相近的驱动进行修改添加。
这里以光强传感器opt3001来说明;
opt3001的驱动文件,从probe入手:
static int opt3001_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct iio_dev *iio;
struct opt3001 *opt;
int irq = client->irq;
int ret;
iio = devm_iio_device_alloc(dev, sizeof(*opt));
if (!iio)
return -ENOMEM;
opt = iio_priv(iio);
opt->client = client;
opt->dev = dev;
mutex_init(&opt->lock);
init_waitqueue_head(&opt->result_ready_queue);
i2c_set_clientdata(client, iio);
//通过I2C读取设备ID是存在
ret = opt3001_read_id(opt);
if (ret)
return ret;
//写寄存器配置opt3001
ret = opt3001_configure(opt);
if (ret)
return ret;
//填充iio_dev 结构体,将opt->info 具体操作实现,
iio->name = client->name;
iio->channels = opt3001_channels;
iio->num_channels = ARRAY_SIZE(opt3001_channels);
iio->modes = INDIO_DIRECT_MODE;
iio->info = &opt3001_info;
//注册一个iio设备
ret = devm_iio_device_register(dev, iio);
if (ret) {
dev_err(dev, "failed to register IIO device\n");
return ret;
}
/* Make use of INT pin only if valid IRQ no. is given */
if (irq > 0) {
//配置中断
ret = request_threaded_irq(irq, NULL, opt3001_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"opt3001", iio);
if (ret) {
dev_err(dev, "failed to request IRQ #%d\n", irq);
return ret;
}
opt->use_irq = true;
} else {
dev_dbg(opt->dev, "enabling interrupt-less operation\n");
}
return 0;
}
从OPT3001probe来看主要是设置了通信接口I2C,初始化设备,并注册iio_dev设备,这样就和iio联系起来。
通道的设置:iio->channels
static const struct iio_chan_spec opt3001_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
BIT(IIO_CHAN_INFO_INT_TIME),
.event_spec = opt3001_event_spec,
.num_event_specs = ARRAY_SIZE(opt3001_event_spec),
},
IIO_CHAN_SOFT_TIMESTAMP(1),
};
每个传感器可以有一个或多个通道,每个通道用于描述传感器输出的一种类型的数据。在这里对于光照传感器来说,只有一个通道去描述光照强度数据就狗了。但是对于其他一些比如加速度计就存在多个通道。这个描述通道作用是在IIO框架交互能够以标准化交互。
iio_info部分
static const struct iio_info opt3001_info = {
.attrs = &opt3001_attribute_group, //提供个sysfs使用
.read_raw = opt3001_read_raw, //读取传感器数据
.write_raw = opt3001_write_raw, //配置
.read_event_value = opt3001_read_event_value, //读取一个事件,比如对opt3001设定了一个阈值,如果超过 就会产生一个事件
.write_event_value = opt3001_write_event_value,//设置一个事件,比如阈值
.read_event_config = opt3001_read_event_config,//读取事件配置
.write_event_config = opt3001_write_event_config,//设置事件配置
};
从结构体来说基本就已经定义了该传感器的功能与接口,这样使得在IIO层就可以去实现数据的采集配置和时间的处理等功能。
实际效果查看
cat in_illuminance_input就可以看到当前的光强强度。