## I2C Repeated Start

During an I2C transfer there is often the need to first send a command and then read back an answer right away. Because the I2C specification allows multiple devices to be masters on the I2C bus, this has to be done without the risk of another master device interrupting this atomic operation. The I2C protocol defines a so-called repeated start condition.

After having sent the address byte the I2C master may send any number of bytes followed by a single stop condition.

Using repeated starts, instead of sending the stop condition after sending data bytes it is also allowed to send another start condition again followed by an address and more data. Any number of repeated start conditions may be sent before sending a stop condition.

Repeated starts may be sent to one or more devices without releasing the bus which guarantees that the operation is not interrupted.

Perhaps because the Raspberry Pi was not intended to support a multimaster environment, the original device drivers for Raspberry Pi did not support transfers using repeated starts. This meant that devices that required repeated starts could not be used with the Raspberry Pi device drivers.

For this reason the module HiPi::BCM2835::I2C was useful as it allowed using the Raspberry Pi hardware directly to communicate with devices directly using repeated start conditions.

The Raspberry Pi I2C device driver has subsequently been updated and now supports repeated start transfers.

This is achieved by setting a module parameter. The module i2c_bcm2708 now accepts an additional parameter - combined - which controls how i2c write/read transfers are completed. This parameter is appled to all I2C transfers.

It is possible to set the 'combined' behaviour on and off even after the module has been loaded.

    To set combined transfers 'on'

    sudo sh -c '/bin/echo Y > /sys/module/i2c_bcm2708/parameters/combined'

    to set combined transfers 'off'

    sudo sh -c '/bin/echo N > /sys/module/i2c_bcm2708/parameters/combined'

Your setting will not survive a module reload and therefore will not survive a reboot.

There currently appears to be no simple way to provide this parameter using device tree settings but you can provide module options by writing a file to the directory `/etc/modprobe.d/`. The following line in this file will cause the i2c_bcm2708 to be loaded with the combined parameter set to 'on'

    options i2c_bcm2708 combined=1

You may call your file whatever you wish but I created it using:

    sudo sh -c 'echo options i2c_bcm2708 combined=1 > /etc/modprobe.d/i2c_repeated_start.conf'

After adding that file you can test correct operation by rebooting the Pi and checking that the parameter has been set to 'Y'.

    sudo cat /sys/module/i2c_bcm2708/parameters/combined

The module HiPi::Device::I2C now supports using the device driver for external devices that require repeated start transfers.

The module now accepts a parameter in its constructor that determines how a call to the method '$instance->bus_read' will be handled.

The parameter 'readmode' now accepts one of three values exported by HiPi::Constant

    use HiPi::Constant qw( :i2c_readmode );

    # imports constants

    # I2C_READMODE_SYSTEM
    # I2C_READMODE_START_STOP
    # I2C_READMODE_REPEATED_START

    my $dd1 = HiPi::Device::I2C->new(
        address  => 0x28,
        readmode => I2C_READMODE_SYSTEM
    );

    my $dd2 = HiPi::Device::I2C->new(
        address  => 0x28,
        readmode => I2C_READMODE_START_STOP
    );

    my $dd3 = HiPi::Device::I2C->new(
        address  => 0x28,
        readmode => I2C_READMODE_REPEATED_START
    );

##### I2C_READMODE_SYSTEM

This is the default mode. In this mode calls to $dd->bus_read() will do a simple write/read to the device driver. This means that the transfer will take place according to the i2c_bcm2708 setting for combined.

##### I2C_READMODE_START_STOP

In this mode, if combine mode is switched on in the i2c_bcm2708 module, calls to $dd->bus_read() will attempt to set combine mode off just for this transfer.

The method reads /sys/module/i2c_bcm2708/parameters/combined first. If that is set to 'Y', the method will write 'N' to that file before starting the transfer, and will write 'Y' back to the file after the transfer thereby restoring it to its previous state. This should mean that the transfer takes place without repeated startscontary to the current i2c_bcm2708 setting.

##### I2C_READMODE_REPEATED_START

In this mode, if combine mode is switched off in the i2c_bcm2708 module, calls to $dd->bus_read() will attempt to set combine mode on just for this transfer.

The method reads /sys/module/i2c_bcm2708/parameters/combined first. If that is set to 'N', the method will write 'Y' to that file before starting the transfer, and will write 'N' back to the file after the transfer thereby restoring it to its previous state. This should mean that the transfer takes place with repeated starts contary to the current i2c_bcm2708 setting.

## Potential Problems

Setting the device driver mode to 'combined' may have unforeseeen consequences. Processes that use the i2c device driver that previously worked well may stop working when the setting is changed.

Using the readmodes I2C_READMODE_START_STOP and I2C_READMODE_REPEATED_START will cause problems if more that one script at a time uses the $(HiPi::Device)->bus_write() method. In such a case it seems highly probable that setting / unsetting of the /sys/module/i2c_bcm2708/parameters/combined value will become unsynchronised.

I may add some locking within the bus_read method in the future but for now will wait for any feedback on any real world problems caused.

HiPi Modules Copyright © 2013 - 2016 Mark Dootson