Applications - I²C

Introduction

This article shows how the I²C bus functionality of theSAMA5D2 Series ARM® Cortex®-A5 Microprocessor Unit (MPU) is enabled in the Linux® kernel and how to access the I²C bus in user space.

Usually, I²C devices are controlled by a kernel driver. But it is also possible to access all devices on an adapter from user space through the I²C dev interface. I²C dev is a character device node file. It can be accessed byread(),write(), andioctl(). The interface of each I²C bus can be exported to user space through its own I²C dev device node.


Prerequisites

This application is developed for the ATSAMA5D27-SOM1-EK1 development platform:

This application is developed using the Buildroot build system.


Hardware

For this application, you will be controlling the I²C bus of themikroBUS 1 expansion socket of the ATSAMA5D27-SOM1-EK1. The figure below shows the expansion capability of the SOM1-EK1.

ATSAMA5D27_SOM1_EK1_expansion_01.png

The ATSAMA5D27 SOM1 contains five Flexible Serial Communications Controller (FLEXCOM) peripherals to provide serial communications protocols: USART, SPI, and TWI.

You will control pin PD23 and PA24 from the ATSAMA5D27 SOM1 which connects to J25 pins 5 and 6 of the mikroBUS 1 connector (labeled TWCK_mBUS1 and TWD_mBUS1 on the schematic).

mikroBUS pinSchematic NameFLEXCOM I/OPackage Pin
J25 pin 5TWCK_mBUS1FLEXCOM1_IO1PA23
J25 pin 6TWD_mBUS1FLEXCOM1_IO0PA24

For more details of the Package and Pinout of the SAMA5D2, refer to Table 6-2. Pinouts in SAMA5D2 Series Data Sheet.

mikrobus1_R1.png

THERMO 5 click

For this demonstration, we will use theMikroe THERMO 5 click board. The THERMO 5 click measures temperature in default range of 0°C to 127°C and in an extended range of -64°C to 191°C with ±1°C accuracy. The THERMO 5 click features theMicrochip EMC1414 temperature sensor.

thermo-5-click.png

Insert theTHERMO 5 click into themikroBUS 1 slot on the ATSAMA5D27-SOM1-EK1 as shown in the figure below:

thermo-5-click-installed.png

Buildroot Configuration

Objective: Using Buildroot, build a bootable image and FLASH onto an SD Memory Card for the ATSAMA5D27-SOM1-EK1 development board.

Follow the steps for building the image in the articleCreate Project with Default Configuration. You will use the default configuration file:atmel_sama5d27_som1_ek_mmc_dev_defconfig.


Device Tree

Objective: Observe how the FLEXCOM1 peripheral was configured for I²C in the device tree. No changes are required.

Once Buildroot has completed its build, the I²C definitions for the ATSAMA5D27-SOM1-EK1 were configured by a device tree. The device tree source includes files (*.dtsi and*.dts) located in the Buildroot output directory:/output/build/linux-linux4sam_6.0/arch/arm/boot/dts/.

1

Examine thesama5d2.dtsi file and observe the FLEXCOM1 device assignments:

