嵌入式Hacker (es-hacker)

Embedded bsp developer enjoys thinking and hacking opensource and develop boards(NanoPi, LicheePi, RPi...)

0%

Linux 驱动开发 / IIO 子系统入门1

前阵子工作上做了一些关于 ADC 的支持,由于现在 ADC 相关的支持都被移动到了 IIO (Industrial I/O) 子系统下,我查阅了一些关于 IIO 资料,包括书籍、文章、内核文档和代码。个人感觉最好的入门文章应该是 ST WiKi 网站上 的 IIO Overview(2019) 和 Analog Device Wiki 网站上的 Linux Industrial I/O Subsystem(2017),为了方便爱偷懒或者英文不好的小伙伴,我提炼了多篇文章的精华内容并在其基础上进行完善,尽量控制篇幅,希望能给大家提供一点小小的帮助。

Linux 驱动开发 / IIO子系统入门1

正文目录:

1
1. 什么是 IIO 子系统?
2
  1.1 IIO 概述
3
  1.2 IIO 相关的组件
4
2. IIO 功能特性
5
3. IIO 相关配置  
6
  3.1 配置内核
7
  3.2 配置设备树
8
    3.2.1 IIO providers
9
    3.2.2 IIO consumers
10
4. IIO API
11
  4.1 用户空间 API
12
    4.1.1 4种接口
13
    4.1.2 操作实例
14
5. 更多值得学习的知识点
15
6. 相关参考

写作目的:

  • 整理一些 IIO 子系统的入门知识。

1. 什么是 IIO 子系统?

1.1 IIO 概述

Industrial I/O 子系统旨在为某种意义上是模数或数模转换器 (ADC,DAC) 的设备提供支持,于2009年由 Huawei 的 Jonathan Cameront 添加。

简单框图:
iio_block_view

支持的设备包括:

1
ADC / DAC
2
加速度计
3
磁力计
4
陀螺仪
5
压力传感器
6
湿度传感器
7
温度传感器
8
...

很久以前,对于上述硬件的支持散落在 Linux 源码中的各种地方。

IIO 的出现,提供了一个统一的框架用于访问和控制上述各种类型的传感器,并且为用户态应用访问传感器提供了标准的接口:sysfs/devfs,并且填补了 Hwmon 和 Input 子系统之间的空白

另外,IIO 不仅可以支持低速的 SoC ADC,还可支持高速、高数据速率的工业设备,例如 100M samples/sec 工业 ADC。

1.2 IIO 相关的组件

IIO-overview

上图基于 STM32 MPU,来源见文末。

1) 客户端应用程序(用户空间):
client-app

该组件会使用 libiio 库来配置 IIO 设备,然后从 IIO 设备读数据或者写数据到 IIO 设备中。客户端程序可以细分为 local client 和 remote client。

2) libiio 库(用户空间):
libiio

libiio 是 Analog Device 公司发起的一个用于访问 IIO 设备的开源库。

它封装了对 /sys/bus/iio/devices(配置 iio) 和 /dev/iio/deviceX(读写iio) 的访问,并且提供了便于测试的 iio 命令行工具 (iio_info / iio_readdev) 和 iiod server

iiod server 内含 local backend 和 remote backend 以支持 local client 和 remote client 的访问。

libiio 的源码位于:github libiio

3) 访问接口(用户空间):

iio 支持多种标准的 Linux 设备访问接口:
char device, sysfs, configfs, debugfs。

4) 内核空间的 iio 消费者(即 IIO consumers):
iio-kern-user

除了用户空间的应用程序能访问 iio 设备之外,在内核里也有其他设备驱动需要使用 iio 子系统的 API 来编写符合自身框架的设备驱动

例如在 iio 子系统里支持了某款 Soc ADC 后,可能会有不同的硬件设备接到该 ADC 通道上,典型的例子是触摸芯片,开发人员需要在 input 子系统的框架下编写 touch driver,在 touch driver的 irq handler 中 调用 iio in-kern API 来读取触摸屏的 X、Y 值。

iio in-kernl API的定义位于头文件:
include/linux/iio/consumer.h

5) IIO framework(内核空间):
IIO 子系统的核心实现。

6) IIO device driver(或称 IIO providers):

7) Linux 内核自带的 IIO 调试工具:


2. IIO 的功能特性

相关参考:

  • Linux-4.14/drivers/staging/iio/Documentation/overview.txt

1) 基础的设备注册和访问

  • 类似于 hwmon 子系统,它们都可以通过 sysfs 以轮循的方式访问设备;

