Last edited 2 years ago

How to use the IIO user space interface

Applicable for STM32MP13x lines, STM32MP15x lines

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
Info white.png Information
Some IIO tools are used in this article (e.g. lsiio). 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.

Info white.png Information
The ADC is enabled by thedevice tree: ADC DT configuration example

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.

Info white.png Information
The DAC is enabled by the device tree: DAC DT configuration example

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.

Info white.png Information
TIM and/or LPTIM are enabled by device tree: See TIM configured in PWM mode and trigger source example and/or LPTIM DT configuration as PWM and trigger source example

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
$ 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.

Info white.png Information
The ADC is enabled by the device tree: ADC DT configuration example

Conversions are triggered by the TIM or LPTIM hardware trigger, See How to set up a TIM or LPTIM trigger using the sysfs interface.

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:

$ 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

4. How to use the quadrature encoder with the sysfs interface[edit source]

Warning white.png Warning
Take care this section is no more dedicated to IIO but is related to the new Linux counter framework coming with STM32 MPU ecosystem-v3


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]

Info white.png Information
The TIM quadrature encoder is enabled by the device tree: TIM configured as quadrature encoder interface
 grep -H "" /sys/bus/counter/devices/*/name   # Look for TIM counter devices
/sys/bus/counter/devices/counter0/name:44000000.timer:counter
 cd /sys/bus/counter/devices/counter0
 cat count0/function_available                # List available modes:
quadrature x2 a
quadrature x2 b
quadrature x4
 echo "quadrature x4" > count0/function       # set quadrature mode
 echo 65535 > count0/ceiling                  # set ceiling value (upper limit for the counter)
 echo 0 > count0/count                        # reset the counter

Runtime configuration is performed using the sysfs interface[4]:

 echo 1 > count0/enable                       # enable the counter

Once started, the encoder value and direction are available using:

 cat count0/count
0
 cat count0/direction
forward

4.2. How to set up the LPTIM quadrature encoder with the sysfs interface[edit source]

Info white.png Information
The LPTIM quadrature encoder is enabled by the device tree: LPTIM configured as quadrature encoder interface

Runtime configuration is performed using the sysfs interface[4]:

 grep -H "" /sys/bus/counter/devices/*/name   # Look for TIM counter devices
/sys/bus/counter/devices/counter0/name:40009000.timer:counter
 cd /sys/bus/counter/devices/counter0
 cat count0/function_available                # List available modes:
increase
quadrature x4
 echo "quadrature x4" > count0/function       # set quadrature mode
 echo 65535 > count0/ceiling                  # set ceiling value (upper limit for the counter)
 echo 1 > count0/enable                       # enable the counter

Once started, the encoder value is available using:

 cat count0/count
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[5] is used to set and clear the encoder input pins, to 'emulate' an external quadrature encoder device.
Info white.png Information
On the STM32MP157X-DKX discovery board, PD1, PG3, TIM1_CH1 and TIM1_CH2 signals are accessible via respectively the D7, D8, D6 and D10 pins of the Arduino Uno connector.

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

Quadrature x4 encoding.png

 cd /sys/bus/counter/devices/counter0/
 cat count0/count                            # [channel A, channel B] = [0, 0]
0
 gpioset gpiochip3 1=1                       # [channel A, channel B] = [1, 0]
 cat count0/count
1
 gpioset gpiochip6 3=1                       # [channel A, channel B] = [1, 1]
 cat count0/count
2
 gpioset gpiochip3 1=0                       # [channel A, channel B] = [0, 1]
 cat count0/count
3
 gpioset gpiochip6 3=0                       # [channel A, channel B] = [0, 0]
 cat count0/count
4
 cat count0/direction
forward
 gpioset gpiochip6 3=1                       # [channel A, channel B] = [0, 1]
 cat count0/count
3
 cat count0/direction                        # Direction has changed, down-counting now
backward
...

5. References[edit source]