SBCX-TN-001: Android Things and SPI bus

From DAVE Developer's Wiki
Jump to: navigation, search
Info Box
SBC-AXEL-02.png Applies to SBC AXEL
Axel-04.png Applies to Axel Ultra
Axel-lite 02.png Applies to Axel Lite
Axel-02.png Applies to AXEL ESATTA

History

Version Date Notes
1.0.0 April 2018 First public release

Introduction

When using Android for embedded platforms, software developers have to face several challenges. Most of them are due to the fact that Android was designed originally to run on mobile devices which meet specific hardware requirements. As such, the APIs to access the hardware interfaces from the application layer are defined accordingly.

These interfaces don't include the ones that are typically used in products addressing industrial applications: I2C bus, SPI bus, UARTs, etc. However, as Android popularity is growing outside the mobile world too, Google defined a set of additional APIs to standardize the access to these interfaces as well. These new APIs are part of the Android Things project.

This Technical Note describes the implementation of the APIs related to the SPI bus to access the bus SPI4 of the SBCX platform in the form of a native library. The solution was tested with Android 4.4.3.

Testbed

As stated in the introduction, the solution has been tested on a platform running Android 4.4.3. From the software standpoint, this platform is a good example of an implementation where significant changes have been done to the Android Open Source Project (AOSP) in order to use it on an industrial embedded device.

Some of these changes modify the Android security model. In many cases, this is acceptable because the overall security of the product is not compromised (for instance, many devices used for industrial applications are not connected to the Internet and/or the physical access is granted to authorized personnel only). In general, system integrators have to assess the impact of such changes to verify that the resulting configuration meets the system requirements.

With regard to the platform here considered, the following is a not exhaustive list of the changes with respect to the standard/typical Android configuration used on mobile devices:

  • As the non-volatile storage device is a NAND flash, UBIFS file system has been used
  • There is only one partition
  • The file system is read/write; as such, the system directory is writeable as well
  • A serial console is available on a UART port; root login is allowed on this console
  • U-Boot has been used as bootloader
  • Some of the device files are directly accessible from the application layer.

Development has been performed on the following hardware platform:

  • Board: SBC-AXEL equipped with AxelLite SOM
  • SOC: NXP i.MX6 DualLite
  • Android Version: 4.4.3 (Jelly Bean).

To verify the functionality of the library, a simple device (serial NOR flash memory) has been connected to the expansion connector (J33). Specifically, the SPI4 bus has been used. To build the example application using the library, the following version Android Studio has been used:

Android Studio 3.1
Build #AI-173.4670197, built on March 22, 2018
JRE: 1.8.0_152-release-1024-b02 amd64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Android SDK API 27

Wiring

Simplyfied diagram of the serial flash memory interfacing

As depicted in the previous image, the serial flash memory has been connected to the SPI4 bus by using SBCX's expansion connector (J33).

The following table details the signals that have been used.

Nor Name Signal name Pin #
1 S# SPI4_SS0_R 10
2 DQ1 SPI4_MOSI_R 6
3 W# 3v3 48
4 Vss GND 2
5 DQ0 SPI4_MOSI_R 8
6 C SPI4_SCLK_R 4
7 HOLD# 3v3 48
8 Vcc 3v3 48

Software implementation

The following picture shows the organization of the software layers.

Software stack

The solution depicted on the left is fully compliant with the Android/Android Things recommendations. The access to the I/O interface—the SPI bus in this case—is managed by the Peripheral Manager and by a specific service.

The solution depicted on the right is the one described in this Technical Note. From the application point of view, it is consistent with the Android Things APIs. However, it is easier to implement thanks to the direct access to the device file associated with the SPI bus.

Requirements

To implement such solution, the following requirements must be met:

  • The Linux kernel must support spidev
  • The device file must have the permissions as shown in this section.
  • The device tree must be modified in order to instantiate the SPI bus to be accessed: it is enough to enable the escpi4 peripheral into SBCX device tree
&ecspi4 {
	status = "okay";
};

Example project

An example project is provided to illustrate how to use the native library. It includes the library itself and a simple application which reads the device identification data of the flash memory (READ IDENTIFICATION command).

Test application

Managing the device connection

In order to open a connection to a particular SPI slave, you need to know the unique name of the bus. The get this name, run the following command from the serial console:

root@sbcx:/ # ls /dev/spi* -al
crw-rw-rw- system   root     153,   0 1970-01-01 00:00 spidev32765.0

It is important to note that the above method is the only one available to retrieve the SPI device name because getSpiBusList() function is not implemented within the library.

Once this name is known, the APIs defined by Android Things can be used to access the bus because the library—named eu.dave.spi_dev—is consistent with them:

public class HomeActivity extends Activity {
    // SPI Device Name
    private static final String SPI_DEVICE_NAME = "/dev/spidev32765.0";

    private SpiDevice mDevice;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mDevice = new SpiDevice(SPI_DEVICE_NAME);
        // Attempt to access the SPI device
        try {
           mDevice.Open();
        } catch (IOException e) {
             Log.w("Error", "Unable to access SPI device" + e.getMessage());
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (mDevice != null) {
            try {
                mDevice.Close();
                mDevice = null;
            } catch (IOException e) {
                Log.w("Error","Unable to close SPI device" + e.getMessage());
            }
        }
    }
}

Configuring clock and data modes

Configuration method are identical to the Andriod Things api ref.

public void configureSpiDevice(SpiDevice device) throws IOException {
    // Low clock, leading edge transfer
    device.setMode(SpiDevice.MODE0);

    device.setFrequency(500000);     // 16MHz
    device.setBitsPerWord(8);          // 8 BPW
    device.setBitJustification(false); // MSB first
}

Transferring data

Is important to note that async I/O are not supported in userspace at this time ref, furthermore LSB mode is not supported by the i.MX6 processor ref.

To communicate with the device the following methods are available

  • void WriteByte(byte symbol): to transfer a byte without reading the response from the device.
  • void WriteBuffer(byte[] data, int len): to transfer a buffer to the device without processing the response.
  • void Transfer(byte[] tx_data, byte[] rx_data, int len): to transfer a buffer smaller than the length of the receiving one.
  • void Transfer(byte[] tx_data, byte[] rx_data) : to transfer and read a buffer with the same length.
  • void Transfer(byte symbol, byte[] rx_data, int len): to transfer a byte and read a buffer of length len.
public void sendCommand(SpiDevice device, byte[] buffer) throws IOException {
    byte[] response = new byte[buffer.length];
    device.Transfer(buffer, response, buffer.length);
    ...
}

Downloads

The example project is available for download at this link.