2) 可读取事件的字符设备(Event chrdevs)

  • 类似于 input 子系统,iio 子系统也可以向应用层上报事件(hardware triggered events),例如阈值检测事件,自由落体检测事件、更复杂的动作检测事件;

  • 目前 event 的格式为:event code + 时间戳;

3) 支持硬件 buffer

4) 支持 Trigger 和软件 buffer


3. IIO 相关配置

3.1 配置内核

Linux-4.14:

1
$ make menuconfig
2
Device Drivers  --->
3
  <*> Industrial I/O support  --->
4
    [*]   Enable buffer support within IIO
5
     < >     IIO callback buffer used for push in-kernel interfaces
6
     <*>     Industrial I/O HW buffering
7
     <*>     Industrial I/O buffering based on kfifo
8
     < >   Enable IIO configuration via configfs                        
9
     [*]   Enable triggered sampling support
10
     (2)     Maximum number of consumers per trigger                    
11
     < >   Enable software triggers support                             
12
           Accelerometers  --->                                         
13
           Analog to digital converters  --->                          
14
           Amplifiers  --->                                             
15
           Chemical Sensors  --->                                       
16
           Hid Sensor IIO Common  ----                                  
17
           SSP Sensor Common  --->                                      
18
           Digital to analog converters  --->                           
19
           IIO dummy driver  --->                                       
20
           Frequency Synthesizers DDS/PLL  --->                         
21
           Digital gyroscope sensors  --->                              
22
           Health Sensors  --->                                         
23
           Humidity sensors  --->                                       
24
           Inertial measurement units  --->                             
25
           Light sensors  --->                                          
26
           Magnetometer sensors  --->                                   
27
           Inclinometer sensors  ----                                   
28
           Triggers - standalone  --->                                  
29
           Digital potentiometers  --->                                 
30
           Pressure sensors  --->                                       
31
           Lightning sensors  --->                                      
32
           Proximity sensors  --->                                      
33
           Temperature sensors  --->

从配置项的数目来看,IIO 的用途真的很广泛。


3.2 配置设备树

相关参考:

  • Linux-4.14/Documentation/devicetree/bindings/iio/iio-bindings.txt

3.2.1 IIO providers

1) 相关要点:

  • IIO channels 源在设备树中用 IIO providers 来指定;

2) 必要属性:

  • io-channel-cells,0 表示只有 1 路 IIO output,1 表示有多路 IIO output;

  • io-channel-ranges: 一个 empty 属性(即不用赋值),会在 driver/iio/inkern.c/iio_channel_get() 中被引用,它表明继承了当前节点的子节点可以引用当前节点的 IIO channel;

3) 例子1 (no trigger)

1
adc: voltage-sensor@35 {
2
  compatible = "maxim,max1139";
3
  reg = <0x35>;
4
  #io-channel-cells = <1>;
5
};

4) 例子2 (with trigger)

1
adc@35 {
2
  compatible = "some-vendor,some-adc";
3
  reg = <0x35>;
4
5
  adc1: iio-device@0 {
6
    #io-channel-cells = <1>;
7
    /* other properties */
8
  };
9
  adc2: iio-device@1 {
10
    #io-channel-cells = <1>;
11
    /* other properties */
12
  };
13
};

3.2.2 IIO consumers

1) 相关要点:

  • IIO consumer 节点的形式是 <phandle iio_specifier>;

  • 它的作用是连接 IIO provider 的 input 端到 IIO consumer 的 output 端;

  • 其中,phandle 是 IIO provider 的句柄,specifier 用于选择第几路 channel;

  • 类似 gpio specifier, IIO specifier 是有 1 个或者多个 cells 的数组,用于确定 IIO device的 output 端,即 1 个 cell 对应一个 IIO channel output;

  • IIO specifier 数组的长度由 IIO provider 节点的 #io-channel-cells 属性决定;

2) 必要属性:

  • io-channels: <phandle iio_specifier> 列表,一个 <phandle iio_specifier> 代表该设备连接着的一路 IIO input。如果 IIO provider 的 io-channel-cells=0 (即只有1路 IIO output), 则省略 iio_specifier。

3) 可选属性:  

  • io-channel-names: IIO input 的名称列表,顺序要和 io-channels 属性保持一致,Consumers drivers 会将该名称和 iio_specifier 指定的 IIO input match 到一起。

4) 例子1

1
some_consumer {
2
		io-channels = <&adc 1>, <&ref 0>;
3
		io-channel-names = "vcc", "vdd";
4
	};

上述例子的引用了provider &adc 的第1路 channel,和proiver &ref 的第0路 channel。


4. IIO API

4.1 用户空间 API

相关参考:

4.1.1 4种接口

