How to use the I2C ports

This article illustrates how to use the I2C on Acme Systems Linux boards based on Microchip SAMG25, SAMA5D3x and SAMD2x CPUs

Pinout of I2C ports

This is the list of I2C ports implemented in hardware. Any other GPIO pin can be used as I2C in bitbanging mode.

Signal Dir Description Roadrunner Acqua Aria G25 Arietta
TWD0 I/O Serial data PD21/PD29 J3.42 (PA30) W17 (PA30) J4.14 (PA30)
TWCK0 O Clock PD22/PD30 J3.41 (PA31) W18 (PA31) J4.12 (PA31)
TWD1 I/O Serial data PC6/PD19/PD4 J3.26 (PC26) N2 (PC0) J4.39 (PC0)
TWCK1 O Clock PC8/PD20/PD5 J3.28 (PA27) N3 (PC1) J4.37 (PC1)
TWD2 I/O Serial data J3.36 (PA18)
TWCK2 O Clock J3.41 (PA31)

Linux Kernel modules to activate

Probably the Kernel used on your Acme Board is already configured to have the I2C bus enabled. To check it just type:

ls /dev/i2c*
/dev/i2c-0  /dev/i2c-1

in this case two busses are already configured on your board. To know on which pins are available the I2C signals, read the pinout section of this page.

However if no device is present you have to check the Kernel drivers setup and the device tree contents.

Follow this tutorial: Compile the Linux Kernel from sources to know how to cross compile the Linux Kernel and how to configure the drivers to enable inside it.

Inside the make menuconfig enable the following items:

Device drivers --->
        I2C support  --->
            <*> I2C support
                [*]   Enable compatibility bits for old user-space
                <*>   I2C device interface
                <*>   I2C bus multiplexing support
                        Multiplexer I2C Chip support  --->
                [*]   Autoselect pertinent helper modules
                        I2C Hardware Bus support  --->
                            <*> Atmel AT91 I2C Two-Wire interface (TWI)
                            <*> GPIO-based bitbanging I2C

then compile the Kernel image, save it on the microsd and reboot.

Device tree configurations

Roadrunner

i2c0: i2c@f8028000 {
    status ="okay";
}

Acqua

i2c0: i2c@f8010000 {
    status ="okay";
};

i2c1: i2c@f8014000 {
    status ="okay";
};

i2c2: i2c@f801c000 {
    status = "okay";
};

Aria & Arietta

i2c0: i2c@f8010000 {
    status ="okay";
};

i2c1: i2c@f8014000 {
    status ="okay";
};

Using I2C from user space

i2c-tools

The faster way to do the first experiments with this board is by installing and using the i2c-tools.

i2c-tools is a package contains a heterogeneous set of I2C tools for Linux such as:

  • a bus probing tool
  • a chip dumper
  • a register-level access helpers
  • an EEPROM decoding scripts
  • ...and more

To install i2c-tools on the FOX Board just type:

apt-get update
apt-get install i2c-tools

Using i2cdetect

i2cdetect is an userspace program to scan an I2C bus for devices. It outputs a table with the list of detected devices on the specified bus.

Example of use:

i2cdetect -y 0

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f                             
00:          -- -- -- -- -- -- -- -- -- -- -- -- --                             
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --                             
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --                             
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --                             
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --                             
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --                             
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --                             
70: -- -- -- -- -- -- -- --                                                     

In this case a device has been detected on address 20 hex.

i2cset and i2cset

i2cset is a small helper program to set registers visible through the I2C bus.

The follow simple command writes the byte value 255 to the I2C device at address 20 hex on the i2c bus 0 (/dev/i2c-0).

i2cset -y 0 0x20 255

If for example you are using a DAISY-22 module with a PCF8574 I2C I/O expander this command will set all the GPIO lines to 1.

i2cget i2cget is a small helper program to read registers visible through the I2C bus.

The follow simple command read a byte from an I2C device at address 20 hex on the i2c bus 0 (/dev/i2c-0).

i2cget -y 0 0x20 0x01

</div>

<div class="tab-pane fade in" id="code_python" markdown="1">

Python code example

python-smbus is a Python module allows SMBus access through the I2C /dev interface on Linux hosts. The host kernel must have I2C support, I2C device interface support, and a bus adapter driver.

Write a register

The following example sends a sequence of values from 0 to 255 to the PCF8574 I2C I/O expander at address 0x20.

#include <stdio.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <errno.h>

#define I2C_ADDR 0x20

int main (void) {
    int value;
    int fd;

    fd = open("/dev/i2c-0", O_RDWR);

    if (fd < 0) {
        printf("Error opening file: %s\n", strerror(errno));
        return 1;
    }

    if (ioctl(fd, I2C_SLAVE, I2C_ADDR) < 0) {
        printf("ioctl error: %s\n", strerror(errno));
        return 1;
    }

    for (value=0; value<=255; value++) {
        if (write(fd, &value, 1) != 1) {
            printf("Error writing file: %s\n", strerror(errno));
        }
        usleep(100000);
    }
    return 0;
}

Run it by typing:

python write.py

Read a register

The following read the GPIO status of a PCF8574 I2C I/O expander at address 0x20. Note that we have to write 1 on the input line we want to read.

import smbus

I2C_ADDRESS = 0x20 

bus = smbus.SMBus(0)

#Set all ports in input mode
bus.write_byte(I2C_ADDRESS,0xFF)

#Read all the unput lines
value=bus.read_byte(I2C_ADDRESS)
print "%02X" % value

Run it by typing:

python read.py

C code example

Write a register

The following example sends a sequence of values from 0 to 255 to the PCF8574 I2C I/O expander at address 0x20 in C language.

#include <stdio.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <errno.h>

#define I2C_ADDR 0x20

int main (void) {
    int value;
    int fd;

    fd = open("/dev/i2c-0", O_RDWR);

    if (fd < 0) {
        printf("Error opening file: %s\n", strerror(errno));
        return 1;
    }

    if (ioctl(fd, I2C_SLAVE, I2C_ADDR) < 0) {
        printf("ioctl error: %s\n", strerror(errno));
        return 1;
    }

    for (value=0; value<=255; value++) {
        if (write(fd, &value, 1) != 1) {
            printf("Error writing file: %s\n", strerror(errno));
        }
        usleep(100000);
    }
    return 0;
}

Compile it by typing:

gcc write.c -o write

Then launch it:

./write

Read a register

The following example get the state of the GPIO line from a PCF8574 I2C I/O expander at address 0x20.

#include <stdio.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <errno.h>

#define I2C_ADDR 0x20

int main (void) {
    char buffer[1];
    int fd;

    fd = open("/dev/i2c-0", O_RDWR);

    if (fd < 0) {
        printf("Error opening file: %s\n", strerror(errno));
        return 1;
    }

    if (ioctl(fd, I2C_SLAVE, I2C_ADDR) < 0) {
        printf("ioctl error: %s\n", strerror(errno));
        return 1;
    }

    buffer[0]=0xFF;
    write(fd, buffer, 1);

    read(fd, buffer, 1);
    printf("0x%02X\n", buffer[0]);
    return 0;
}

Compile it by typing:

gcc read.c -o read

Then launch it:

./read

Examples

Links

Sergio Tanzilli
System designer, software developer and company co-founder
tanzilli@acmesystems.it
Webpages: https://www.acmesystems.it and http://www.tanzolab.it
Github repository: https://github.com/tanzilli and https://github.com/acmesystems