I2C Bus API

From Nutwiki
Revision as of 21:07, 3 December 2012 by Harald (Talk | contribs) (Slave Configuration)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Introduction

Starting with Nut/OS 5.0.6 a new API has been implemented for I2C support. The problem with the previous API was, that only one type of I2C interface could be used by an application. For example, there was no chance to mix bit banging drivers with I2C hardware based drivers in a single application.

The new API presented here is not very well tested and at the time of this writing it has been implemented for the AT91 only and tested on Ethernut 5 only. And, there's only one device driver available, which makes use of this API and supports the PCF8563 RTC chip.

Application Usage

I2C Device Registration

As usual, an application that wants to make use of a specific device driver needs to register it first. The following call is used for I2C slave devices:

NutRegisterI2cSlave(<slave_driver>, <bus_driver>);

Beside doing all the required interface initialization, this call attaches the given slave to the specified bus.

Right now only one slave and one bus driver is available. The bus driver is referenced by the structure variable i2cBus0At91.

The slave driver is a bit more complicated, because it is typically embedded into another device, in our case in the RTC (realtime clock) driver used to keep track of the current date and time. This RTC driver is referenced by the structure variable rtcI2cPcf85xx, which contains a reference to the I2C slave driver in the dcb member of this structure. Thus, the call to register the slave driver and attach it to a given bus is

NutRegisterI2cSlave(rtcI2cPcf85xx.dcb, &i2cBus0At91);

Note, that this call registers the I2C part only. It is still required to register the RTC driver itself, so Nut/OS knows where to get the current date and time information from. This is done by calling

NutRegisterRtc(&rtcI2cPcf85xx);

This is the same call that is used by older drivers using the legacy I2C API.

I2C Bus Probing

This is a new feature, which hadn't been available in the legacy API. The related API call is

NutI2cBusScan(<bus_driver>, <start_sla>, <end_sla>);

It scans the bus for a given range of slave addresses and, if a device is found, returns the discovered address. If none was found, it returns I2C_SLA_NONE instead.

The following routine can be used to scan the entire bus.

<source lang="c"> void ScanBus(NUTI2C_BUS *bus) {

   int sla = 0;
   puts("Scanning I2C bus");
   for (;;) {
       sla = NutI2cBusScan(bus, sla, 127);
       if (sla == I2C_SLA_NONE) {
           break;
       }
       printf("%3d (0x%02x) detected\n", sla, sla);
       sla++;
   }

} </source>

To scan the first I2C bus connected to an AT91 CPU, you could call

ScanBus(&i2cBus0At91);

Bus Configuration

There are currently two configurable items, bus clock rate and bus access timeout.

While the first is set with

NutI2cBusRate(<bus_driver>, <frequency>);

the latter is set with

NutI2cBusTimeout(<bus_driver>, <milliseconds>);

Setting the bus clock rate is quite obvious, the clock frequency is specified in Hertz. By default the rate is set to 100kHz, which is specified in the original I2C specification and should work with the majority of I2C chips. When passing a frequency of zero, then the clock is reset to the mentioned default.

To set the rate to 400kHz, call

NutI2cBusRate(&i2cBus0At91, 400000);

The bus driver may be used concurrently by any number of threads, but of course only one thread can access the bus at a time. The timeout is used when a thread tries to access the bus, which is currently in use by another thread. If access isn't granted within the specified number of milliseconds, then the related function will return an error.

NutI2cBusTimeout(&i2cBus0At91, 1);

sets the timeout to one millisecond, which is the minimum. As you may already know, the value of zero is used by Nut/OS to disable timeout monitoring.

Both function return the previously set value. If you simply want to query the current setting, you can pass a special value, I2C_CURRENT_RATE for the clock and I2C_CURRENT_TIMEOUT as the timeout value.

For example, the following code fragment will set the rate to 400kHz and then print out the currently set value.

<source lang="c"> if (NutI2cBusRate(&i2cBus0At91, 400000) == I2C_CURRENT_RATE) {

   puts("Error");

} printf("%ld bps\n", NutI2cBusRate(&i2cBus0At91, I2C_CURRENT_RATE)); </source>

Also note, that NutI2cBusRate() returns I2C_CURRENT_RATE instead of the previously set value if the requested clock rate is beyond the capabilities of the hardware. In this case the clock rate will not change. In contrast, NutI2cBusTimeout() will accept all values and therefore always return the previously set timeout.

Slave Configuration

Two configurable items are related to a single I2C slave, the slave address and the response timeout.

Usually there is no need to change the slave address, but some chips do have a configurable address, which may differ from the default address that is hard coded in the driver.

NutI2cSlaveAddress(<slave_driver>, <slave_address>);

To avoid any confusion, this will set the address in the driver, used to access the hardware chip. While the chips actual address is typically set via specific pins, possibly connected to jumpers.

The second item, the response timeout can be changed by calling

NutI2cSlaveTimeout(<slave_driver>, <milliseconds>);

If the chip will not respond within the given time, the related function will return an error.