Skip to main content
Version: Torizon OS 7.x.y

How to use SPI on Torizon

Introduction

This article describes how to access SPI devices from both kernel space and user space on Torizon OS. SPI (Serial Peripheral Interface) is a common interface for a variety of sensors and other hardware peripherals. SPI devices operate in a relationship, where the "master" (controller) starts the communication with the "slave" (sensor, display, or other peripheral), which returns information. It requires a software layer to connect the OS system calls to the hardware.

Device Drivers and User Space Applications

A device driver controls how a hardware device interacts with the OS. For instance, it can efficiently use peripheral resources and handle potential problems, such as race conditions, that may occur when multiple threads attempt to communicate with the hardware. However, debugging can be challenging if issues arise, given that driver source codes are often complex and difficult to understand.

Another option is to use the standard SPI interface for Linux and manually program how the hardware and operating system interact. This method provides considerable flexibility in defining the peripheral behavior, but it requires knowing hardware details. If you are working with a widely used device, you will probably find source code in different programming languages that can serve as a starting point for your application. However, it may lead to conflicts and performance issues. Thus, this approach can help with device testing when driver errors arise or no specific device drivers are available.

The steps for each method vary with different devices and use cases. Thus, to support your understanding, this article provides the following sections:

This article complies with the Typographic Conventions for Torizon Documentation.

Prerequisites

Connect an SPI Device

Toradex SoMs offer multiple SPI buses for connecting peripherals, with some pins having SPI as their default or alternate function. Prioritize using pin functions compatible with all modules within the same family, ensuring the best compatibility with standard software and other modules in the family. To change pin functionalities, refer to the Pin Multiplexing article.

You can find pin information in the SoM and carrier board datasheets.

danger

Always check the IO voltage level of your modules and peripherals before connecting them. The TTL level for Colibri and Apalis IO is 3.3V, while for Verdin it is 1.8V. Do not apply 3.3V signals to Verdin IO.

The Verdin iMX8M Plus module provides multiple SPI buses, including the SPI_1, which is designed for general use and is the one we will use in examples throughout this article. That bus features the following signals:

  • SPI_1_MOSI
  • SPI_1_MISO
  • SPI_1_CS
  • SPI_1_CLK

You can find this information in the SPI section of your module datasheet as follows:

X1 Pin#Verdin Std Functioni.MX 8MP Ball Namei.MX 8MP FunctionI/ODescription
200SPI_1_MOSIECSPI1_MOSIECSPI1_MOSIOMaster Output, Slave Input
198SPI_1_MISOECSPI1_MISOECSPI1_MISOIMaster Input, Slave Output
202SPI_1_CSECSPI1_SS0ECSPI1_SS0I/OSlave Select
196SPI_1_CLKECSPI1_SCLKECSPI1_SCLKI/OSerial Clock

To connect your device, search for pins that carry these signals in your carrier board schematics or datasheet.

Locate SPI buses in the OS

Torizon OS, as a Linux-based distribution, treats peripheral devices as files. SPI files are located in the /sys/bus/spi directory. This section explains how to locate SPI buses in the OS using the information available in modules and carrier boards datasheets and device tree files.