1). sysfs interface

  • /sys/bus/iio/devices/iio:deviceX;

  • 可用于配置 /dev/iio:deviceX 接口的 events / data

  • 可用于轮循的方式低速地直接读/写 IIO 设备;

  • Documentation/ABI/testing/sysfs-bus-iio;

2). character device

  • /dev/iio:deviceX,该接口在 IIO 子系统里是可选非必要的;

  • 标准的文件 IO API: open(), read(), write(), close().

  • 用于读取 events 和 data;

3). configfs

  • 用于配置额外的 IIO 特性,例如:软件 triggers 或者 hrtimer triggers;

  • 详细说明:

    • Documentation/ABI/testing/configfs-iio;
    • Documentation/iio/iio_configfs.txt;

4). debugfs

  • 一些调试功能,例如 direct_reg_access 节点可用于读写寄存器;

4.1.2 操作实例

IIO direct mode: 通过 sysfs 以轮循的方式读 ADC 或者写 DAC:
1) 直接读 ADC
确定 sysfs 节点(方式1,不依赖工具)

1
$ grep -H "" /sys/bus/iio/devices/*/name | grep adc
2
/sys/bus/iio/devices/iio:device0/name:48003000.adc:adc@0
3
/sys/bus/iio/devices/iio:device1/name:48003000.adc:adc@1

sysfs 中的 iio:device0 sysfs 对应 ADC1;

1
$ cd /sys/bus/iio/devices/iio:device0/
2
$ cat in_voltage6_raw      # Convert ADC1 channel 0 (analog-to-digital): get raw value
3
40603
4
$ cat in_voltage_scale     # Read scale
5
0.044250488
6
$ cat in_voltage_offset    # Read offset
7
0
8
$ awk "BEGIN{printf (\"%d\n\", (40603 + 0) * 0.044250488)}"
9
1796

计算公式: Scaled value = (raw + offset) * scale = 1796 mV;

2) 直接写 DAC
确定 sysfs 节点(方式2)

1
$ lsiio | grep dac
2
Device 003: 40017000.dac:dac@1
3
Device 004: 40017000.dac:dac@2

sysfs 中的 iio:device3 sysfs 对应 DAC1,lsiio 来源于Linux 内核源码(tools/iio/)。

1
$ cd /sys/bus/iio/devices/iio:device3/
2
$ cat out_voltage1_scale              # Read scale
3
0.708007812
4
$ awk "BEGIN{printf (\"%d\n\", 2000 / 0.708007812)}"  # 假设要输出 2000 mV
5
2824
6
$ echo 2824 > out_voltage1_raw        # Write raw value to DAC1
7
$ echo 0 > out_voltage1_powerdown     # Enable DAC1 (out of power-down mode)

5. 更多值得学习的知识点

  • 以 timer triggers 和 buffers 的方式读 ADC 或者写 DAC;
  • IIO 内核空间 API;
  • 编写 IIO device driver
  • 编写 IIO consumer driver

鉴于大多数人的注意力无法在一篇文章里上集中太久,更多的内容请大家先自行去阅读吧,不是自己理解到的东西是消化不了的。有机会的话我会把更多的读书心得放在后面的文章。


6. 相关参考

本文只是一篇入门的文章,仍然有许多细节的知识点没有被描述到。如果本文能让你对 IIO 子系统有个大概的认识,那么本文的目的也就算达成了,以后还会继续写更多 IIO 子系统的文章。知识的学习应该是螺旋上升的,找到一条平缓的学习路线很重要,先有一个整体的概念,然后再不断地去丰富细节会比较好一点。就好像爬山一样,相对于几千米的高山,人类看起来总是显得多么的渺小,但是只要每天平稳地走几百个阶梯,再高的山也能在不知不觉中走完。


思考技术,也要思考人生

学习技术,更要学习如何生活

你和我各有一个苹果,如果我们交换苹果的话,我们还是只有一个苹果。但当你和我各有一个想法,我们交换想法的话,我们就都有两个想法了。

如果你也对 嵌入式系统 (Linux、RTOS、OpenWrt、Android) 和 开源软件 感兴趣,并且想和更多人互相交流学习的话,请关注我的公众号:嵌入式Hacker,一起来学习吧。

无论是关注或转发,还是打赏,都是对作者莫大的支持。对了,记得点 在看和点赞 ,如果你还想看到我的推送的话。

ps:
欢迎加入我的微信群:加我微信,我拉你进群,暗号(加群)。

祝各位工作顺利,家庭幸福,财源滚滚~

这是一篇有趣的文章吗?

欢迎关注我的其它发布渠道