How to use the IIO user space interface with a user terminal.
1. Purpose[edit source]
This article describes how to use the IIO with a user terminal.
The use cases of the following examples are:
- analog to digital
- digital to analog
- quadrature encoder[1] monitoring
Conversions between an STM32 board and an external device:
- Basic reads from ADC (for example by polling) or writes to a DAC are performed using sysfs
- More advanced use cases (with timer triggers and buffers) are performed using sysfs configuration and character devices either directly or with tools
- Simulation of a quadrature encoder device using GPIOs
Note: Some IIO tools are used in this article. A list of IIO tools is defined in dedicated articles: IIO Linux kernel tools and libiio tools.
2. How to do a simple conversion using the sysfs interface[edit source]
The IIO sysfs interface can be used to configure devices and do simple conversions at low rates.
This is usually referred to as IIO direct mode in IIO device drivers.
Documentation/ABI/testing/sysfs-bus-iio[2] is the Linux® kernel documentation that fully describes the IIO standard ABI.
Note: To convert a raw value to standard units, the IIO defines this formula: Scaled value = (raw + offset) * scale
2.1. How to do a simple ADC conversion using the sysfs interface[edit source]
This example shows how to read a single data from the ADC, using sysfs.
First, look for the IIO device matching the ADC peripheral:
$ grep -H "" /sys/bus/iio/devices/*/name | grep adc # or use 'lsiio | grep adc'
/sys/bus/iio/devices/iio:device0/name:48003000.adc:adc@0 # Going to use iio:device0 sysfs, that matches ADC1
/sys/bus/iio/devices/iio:device1/name:48003000.adc:adc@100
Then, perform a single conversion on an ADC, and also read the ADC scale and offset:
$ cd /sys/bus/iio/devices/iio:device0/ $ cat in_voltage6_raw # Convert ADC1 channel 0 (analog-to-digital): get raw value 40603 $ cat in_voltage_scale # Read scale 0.044250488 $ cat in_voltage_offset # Read offset 0 $ awk "BEGIN{printf (\"%d\n\", (40603 + 0) * 0.044250488)}" # Scaled value = (raw + offset) * scale 1796 # Result: 1796 mV
2.2. How to do a simple DAC conversion using the sysfs interface[edit source]
This example shows how to write single data to the DAC, using sysfs.
First, look for the IIO device matching the DAC peripheral:
$ lsiio | grep dac
Device 003: 40017000.dac:dac@1 # Going to use iio:device3 sysfs, that matches DAC1
Device 004: 40017000.dac:dac@2
Then, check the DAC scale to compute the raw value:
$ cd /sys/bus/iio/devices/iio:device3/ $ cat out_voltage1_scale # Read scale 0.708007812 $ awk "BEGIN{printf (\"%d\n\", 2000 / 0.708007812)}" # Example to convert convert 2000 mV (millivolts) 2824 $ echo 2824 > out_voltage1_raw # Write raw value to DAC1 $ echo 0 > out_voltage1_powerdown # Enable DAC1 (out of power-down mode): DAC now converts from digital to analog # User can now convert new value with 'echo xxxx > out_voltage1_raw'
3. Convert one or more channels using triggered buffer mode[edit source]
Building upon on what is described in the article User space interface, the user should:
- configure and enable the IIO trigger via sysfs (/sys/bus/iio/devices/triggerX)
- configure and enable the IIO device via sysfs (/sys/bus/iio/devices/iio:deviceX)
- access configured events and data from character device (/dev/iio:deviceX)
This is typically the case when using one of the IIO buffer modes.
See The Linux driver implementer’s API guide - Industrial I/O Buffers for further details.
The STM32 provides several hardware triggers, among which TIM and LPTIM can be used in IIO.
3.1. How to set up a TIM or LPTIM trigger using the sysfs interface[edit source]
This example shows how to set up a TIM or an LPTIM trigger, using sysfs.
Runtime configuration is performed using the sysfs interface:
$ lsiio | grep tim # Look for IIO device that matches TIM and/or LPTIM peripheral Device 010: 44000000.timer:trigger@0 Trigger 000: tim6_trgo Trigger 001: tim1_trgo Trigger 002: tim1_trgo2 Trigger 003: tim1_ch1 Trigger 004: tim1_ch2 Trigger 005: tim1_ch3 Trigger 006: tim1_ch4
Either the TRGO or the PWM output can be configured, and used as the trigger source for analog conversions.
- To configure the timX_trgo trigger, the "sampling_frequency" (Hz) can be set directly:
$ cd /sys/bus/iio/devices/trigger0/ $ cat name tim6_trgo $ echo 10 > sampling_frequency # Set up 10Hz sampling frequency on tim6_trgo
- When using the timX_chY or the lptimX_outY trigger, the frequency must be set using the PWM framework. See How to use PWM with sysfs interface.
$ cd /sys/bus/platform/devices/44000000.timer:pwm/pwm/pwmchip0 $ echo 0 > export # Export tim1_ch1 PWM $ echo 100000000 > pwm0/period $ echo 50000000 > pwm0/duty_cycle $ echo 1 > pwm0/enable # Enable tim1_ch1 with 10Hz frequency and 50% duty cycle
3.2. How to perform multiple ADC conversions in triggered buffer mode[edit source]
This example shows how to read multiple data from an ADC, to scan one or more channels.
As an example, ADC in0 and in1 can be converted in sequence.
- sysfs interface overview:
$ cd /sys/bus/iio/devices/iio\:device0 $ cat name 48003000.adc:adc@0 $ ls scan_elements in_voltage0_en in_voltage0_index in_voltage0_type in_voltage1_en in_voltage1_index in_voltage1_type $ ls trigger current_trigger $ ls buffer enable length watermark
- Example to enable ADC channel 0 and channel 1, and use the tim6_trgo trigger source :
$ echo 1 > scan_elements/in_voltage0_en # Enable channel 0 $ echo 1 > scan_elements/in_voltage1_en # Enable channel 1 $ echo "tim6_trgo" > trigger/current_trigger # Assign tim6_trgo trigger to ADC $ cat trigger/current_trigger tim6_trgo $ echo 1 > buffer/enable # Start ADC in buffer mode
- character device data out:
$ hexdump -e '"iio0 :" 8/2 "%04x " "\n"' /dev/iio:device0 & # Read data from /dev/iio:device0, display by group of 8, 2 bytes. iio0 :9f15 0000 9e9f 0000 9f18 0000 9ee4 0000 # Result: raw data out in the form of: in0 data | in1 data | in0 data... ...
3.3. How to perform multiple ADC conversions in triggered buffer mode using libiio[edit source]
Prerequisite: please see the similar example: How to perform multiple ADC conversions in triggered buffer mode.
That example uses iio_readdev[3] provided by libiio tools.
The example below requests 8 data samples on the ADC configured with:
- channel 0 and channel 1, also referred to as voltage0 and voltage1, enabled
- tim6_trgo, also referred to as trigger0 to trigger conversions, see How to set up a TIM or LPTIM trigger using the sysfs interface
$ iio_readdev -t trigger0 -s 8 -b 8 iio:device0 voltage0 voltage1 | hexdump 0000000 9efe 0000 9ed9 0034 9eff 0000 9ee5 0000 0000010 9edb 0011 9ecc 000b 9eb0 0000 9ed4 0001
3.4. How to get ADC analog watchdog events[edit source]
Before starting triggered conversions (buffer mode), an analog watchdog can be set to report 'out of range' events:
- it needs to be configured with high and low threshold values
- it needs to be enabled to report events when either the high or low threshold is reached
$ cd /sys/bus/iio/devices/iio\:device0/ echo $((0x9000)) > events/in_voltage0_thresh_rising_value # Set high threshold value echo $((0x7000)) > events/in_voltage0_thresh_falling_value # Set low threshold value echo 1 > events/in_voltage0_thresh_either_en # Enable analog watchdog to report out of range events
Before starting conversions as described in above, start event monitor in background:
$ iio_event_monitor /dev/iio:device0 &
After starting conversions, if the ADC conversion results are outside the configured thresholds, the event monitor reports as follows:
Event: time: 1529352199639112110, type: voltage, channel: 0, evtype: thresh, direction: either Event: time: ...
4. How to use the quadrature encoder with the sysfs interface[edit source]
This example shows how to monitor the position (count) of a linear (or rotary) encoder.
It uses quadrature the encoder[1] interface available on the TIM and LPTIM internal peripherals.
4.1. How to set up the TIM quadrature encoder with the sysfs interface[edit source]
Runtime configuration is performed using the sysfs interface:
$ lsiio | grep tim # Look for IIO device that matches TIM peripheral Device 002: 44000000.timer:timer@0 ... $ cd /sys/bus/iio/devices/iio:device2/ $ cat in_count_quadrature_mode_available # List available modes: channel_A channel_B quadrature $ echo 65535 > in_count0_preset # set ceiling value (upper limit for the counter) $ echo quadrature > in_count0_quadrature_mode # set quadrature mode $ echo 0 > in_count0_raw # reset the counter $ echo 1 > in_count0_en # enable the counter
Once started, the encoder value and direction are available using:
$ cat in_count0_raw 0 $ cat in_count0_count_direction up
4.2. How to set up the LPTIM quadrature encoder with the sysfs interface[edit source]
Runtime configuration is performed using the sysfs interface:
$ lsiio | grep tim Device 003: 50021000.timer:counter ... $ cd /sys/bus/iio/devices/iio:device3/ $ cat in_count_quadrature_mode_available # List available modes: non-quadrature quadrature $ echo 65535 > in_count0_preset # set ceiling value (upper limit for the counter) $ echo quadrature > in_count0_quadrature_mode # set quadrature mode $ echo both-edges > in_count0_polarity # set polarity (both edges) $ echo 1 > in_count0_en # enable the counter
Once started, the encoder value is available using:
$ cat in_count0_raw
0
4.3. How to use the TIM or LPTIM quadrature encoder with the sysfs interface[edit source]
This example shows how to monitor the TIM quadrature encoder interface via sysfs (the LPTIM case is very similar):
- In this example, two GPIO lines (PD1, PG3) are externally connected to the TIM (or LPTIM)
- Then libgpiod[4] is used to set and clear the encoder input pins, to 'emulate' an external quadrature encoder device.
Step-by-step example:
- Externally connect and initialise GPIO pins to TIM or LPTIM encoder input pins, to 'emulate' an external quadrature encoder
$ gpiodetect ... gpiochip6 [GPIOG] (16 lines) ... gpiochip3 [GPIOD] (16 lines) ... $ gpioset gpiochip3 1=0 # initialize PD1 to 0 as GPIO, connect it to TIM or LPTIM channel A input $ gpioset gpiochip6 3=0 # initialize PG3 to 0 as GPIO, connect it to TIM or LPTIM channel B input
- Set up the TIM or LPTIM quadrature encoder with the sysfs interface, see How to set up the TIM quadrature encoder with the sysfs interface or How to set up the LPTIM quadrature encoder with the sysfs interface
- GPIO pins are then set or cleared as follows:
$ cd /sys/bus/iio/devices/iio:deviceX/ $ cat in_count0_raw # [channel A, channel B] = [0, 0] 0 $ gpioset gpiochip3 1=1 # [channel A, channel B] = [1, 0] $ cat in_count0_raw 1 $ gpioset gpiochip6 3=1 # [channel A, channel B] = [1, 1] $ cat in_count0_raw 2 $ gpioset gpiochip3 1=0 # [channel A, channel B] = [0, 1] $ cat in_count0_raw 3 $ gpioset gpiochip6 3=0 # [channel A, channel B] = [0, 0] $ cat in_count0_raw 4 $ cat in_count0_count_direction up $ gpioset gpiochip6 3=1 # [channel A, channel B] = [0, 1] $ cat in_count0_raw 3 $ cat in_count0_count_direction # Direction has changed, down-counting now down ...
5. References[edit source]
- ↑ 1.0 1.1 Quadrature encoder, Incremental encoder overview
- ↑ sysfs-bus-iio ABI, Linux standard sysfs IIO interface
- ↑ https://wiki.analog.com/resources/tools-software/linux-software/libiio/iio_readdev, iio_readdev
- ↑ Control GPIO through libgpiod