IIO is the kernel framework providing drivers for IIO devices. Most IIO devices are either on a I2C or SPI bus. Once the kernel driver module is loaded and the driver probed the user space library libiiod
can be used to access them.
We are trying to build drivers for every available IIO device, but these are being added quickly. The idea is that Intel Edison provides a unique platform to easily add an I2C or SPI device to experiment on. If something is missing please raise an issue or send a patch.
Since there are so many drivers we do not build them into the kernel, but provide them as modules. The reasoning here is, eMMC space is relatively cheap while RAM memory is easily consumed.
If you want to optimize for disk space and remove drivers you need to take a look at meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/files/iio.cfg
.
The following is a walk through on how to get a I2C sensor to work.
The following site keep up-to-date lists of supported hardware for each kernel version cateee.net.
To find your device open lkddb-<kernel version>.list
and find or grep your devices name. The latest kernel version is always here. Looking up TSL2561 we find:
i2c "tsl2561" : CONFIG_IIO CONFIG_SENSORS_TSL2563 : drivers/iio/light/tsl2563.c
This means, it’s a I2C device, supported by the IIO subsystem and support is builtin to the tsl2563
module.
upm
driver code as an example. Either case is outside the scope of this example.Dive into meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/files/iio.cfg
to see if we build the module:
CONFIG_SENSORS_TSL2563=m
This means, we build it as a module and the module is installed on the image.
This concerns hardware, so make sure you connect the device to the correct Edison-Arduino pin and correct voltage levels are selected.
In the case of the TSL2561 there’s many sources like GY 2561 where the module accepts 3.3V - 5V. We connect VCC (power header 3.3V), GND, SCL, SDA (digital header).
Load the i2c-dev
module into the kernel
modprobe -i i2c-dev
and reset the power to the module
gpioset `gpiofind TRI_STATE_ALL`=0 && gpioset `gpiofind TRI_STATE_ALL`=1
then detect the device
root@edison:~# i2cdetect -y -r 6
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- 39 -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
This means a device is present on address 39 and it’s not bound to any driver. In fact you can communicate to the device from user space now, for instance using MRAA. But that’s another example.
Using the info we have collected by now, insert the kernel module belonging to the device and bind it
modprobe tsl2563
echo "tsl2561 0x39" > /sys/bus/i2c/devices/i2c-6/new_device
The means the module tsl2563
is inserted, of which we know it provides support for the tsl2561
on address 0x39.
Now you will see:
root@edison:~# i2cdetect -y -r 6
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- UU -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
The device is in use.
For a quick lookup the iio_info
command is provided.
root@edison:~# iio_info
Library version: 0.21 (git tag: 565bf68)
Compiled with backends: local xml ip usb serial
IIO context created with local backend.
Backend version: 0.21 (git tag: 565bf68)
Backend description string: Linux edison 6.1.0-rc6-edison-acpi-standard #1 SMP PREEMPT_DYNAMIC Mon Nov 21 00:02:16 UTC 2022 x86_64
IIO context has 2 attributes:
local,kernel: 6.1.0-rc6-edison-acpi-standard
uri: local:
IIO context has 4 devices:
...
iio:device2: tsl2561
3 channels found:
intensity_ir: (input)
2 channel-specific attributes found:
attr 0: calibscale value: 1000
attr 1: raw value: 226
illuminance0: (input)
1 channel-specific attributes found:
attr 0: input value: 29
intensity_both: (input)
2 channel-specific attributes found:
attr 0: calibscale value: 1000
attr 1: raw value: 1149
No trigger on this device
iio_sysfs_trigger:
0 channels found:
2 device-specific attributes found:
attr 0: add_trigger ERROR: Permission denied (-13)
attr 1: remove_trigger ERROR: Permission denied (-13)
No trigger on this device
This particular device has 2 light sensors, one IR and one ordinary light. From that the response of the human eye is calculated.
libiio
tools are written in Python and can be used as an example code.
For instance iio_attr.py
root@edison:~# iio_attr -c tsl2561 -u local: intensity_both raw
10455
See the instructions on how to build and use iio_oscilloscope or use the following for Ubuntu 22.10.
apt-get -y install libglib2.0-dev libgtk2.0-dev libgtkdatabox-dev libmatio-dev libfftw3-dev libxml2 libxml2-dev bison flex libavahi-common-dev libavahi-client-dev libcurl4-openssl-dev libjansson-dev cmake libaio-dev libserialport-dev
git clone https://github.com/analogdevicesinc/iio-oscilloscope.git
cd iio-oscilloscope
git checkout gtk3transition-master-rebase
mkdir build && cd build
cmake ..
cmake --build .
sudo cmake --install .
Start with:
LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/osc
You can access the edison through the debug tab.
Alternatively, from the command line:
ferry@delfion:~$ iio_attr -c tsl2561 -u ip:edison.local intensity_both raw
9747
Look for a similar device in meta-acpi/recipes-bsp/acpi-tables/samples/edison/, in this case something with an analog input and I2C bus. For instance ads1015.asl
.
DefinitionBlock ("ads1015.aml", "SSDT", 5, "", "ADS1015", 1)
{
External (\_SB.PCI0.I2C1, DeviceObj)
Scope (\_SB.PCI0.I2C1)
{
Device (TIAD)
{
Name (_HID, "PRP0001")
Name (_DDN, "TI ADS1015 ADC")
Name (_CRS, ResourceTemplate () {
I2cSerialBus (
0x48, // Bus address
ControllerInitiated, // Don't care
400000, // 400 kHz
AddressingMode7Bit, // 7-bit addressing
"\\_SB.PCI0.I2C1", // I2C host controller
0 // Must be 0
)
})
/*
* See Documentation/devicetree/bindings/iio/adc/ads1015.txt
* for more information about these bindings.
*/
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () { "compatible", Package () { "ti,ads1015" } },
}
})
}
}
}
Now look up documentation Documentation/devicetree/bindings/iio/light/amstaos,tsl2563.yaml
description: |
Ambient light sensor with an i2c interface.
properties:
compatible:
enum:
- amstaos,tsl2560
- amstaos,tsl2561
- amstaos,tsl2562
- amstaos,tsl2563
reg:
maxItems: 1
amstaos,cover-comp-gain:
description: Multiplier for gain compensation
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 16]
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
light-sensor@29 {
compatible = "amstaos,tsl2563";
reg = <0x29>;
amstaos,cover-comp-gain = <16>;
};
};
...
And then change the things we know:
DefinitionBlock ("tsl2561.aml", "SSDT", 5, "", "TSL2561", 1)
{
/*
* DefinitionBlock (AMLFileName, TableSignature, ComplianceRevision,
* OEMID, TableID, OEMRevision)
*
* AMLFileName — Name of the AML file (string). Can be a null string.
* TableID — A specific identifier for the table (8-character string)
*/
External (\_SB.PCI0.I2C6, DeviceObj)
/*
* I2C6 is defined in another table, will be resolved at table load
*/
Scope (\_SB.PCI0.I2C6)
{
Device (AMLS) // A name for the device
{
Name (_HID, "PRP0001")
Name (_DDN, "AMS TSL2561 Light Sensor")
Name (_CRS, ResourceTemplate () {
I2cSerialBus (
0x39, // Bus address
ControllerInitiated, // Don't care
400000, // 400 kHz
AddressingMode7Bit, // 7-bit addressing
"\\_SB.PCI0.I2C6", // I2C host controller
0 // Must be 0
)
})
/*
* See Documentation/devicetree/bindings/iio/light/amstaos,tsl2563.yaml
* for more information about these bindings.
*/
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () { "compatible", Package () { "amstaos,tsl2561" } },
}
})
}
}
}
First reset the power to the module, then manually load the table through configfs:
root@edison:~# gpioset `gpiofind TRI_STATE_ALL`=0 && gpioset `gpiofind TRI_STATE_ALL`=1
root@edison:~# mkdir /sys/kernel/config/acpi/table/tsl2561
root@edison:~# cat tsl2561.aml > /sys/kernel/config/acpi/table/tsl2561/aml
root@edison:~# journalctl -b -0 -e
Nov 26 21:48:42 edison kernel: ACPI: Host-directed Dynamic ACPI Table Load:
Nov 26 21:48:42 edison kernel: ACPI: SSDT 0xFFFF9F7C884E7800 000117 (v05 TSL2561 00000001 INTL 20200925)
Nov 26 21:48:42 edison kernel: tsl2563 i2c-PRP0001:02: model 5, rev. 0
Once a table is loaded, you can not load it again. The nice thing about using configfs
to load an ASL table is that you can unload it. After this you can load a modified This makes debugging a lot faster.
root@edison:~# rmdir /sys/kernel/config/acpi/table/tsl2561
The TSL2561 has an additional interrupt pin that can be set to generate an interrupt (that in turn generates an event) when there is a significant change in light. We can extend the ASL to use one of the GPIO’s, in particular GP48 which is connected to the Intel-Arduino digital header marked IO7. Because this means programming the GPIO expanders and this is done in the arduino.asli
we need to make a new ASL arduino-leds-ds2-tsl2561.asl
from arduino-leds-ds2.asl and expand that. An example of how to expand with an additional pin can be found in adafruit-mi0283qt.asl
. Finally we must convert tsl2561.asl
into tsl2561.asli
, expand that with the interrupt and include it in arduino-leds-ds2-tsl2561.asl
.
arduino-leds-ds2-tsl2561.asl
:
DefinitionBlock ("leds-ds2-ts2651.aml", "SSDT", 5, "", "LEDS-DS2-TSL2561", 1)
{
#define SPI_CLK_SEL_HOG Name (DSMX, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "gpio-hog", 1 }, Package () { "gpios", Package () { 11, 0 } }, Package () { "output-low", 1 }, Package () { "line-name", "ds2-mux" }, } })
#define SPI_CLK_SEL_REF Package () { "ds2-mux", "DSMX" },
#define CONF_SPI_CLK_SEL
#define MUX18_DIR_HOG Name (DSOE, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "gpio-hog", 1 }, Package () { "gpios", Package () { 13, 0 } }, Package () { "output-high", 1 }, Package () { "line-name", "ds2-oe" }, } })
#define MUX18_DIR_REF Package () { "ds2-oe", "DSOE" },
#define CONF_MUX18_DIR
#define DIG7_PU_PD_HOG Name (LSPU, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "gpio-hog", 1 }, Package () { "gpios", Package () { 7, 0 } }, Package () { "output-high", 1 }, Package () { "line-name", "lightsensor-pu" }, } })
#define DIG7_PU_PD_REF Package () { "lightsensor-pu", "LSPU" },
#define CONF_DIG7_PU_PD
#define MUX32_DIR_HOG Name (LSMX, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () { "gpio-hog", 1 }, Package () { "gpios", Package () { 7, 0 } }, Package () { "output-low", 1 }, Package () { "line-name", "lightsensor-mux" }, } })
#define MUX32_DIR_REF Package () { "lightsensor-mux", "LSMX" },
#define CONF_MUX32_DIR
#include "arduino.asli"
#include "leds.asli"
#include "tsl2561.asli"
}
tsl2561.asli
:
External (\_SB.PCI0.I2C6, DeviceObj)
/*
* I2C6 is defined in another table, will be resolved at table load
*/
Scope (\_SB.PCI0.I2C6)
{
Device (AMLS) // A name for the device
{
Name (_HID, "PRP0001")
Name (_DDN, "AMS TSL2561 Light Sensor")
Name (_CRS, ResourceTemplate () {
I2cSerialBus (
0x39, // Bus address
ControllerInitiated, // Don't care
400000, // 400 kHz
AddressingMode7Bit, // 7-bit addressing
"\\_SB.PCI0.I2C6", // I2C host controller
0 // Must be 0
)
// Assign GPIO48 DIG7 on the arduino header as interrupt
// Found in ft6236.asli example
GpioInt(Level, ActiveLow, Exclusive, PullNone, 0,
"\\_SB.PCI0.GPIO", 0, ResourceConsumer, , ) { 48 }
})
/*
* See Documentation/devicetree/bindings/iio/light/amstaos,tsl2563.yaml
* for more information about these bindings.
*/
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () { "compatible", Package () { "amstaos,tsl2561" } },
}
})
}
#include "gpioexp-dep.asli"
}
tsl2563
driver during probe appeared to request an IRQ handler of the IRQF_TRIGGER_RISING type, overwriting the type defined in the table (Level). As a result the IRQ source was never cleared. The particular line of code was from before 2010, way before Edison existed. Just deleting IRQF_TRIGGER_RISING was already sufficient to make the driver behave normally. Be prepared to check the kernel driver source when things are not working - and write a small patch if needed.To make the interrupt work we need to set thresholds and enable it.
echo 10000 > /sys/bus/iio/devices/iio:device1/events/in_intensity_both_thresh_rising_value
echo 1000 > /sys/bus/iio/devices/iio:device1/events/in_intensity_both_thresh_falling_value
echo 1 > /sys/bus/iio/devices/iio\:device1/events/in_intensity_both_thresh_falling_en
echo 1 > /sys/bus/iio/devices/iio\:device1/events/in_intensity_both_thresh_rising_en
This makes the sensor generate an interrupt every 1.6 sec. when the light intensity is outside the 1000 - 10000 range. You can check that using:
cat /proc/interrupts | grep tsl
304: 0 216 gpio-merrifield 48 tsl2563_event
Or monitor using iio_event_monitor
, an application that is part of the kernel sources under tools/iio/
./iio_event_monitor -a /dev/iio\:device1
Event: time: 1670103049318599238, type: intensity, channel: 0, evtype: thresh, direction: either
© 2018 Ferry Toth