As an example, the following steps describe how to locate the SPI_1 bus of the Verdin iMX8M Plus in the OS.

  1. Identify the pin control group that contains the SPI signals: You can find this information in the SoM's device tree.

    The following code snippet indicates, for example, that the pinctrl_ecspi1 control group contains the ECSPI1_MISO pin with the ECSPI1_MISO function.

    imx8mp-verdin.dtsi
    pinctrl_ecspi1: ecspi1grp {
    fsl,pins =
    <MX8MP_IOMUXC_ECSPI1_MISO__ECSPI1_MISO 0x1c4>, /* SODIMM 198 */
    <MX8MP_IOMUXC_ECSPI1_MOSI__ECSPI1_MOSI 0x4>, /* SODIMM 200 */
    <MX8MP_IOMUXC_ECSPI1_SCLK__ECSPI1_SCLK 0x4>, /* SODIMM 196 */
    <MX8MP_IOMUXC_ECSPI1_SS0__GPIO5_IO09 0x1c4>; /* SODIMM 202 */
    };
  2. Locate the bus node that corresponds to the identified pin control group.

    The snippet below shows that the ecspi1 node uses the pinctrl_ecspi1 pin control group.

    imx8mp-verdin.dtsi
    /* Verdin SPI_1 */
    &ecspi1 {
    #address-cells = <1>;
    #size-cells = <0>;
    cs-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_ecspi1>;
    };
  3. Search for the corresponding register address in the root node: The reg property contains the SPI bus address.

    In the following example, the address is 0x30820000.

    imx8mp.dtsi
    ecspi1: spi@30820000 {
    #address-cells = <1>;
    #size-cells = <0>;
    compatible = "fsl,imx8mp-ecspi", "fsl,imx6ul-ecspi";
    reg = <0x30820000 0x10000>;
    interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clk IMX8MP_CLK_ECSPI1_ROOT>,
  4. List the available SPI buses. In your module's terminal, run the following command:

    # ls -l /sys/bus/spi/devices/
    lrwxrwxrwx 1 root root 0 Oct 19 12:54 spi1.0 -> ../../../devices/platform/soc@0/30800000.bus/30820000.spi/spi_master/spi1/spi1.0

    The output above shows there is one SPI bus available. It is possible to identify that spi1.0 is the SPI_1 file, which has the 0x30820000 address.

Peripheral Access from Kernel Space

How to Configure and Use SPI Devices

This section provides a step-by-step guide on how to collect the measurements from an FXAS21002C sensor using its driver. It will give you a general understanding of working with kernel drivers in embedded Linux.

  1. Verify if any driver for your device is available in the Linux Kernel Source.

    The FXAS21002C driver is named fxas21002c and resides at linux/drivers/iio/gyro.

    tip

    The device name often gives insights into how its driver files are labeled.

  2. Search for the corresponding config variable: The Makefile with this information is in the same folder as the driver's source code. The config variable can help you check the driver availability on your OS.

    The Makefile for industrial I/O gyroscope sensor drivers has the following configuration:

    obj-$(CONFIG_FXAS21002C) += fxas21002c_core.o
    obj-$(CONFIG_FXAS21002C_SPI) += fxas21002c_spi.o

    If the driver is available on your OS, you will find the CONFIG_FXAS21002C and CONFIG_FXAS21002C_SPI config variables in the kernel configuration.

  3. Check the kernel configuration of your OS: The config.gz file, located at /proc, contains driver module information.

    For example, to check if the FXAS21002C configuration is available on Torizon OS, run the zcat command:

    # zcat /proc/config.gz | grep FXAS21002C
    CONFIG_FXAS21002C=m
    CONFIG_FXAS21002C_I2C=m
    CONFIG_FXAS21002C_SPI=m

    The configuration set as m indicates that the driver is available in the OS as a Loadable Kernel Module (LKM) rather than being built-in.

    If the driver is not available on Torizon OS, follow the instructions described in the Building External Kernel Modules With Torizon article.

  4. Customize your OS image: Since this driver is an LKM, it has to be activated using a Device Tree Overlay. You can find examples of overlays on the web that can serve as a base for your customization.

    Identify the node of an SPI bus as described in the Locate SPI buses in the OS. For instance, the ecspi1 node contains the SPI_1 bus configuration of the Verdin iMX8M Plus module. To activate the device driver for this bus, you can create a Device Tree Overlay to configure the ecspi1 node.

    The following overlay activates the FXAS21002C driver:

    // Definitions for the FXAS21002C Gyroscope
    /dts-v1/;
    /plugin/;

    / {
    compatible = "toradex,verdin-imx8mp";
    };

    &ecspi1 { // select interface as per board
    #address-cells = <1>;
    #size-cells = <0>;
    status = "okay";

    gyroscope@0 {
    compatible = "nxp,fxas21002c";
    reg = <0x0>;

    spi-max-frequency = <2000000>;

    interrupt-parent = <&gpio3>;
    interrupts = <4 IRQ_TYPE_EDGE_RISING>;
    interrupt-names = "INT2";

    status = "okay";
    };
    };

    For detailed information about OS image customization, refer to the Device Tree Overlays Technical Overview article and the TorizonCore Builder documentation.

  5. Check if the overlay was successfully applied: Refer to Device Tree Overlays on Torizon**

  6. Confirm if the driver is active: run the lsmod command in your module terminal.

    For example:

    # lsmod | grep fxas21002c
    fxas21002c_spi 16384 0
    fxas21002c_core 24576 1 fxas21002c_spi
  7. Find the device driver's files: Torizon OS, as a Linux-based distribution, treats peripheral devices as files. SPI device files reside in the /sys/bus/spi/devices/ folder. For detailed information, see Overview of Linux kernel SPI support.

    For an FXAS21002C connected to the SPI_1 bus (accessible via the spi1.0 file), the sensor data is located in the /sys/bus/spi/devices/spi1.0/iio:device1/ folder, as presented in the following examples.

    Display the device driver:

    # readlink /sys/bus/spi/devices/spi1.0/driver
    ../../../../../../../../bus/spi/drivers/fxas21002c_spi

    Read the device name:

    # cat /sys/bus/spi/devices/spi1.0/iio\:device1/name
    fxas21002c

    Locate the sensor measurements:

    # ls /sys/bus/spi/devices/spi1.0/iio\:device1/
    buffer in_anglvel_filter_low_pass_3db_frequency_available in_anglvel_z_raw scan_elements
    buffer0 in_anglvel_sampling_frequency in_temp_raw subsystem
    dev in_anglvel_scale name trigger
    in_anglvel_filter_high_pass_3db_frequency in_anglvel_scale_available of_node uevent
    in_anglvel_filter_high_pass_3db_frequency_available in_anglvel_x_raw power
    in_anglvel_filter_low_pass_3db_frequency in_anglvel_y_raw sampling_frequency_available
    info

    The sensor measurements appear in the iio:deviceX folder because the FXAS21002C falls into the category of IIO devices.

  8. Integrate the driver into your application: without worrying about hardware details, you can use standard read/write functions of any programming language to communicate with the peripheral.

Container Access

The files in the /sys/bus/spi/devices folder are symbolic links to the actual device in /sys/devices/platform/<soc>/<bus-address>.bus/<spi-addres>.spi/spi_master/spiX/spiX.0. Mounting symbolic links results in a read-only filesystem issue. Therefore, use absolute paths of symbolic links, such as /sys/bus/spi/devices/spi1.0/iio:device1/.

tip

You can use the pwd -P command to see the absolute path of the device.

To access the files created by the driver within the container, you have to mount the directory into the container using the -v flag as follows:

# docker run --rm -it -v <device-absolute-path>:<container-path-destination> <container-image>
tip

The -v flag uses colon characters (:) to separate parameters. Therefore, if the folder's path contains :, add bind mounts or volumes using the --mount flag instead of using -v as follows:

docker run --rm -it --mount source='<device-absolute-path>',target='<container-path-destination>',type=bind  <container-image>

For comprehensive information, see Manage data in Docker.

Use Case Example

This section contains a short example of collecting FXAS21002C measurements from within a container after enabling its driver.

  1. Run a Debian Container for Torizon: Do not forget to mount the device data folder into the container.

    # docker run --rm -it --mount source=/sys/bus/spi/devices/spi1.0/iio:device1/,target=/fxas21002c,type=bind torizon/debian:${CT_TAG_DEBIAN}
  2. Once inside the container, locate the device driver files.

    ## ls /fxas21002c
    buffer in_anglvel_filter_low_pass_3db_frequency_available in_anglvel_z_raw scan_elements
    buffer0 in_anglvel_sampling_frequency in_temp_raw subsystem
    dev in_anglvel_scale name trigger
    in_anglvel_filter_high_pass_3db_frequency in_anglvel_scale_available of_node uevent
    in_anglvel_filter_high_pass_3db_frequency_available in_anglvel_x_raw power
    in_anglvel_filter_low_pass_3db_frequency in_anglvel_y_raw sampling_frequency_available
  3. Set up the sensor and collect data: The example below uses Linux terminal commands to interact with the device.

    Read the raw value of the gyroscope on the X-axis.

    ## cat /fxas21002c/in_anglvel_x_raw
    1598

    Get the available scale values. Then, change the current scale value.

    ## cat /fxas21002c/in_anglvel_scale
    62.5
    ## cat /fxas21002c/in_anglvel_scale
    62.5 31.25 15.625 7.8125
    ## echo 31.25 > /fxas21002c/in_anglvel_scale
    ## cat /fxas21002c/in_anglvel_scale
    31.25

    Read the new in_anglvel_x_raw value.

    ## cat /fxas21002c/in_anglvel_x_raw
    3168

    To convert the above value into a meaningful one, refer to your peripheral datasheet. For the FXAS21002C, the gyroscope value on the X-axis expressed in º/s is obtained by multiplying the in_anglvel_x_raw by in_anglvel_scale and then dividing the result by 1000:

    in_accel_x = (in_anglvel_x_raw * in_anglvel_scale)/1000
    in_accel_x = (3168 * 31.25)/1000 = 99 °/s

Peripheral Access from User Space

Without a driver exporting device data and operations into files, you can access the device through the Linux SPI interface, which is located in the /dev directory. When using this approach, the signals exchanged between your module and the peripheral are merely a sequence of bytes. Therefore, you will have to convert them into meaningful information, which requires understanding peripheral properties, such as read and write operations, register addresses, and pin functionalities.

Container Access

Containers run in an isolated environment and are not allowed to access any devices by default. To enable access to specific SPI buses within containers, Expose Hardware to Containers: identify the device files through which they are accessible, as described in the Locate SPI buses in the OS section, and then run the container using the --device flag.

The command below allows the container to access the SPI_1 bus of the Verdin iMX8M Plus, which is accessible through the spi1.0 device file.

# docker run -it --rm --device /dev/verdin-spi-cs0 <container_namespace>

You can also mount the /dev directory with the -v flag. Then, give the container access to all I²C buses with the --device_cgroup_rules flag:

# docker run -it --rm -v /dev:/dev --device-cgroup-rule='c 89:* rmw' <container_namespace>

For additional information regarding those flags, refer to the docker run and Docker run reference documentation.

Use Case Example

This section provides a step-by-step guide about a sample in C that configures and uses the SPI communication protocol through the SPI userspace API. Note that while the steps in this approach vary by languages/frameworks and use cases, they provide a general idea of working with peripherals in user space.

This sample uses the source code for testing the spidev present in the Linux Kernel. After running it, the final result that you are going to see at the debug terminal is expected to be the following:

...
spi mode: 0x0
bits per word: 8
max speed: 500000 Hz (500 kHz)
...

To use this sample, perform the following steps:

  1. Download the source code from GitHub: This application is part of the Toradex-provided Samples.

  2. Modify the source code to match the SPI interface you are using: The second commit shows the modifications made to a Torizon IDE Extension template to use the SPI device.

    For example, if using an Apalis iMX8 and using the SPI through the SPI1_MOSI, SPI1_MISO, SPI1_CS and SPI1_CLK signals, you would modify the code as follows:

    - static const char *device = "/dev/verdin-spi-cs0";
    + static const char *device = "/dev/apalis-spi-cs0";
  1. Build and run the container. On Torizon OS, there are two possible approaches:


Send Feedback!