How to use the I2C ports

This article illustrates how to use the I2C on Acme Systems Linux boards based on Microchip SAMG25 (Aria, Arietta) and SAMA5D3x (Acqua) CPUs

For the RoadRunner SOM (SAMA5D27) please read this article.

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 Acqua Aria G25 Arietta
TWD0 I/O Serial data J3.42 (PA30) W17 (PA30) J4.14 (PA30)
TWCK0 O Clock J3.41 (PA31) W18 (PA31) J4.12 (PA31)
TWD1 I/O Serial data J3.26 (PC26) N2 (PC0) J4.39 (PC0)
TWCK1 O Clock 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: 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.

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:

sudo apt update
sudo apt 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:

sudo 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).

sudo 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).

sudo i2cget -y 0 0x20 0x01

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.

This example will be available as soon as possible

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>
#include <string.h> 
#include <sys/ioctl.h> 
#include <unistd.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>
#include <string.h> 
#include <sys/ioctl.h> 
#include <unistd.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
Systems designer, webmaster of www.acmesystems.it and founder of Acme Systems srl

Personal email: tanzilli@acmesystems.it
Web pages: https://www.acmesystems.it --- https://www.acmestudio.it
Github repositories: https://github.com/tanzilli --- https://github.com/acmesystems
Telegram group dedicated to the Acme Systems boards: https://t.me/acmesystemssrl