679   flx1_clk: flx1_clk {680      #clock-cells = <0>;681      reg = <20>;682      atmel,clk-output-range = <0 83000000>;683   };..1255   flx1: flexcom@f8038000 {1256     compatible = "atmel,sama5d2-flexcom";1257     reg = <0xf8038000 0x200>;1258     clocks = <&flx1_clk>;1259     #address-cells = <1>;1260     #size-cells = <1>;1261     ranges = <0x0 0xf8038000 0x800>;1262     status = "disabled";1263   };

Line 681 the PID for FLEXCOM1 is 20, this definition of the offset will be used to enable FLEXCOM1 clock in PMC.

Line 682 the FLEXCOM1 input clock, max frequency is 83 MHz.

Line 1256 specifies which driver will be used for the FLEXCOM peripheral.

Line 1257 the FLEXCOM1 base address is 0xf8038000, size is 0x200.

Line 1258 is the definition for the FLEXCOM1 clock source.

Line 1262 shows the default is “disabled” and will be changed to “okay” in line 195 of theat91-sama5d27_som1_ek.dts file below.

2

Examine theat91-sama5d27_som1_ek.dts file and observe the I²C device assignments:

56   aliases {57      serial0 = &uart1;    /* DBGU */58      serial1 = &uart4;    /* mikro BUS 1 */59      serial2 = &uart2;    /* mikro BUS 2 */60      i2c1    = &i2c1;61      i2c2    = &i2c2;62   };..179  flx1: flexcom@f8038000 {180     atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_TWI>;181     status = "okay";182183     i2c2: i2c@600 {184        compatible = "atmel,sama5d2-i2c";185        reg = <0x600 0x200>;186        interrupts = <20 IRQ_TYPE_LEVEL_HIGH 7>;187        dmas = <0>, <0>;188        dma-names = "tx", "rx";189        #address-cells = <1>;190        #size-cells = <0>;191        clocks = <&flx1_clk>;192        pinctrl-names = "default";193        pinctrl-0 = <&pinctrl_mikrobus_i2c>;194        atmel,fifo-size = <16>;195        status = "okay";196     };197  };..522  pinctrl_mikrobus_i2c: mikrobus1_i2c {523     pinmux = <PIN_PA24__FLEXCOM1_IO0>,524        <PIN_PA23__FLEXCOM1_IO1>;525     bias-disable;526  };

Line 61, the aliases of FLEXCOM1 I²C is i2c2. FLEXCOM1 I²C will be registered as I²C adapter 2.

Line 180 specifies the I²C mode for the FLEXCOM1 peripheral.

Line 181 enables the device.

Line 184 specifies which driver will be used for this I²C device.

Line 185 is the register offset address for I²C in FLEXCOM1 is 0x600, size is 0x200.

Line 186 the PID for FLEXCOM4 is 23, high level triggered, priority is 7. It is used to configure FLEXCOM4 interrupt in AIC.

Line 187 shows that the DMA feature is not enabled.

Line 191 is the definition for the FLEXCOM1 clock source

Line 193 is the pin definition for I²C.

Line 194 specifies the size of the FIFO as 16.

Line 195 enables the device.

Line 523 is the mux of PA24 to be switched to FLEXCOM1_IO0.

Line 524 is the mux of PA23 to be switched to FLEXCOM1_IO1

Line 525 the pull up/down feature is disabled.


Kernel

Objective: Observe how the I²C functionality was configured in the Linux kernel. No changes are required.

1

From the buildroot directory, run the Linux kernel menuconfig:

$make linux-menuconfig

The top-level menu will be displayed:

linux-config-top-level.png

Device Driver

2

SelectDevice Drivers —->

linux-config-device-drivers.png

3

SelectI²C Support —->

i2c-support.png

4

Observe<*> I²C device interface is selected.

This will select the driver for the I²C_CHARDEV device.

5

SelectI²C Hardware Bus support —->

i2c-hardware-bus-support.png

6

Observe<*> Atmel AT91 I²C Two-Wire interface (TWI) is selected.

This supports the use of the I²C interface on Microchip (formerly Atmel) AT91 processors.


Rootfs

There are two methods to access I²C bus driver:

Kernel Space:
Register your own I²C driver viai2c_add_driver() interface, then access I²C bus driver via structi2c_client handle.

User Space:
As discussed in the "Device Tree" section above, the FLEXCOM1 peripheral is registered as I²C adapter 2. Enabling the I²C_CHARDEV kernel feature (default) you can access the I²C bus driver via/dev/i2c-2 device node.

I²C_CHARDEV is a good choice because all codes are running in user space (it’s easier for developing).


Application

The following is a C-Language demonstration program (i2c_dev.c) for reading temperature from the THERMO 5 click board via the/dev/i2c-2 node.

To compile:

$buildroot/output/host/bin/arm-buildroot-linux-uclibcgnueabihf-gcc i2c_dev.c -o i2c_test

Be sure to type in the location of the cross-compiler on your host computer.

Source code:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <sys/ioctl.h>#include <linux/i2c.h>#include <linux/i2c-dev.h>#define DEV_I²C "/dev/i2c-2"#define SLAVE_ADDR 0x4C /* EMC1414 I2C slave address *///#define COMBINED_TRANSCTIONint main(int argc, char *argv[]){    int fd;    int ret;    unsigned char buf[2];    // open device node    fd = open(DEV_I2C, O_RDWR);    if (fd < 0) {        printf("ERROR open %s ret=%d\n", DEV_I2C, fd);        return -1;    }    if (ioctl(fd, I2C_SLAVE, SLAVE_ADDR) < 0) {        printf("ERROR ioctl() set slave address\n");        return -1;    }#ifdef COMBINED_TRANSCTION    struct i2c_rdwr_ioctl_data data;    struct i2c_msg messages[2];    // Set conversion rate    buf[0] = 0x04; // Conversion rate register address    buf[1] = 0x04; // Set conversion rate to 1 second    messages[0].addr  = SLAVE_ADDR; //device address    messages[0].flags = 0; //write    messages[0].len   = 2;    messages[0].buf   = buf; //data address    data.msgs  = &messages[0];    data.nmsgs = 1;    if (ioctl(fd, I2C_RDWR, &data) < 0) {        printf("ERROR ioctl() conversion rate\n");        return -1;    }    // Read temperature    buf[0] = 0x00; // Internal Diode High Byte register address    buf[1] = 0;    // clear receive buffer    messages[0].addr  = SLAVE_ADDR; //device address    messages[0].flags = 0; //write    messages[0].len   = 1;    messages[0].buf   = &buf[0]; //data address    messages[1].addr  = SLAVE_ADDR; //device address    messages[1].flags = I2C_M_RD; //read    messages[1].len   = 1;    messages[1].buf   = &buf[1];    data.msgs  = messages;    data.nmsgs = 2;    while (1) {        if (ioctl(fd, I2C_RDWR, &data) < 0) {            printf("ERROR ioctl() read data\n");            return -1;        }        printf("Temperature is %d\n", buf[1]);        sleep(1);    }#else    // Set conversion rate    buf[0] = 0x04; // Conversion rate register address    buf[1] = 0x04; // Set conversion rate to 1 second    ret = write(fd, buf, 2);    if (ret != 2) {        printf("ERROR write() conversion rate\n");        return -1;    }    // Read temperature    // Set internal address register pointer    buf[0] = 0x00; // Internal Diode High Byte register address    ret = write(fd, &buf[0], 1);    if (ret != 1) {        printf("ERROR write() register address\n");        return -1;    }    while (1) {        // Read temperature        // Read data        buf[1] = 0; // clear receive buffer        ret = read(fd, &buf[1], 1);        if (ret != 1) {            printf("ERROR read() data\n");            return -1;        }        printf("Temperature is %d\n", buf[1]);        sleep(1);    }#endif    // close device node    close(fd);    return 0;}

Hands-On

Copy thei2c_test program to the target and execute. The temperature data will be printed out.

# chmod +x i2c_test# ./i2c_testTemperature is 26Temperature is 26Temperature is 26Temperature is 26Temperature is 26Temperature is 26

Tools and Utilities

I2C Tools is a tool for I2C bus testing. It is included in the default configuration of Buildroot. You can view the selection by performing the following:

1

From the Buildroot directory, start menuconfig:

$make menuconfig

2

SelectTarget packages ——>

3

SelectHardware handling ——>

4

Observe[*] i2c-tools is selected.

hardware-handling.png

i2c-tools Commands

There are several commands ini2c-tools:

# i2cdetectError: No i2c-bus specified!Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]       i2cdetect -F I2CBUS       i2cdetect -l  I2CBUS is an integer or an I2C bus name  If provided, FIRST and LAST limit the probing range.# i2cdetect -y 3     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: UU 51 52 53 54 55 56 57 -- -- -- -- -- -- -- -- 60: 60 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
# i2cdumpError: No i2c-bus specified!Usage: i2cdump [-f] [-y] [-r first-last] I2CBUS ADDRESS [MODE [BANK [BANKREG]]]  I2CBUS is an integer or an I2C bus name  ADDRESS is an integer (0x03 - 0x77)  MODE is one of:    b (byte, default)    w (word)    W (word on even register addresses)    s (SMBus block)    i (I2C block)    c (consecutive byte)    Append p for SMBus PEC# i2cdump -f -y 3 0x50No size specified (using byte-data access)     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................10: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................60: 20 4d 43 48 49 50 20 52 46 4f 12 02 42 42 31 ff     MCHIP RFO??BB1.70: 00 00 44 32 37 2d 53 4f 4d 31 2d 45 4b 31 44 f7    ..D27-SOM1-EK1D?80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
# i2cgetUsage: i2cget [-f] [-y] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]  I2CBUS is an integer or an I2C bus name  ADDRESS is an integer (0x03 - 0x77)  MODE is one of:    b (read byte data, default)    w (read word data)    c (write byte/read byte)Append p for SMBus PEC# i2cget -f -y 3 0x50 0x600x20# i2cget -f -y 3 0x50 0x610x4d

WARNING: for the i2cset command,DO NOT modify any data in the EEPROM of the ATSAMA5D27-SOM1-EK1.

# i2csetUsage: i2cset [-f] [-y] [-m MASK] [-r] I2CBUS CHIP-ADDRESS DATA-ADDRESS [VALUE] ... [MODE]  I2CBUS is an integer or an I2C bus name  ADDRESS is an integer (0x03 - 0x77)  MODE is one of:    c (byte, no value)    b (byte data, default)    w (word data)    i (I2C block data)    s (SMBus block data)Append p for SMBus PEC
# i2ctransferUsage: i2ctransfer [-f] [-y] [-v] [-V] I2CBUS DESC [DATA] [DESC [DATA]]...  I2CBUS is an integer or an I2C bus name  DESC describes the transfer in the form: {r|w}LENGTH[@address]    1) read/write-flag 2) LENGTH (range 0-65535) 3) I2C address (use last one if omitted)  DATA are LENGTH bytes for a write message. They can be shortened by a suffix:    = (keep value constant until LENGTH)    + (increase value by 1 until LENGTH)    - (decrease value by 1 until LENGTH)    p (use pseudo random generator until LENGTH with value as seed)Example (bus 0, read 8 byte at offset 0x64 from EEPROM at 0x50):# i2ctransfer 0 w1@0x50 0x64 r8Example (same EEPROM, at offset 0x42 write 0xff 0xfe ... 0xf0):# i2ctransfer 0 w17@0x50 0x42 0xff-

Summary

You used Buildroot to build an image with I²C bus support for the ATSAMA5D2 Series MPU. You accessed the I²C bus via two different methods: kernel space usingi2c_add_driver() interface and user space by the/dev/i2c-X device. You walked through the device tree and kernel to observe how the embedded Linux system configures the source code for building.