Industrial I/O and triggers
I've maintained a couple of IIO-drivers (MCP3911 [4] and LTC1660 [5]) for some time now and it's time to give at least the MCP3911 a face-lift.
This time the face lift includes support for:
- Buffers
- Triggers
- Make the driver interrupt driven
- Add support for setting Oversampling Ratio
- Add support for setting PGA (Pre Gain Amplifier)
Also clean it up a bit by only using device managed resources.
What is Industrial I/O?
Industrial I/O, or IIO [1], is a subsystem that exposes sensors and actuators in a common way to userspace. The subsystem supports a range of different sensors including ADCs, IMUs, pressure sensors, light sensors, accelerometers and more. Even actuators such as DACs and amplifiers has its place in the IIO subsystem.
The hwmon [2] subsystem provides an interface for a few types of sensors as well, but the framework lack support to cover some use cases that IIO tries to solve, such as:
- High speed sensors
- Triggered sampling
- Data buffering
In short, use hwmon for slow sensors and actuators, otherwise use IIO (preferred for new devices).
The IIO subsystem also provides a stable ABI for various userspace HALs which hwmon does not. libiio [3] is the official and preferred one for the IIO.
![/media/iio.png](/media/iio.png)
Sysfs
All IIO devices is exported by sysfs where those can be configured and read single shot values. For example, the raw value of the first channel of an ADC can be read out by:
cat /sys/bus/devices/iio:device/in_voltage0_raw
All other parameters such as oversampling ratio and scaling value is also exposed here.
Scaling value
The value you get from in_voltageX_raw is the raw value, it means that it has to be converted in order to get something meaningful out of it.
To get the value in mV you have to take the scale and offset value into account:
Value in mV = (raw + offset) * scale
All these values are exposed by sysfs in in_voltage_scale and in_voltage_offset respectively.
Triggers
Triggers can be both hardware and software based.
Example on hardware based triggers are:
- GPIO-based interrupts
Example on software based triggers are:
- sysfs - you can trig a data poll from userspace
- hrtimer - let you specify the period and a High Resolution timer will be created and trig a data poll at a given frequency
CONFIG_IIO_SYSFS_TRIGGER
By enable CONFIG_IIO_SYSFS_TRIGGER you can make use of the sysfs trigger
# echo 0 > /sys/bus/iio/devices/iio_sysfs_trigger/add_trigger # cat /sys/bus/iio/devices/iio_sysfs_trigger/trigger0/name sysfstrig0
CONFIG_IIO_HRTIMER_TRIGGER
By enable CONFIG_IIO_HRTIMER_TRIGGER you can make use of a timer based trigger
# mount -t configfs none /sys/kernel/config # mkdir /config/iio/triggers/hrtimer/my_50ms_trigger # echo 2000 > /sys/bus/iio/devices/trigger0/sampling_frequency
Make use of a trigger
As long as the device supports triggers, there will be an /sys/bus/iio/devices/iio:device0/trigger/current_trigger entry. All available triggers, both hardware and software based, are located in /sys/bus/iio/devices/triggerX.
One nice feature is that one trigger can be used for multiple devices.
In order to activate a trigger for a certain device, simply write the trigger name to the current_trigger entry:
# cat /sys/bus/iio/devices/trigger0/name > /sys/bus/iio/devices/iio:device0/trigger/current_trigger
The next step is to decide and enable those channels you want to scan
# echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_en # echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage1_en # echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_timestamp_en
And finally, start the sampling process
# echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable
Now you will get the raw values for voltage0, voltage1 and the timestamp by reading from the /dev/iio:device0 device.
You will read out a stream of data from the device. Before applying the scale value to the raw data, the buffer data may be processed somehow depending on its format. The format for each channel is available as a sysfs entry as well:
# cat /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_type be:s24/32>>0
The buffer format for voltage0 means that each sample is 32 bits wide, does not need any shifting and should be intepreted as a signed 24-bit value.
Conclusion
The IIO subsystem is rather complex. The framework also supports events which makes it possible to trig on specific threshold values. As the subsystem is optimized for performance and the triggers makes it possible to read values at a given frequency or event, this makes a lot more use cases possible than the older hwmon interface.
FYI, the patches for MCP3911 is currently up to be merged into mainline.
References
[1] | https://www.kernel.org/doc/html/latest/driver-api/iio/intro.html |
[2] | https://www.kernel.org/doc/html/latest/hwmon/hwmon-kernel-api.html |
[3] | https://wiki.analog.com/resources/tools-software/linux-software/libiio |
[4] | https://elixir.bootlin.com/linux/latest/source/drivers/iio/adc/mcp3911.c |
[5] | https://elixir.bootlin.com/linux/latest/source/drivers/iio/dac/ltc1660.c |