NXP i.MX8系列平台开发讲解 - 3.4 Linux 之IIO子系统(sensor opt3001)

本文介绍了LinuxIIO子系统在Android11内核(kernel5.10)下的工业级传感器支持,包括IIO子系统的框架、实现方式以及如何通过sysfs和设备文件访问配置传感器。重点讲述了IIO子系统如何统一接口,简化传感器设备的管理和应用开发。

 专栏文章目录传送门返回专栏目录


目录

1. Linux IIO子系统介绍

2. Linux IIO子系统框架

3. IIO 子系统的实现


本文实操是基于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就可以看到当前的光强强度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码思途远

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值