OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [io/] [i2c/] [current/] [doc/] [i2c.sgml] - Rev 845

Go to most recent revision | Compare with Previous | Blame | View Log

<!-- DOCTYPE part  PUBLIC "-//OASIS//DTD DocBook V3.1//EN" -->

<!-- {{{ Banner                         -->

<!-- =============================================================== -->
<!--                                                                 -->
<!--     i2c.sgml                                                    -->
<!--                                                                 -->
<!--     Generic I2C documentation.                                  -->
<!--                                                                 -->
<!-- =============================================================== -->
<!-- ####ECOSDOCCOPYRIGHTBEGIN####                                   -->
<!-- =============================================================== -->
<!-- Copyright (C) 2004, 2009 Free Software Foundation, Inc.         -->
<!-- This material may be distributed only subject to the terms      -->
<!-- and conditions set forth in the Open Publication License, v1.0  -->
<!-- or later (the latest version is presently available at          -->
<!-- http://www.opencontent.org/openpub/)                            -->
<!-- Distribution of the work or derivative of the work in any       -->
<!-- standard (paper) book form is prohibited unless prior           -->
<!-- permission obtained from the copyright holder                   -->
<!-- =============================================================== -->
<!-- ####ECOSDOCCOPYRIGHTEND####                                     -->
<!-- =============================================================== -->
<!-- #####DESCRIPTIONBEGIN####                                       -->
<!--                                                                 -->
<!-- Author(s):   bartv                                              -->
<!-- Date:        2004/10/05                                         -->
<!--                                                                 -->
<!-- ####DESCRIPTIONEND####                                          -->
<!-- =============================================================== -->

<!-- }}} -->

<part id="io-i2c"><title>I2C Support</title>

<refentry id="i2c">
  <refmeta>
    <refentrytitle>Overview</refentrytitle>
  </refmeta>
  <refnamediv>
    <refname>Overview</refname>
    <refpurpose>eCos Support for I2C, the Inter IC Bus</refpurpose>
  </refnamediv>

  <refsect1 id="i2c-description"><title>Description</title>
    <para>
The Inter IC Bus (I2C) is one of a number of serial bus technologies.
It can be used to connect a processor to one or more peripheral chips,
for example analog-to-digital convertors or real time clocks, using
only a small number of pins and PCB tracks. The technology was
originally developed by Philips Semiconductors but is supported by
many other vendors. The bus specification is freely available.
    </para>
    <para>
In a typical I2C system the processor acts as the I2C bus master. The
peripheral chips act as slaves. The bus consists of just two wires:
SCL carries a clock signal generated by the master, and SDA is a
bi-directional data line. The normal clock frequency is 100KHz. Each
slave has a 7-bit address. With some chips the address is hard-wired,
and it is impossible to have two of these chips on the same bus. With
other chips it is possible to choose between one of a small number of
addresses by connecting spare pins to either VDD or GND.
    </para>
    <para>
An I2C data transfer involves a number of stages:
    </para>
    <orderedlist>
      <listitem><para>
The bus master generates a start condition, a high-to-low transition
on the SDA line while SCL is kept high. This signalling cannot occur
during data transfer.
      </para></listitem>
      <listitem><para>
The bus master clocks the 7-bit slave address onto the SDA line,
followed by a direction bit to distinguish between reads and writes.
      </para></listitem>
      <listitem><para>
The addressed device acknowledges. If the master does not see an
acknowledgement then this suggests it is using the wrong address for
the slave device.
      </para></listitem>
      <listitem><para>
If the master is transmitting data to the slave then it will send this
data one byte at a time. The slave acknowledges each byte. If the
slave is unable to accept more data, for example because it has run
out of buffer space, then it will generate a nack and the master
should stop sending.
      </para></listitem>
      <listitem><para>
If the master is receiving data from the slave then the slave will
send this data one byte at a time. The master should acknowledge each
byte, until the last one. When the master has received all the data it
wants it should generate a nack and the slave will stop sending. This
nack is essential because it causes the slave to stop driving the SDA
line, releasing it back to the master.
      </para></listitem>
      <listitem><para>
It is possible to switch direction in a single transfer, using what is
known as a repeated start. This involves generating another start
condition, sending the 7-bit address again, followed by a new
direction bit.
      </para></listitem>
      <listitem><para>
At the end of a transfer the master should generate a stop condition,
a low-to-high transition on the SDA line while SCL is kept high. Again
this signalling does not occur at other times.
      </para></listitem>
    </orderedlist>
    <para>
There are a number of extensions. The I2C bus supports multiple bus
masters and there is an arbitration procedure to allow a master to
claim the bus. Some devices can have 10-bit addresses rather than
7-bit addresses. There is a fast mode operating at 400KHz instead of
the usual 100KHz, and a high-speed mode operating at 3.4MHz. Currently
most I2C-based systems do not involve any of these extensions.
    </para>
    <para>
At the hardware level I2C bus master support can be implemented in one
of two ways. Some processors provide a dedicated I2C device, with the
hardware performing much of the work. On other processors the I2C
device is implemented in software, by bit-banging some GPIO pins. The
latter approach can consume a significant number of cpu cycles, but is
often acceptable because only occasional access to the I2C devices is
needed.
    </para>
  </refsect1>

  <refsect1 id="i2c-ecos-implementation"><title>eCos Support for I2C</title>
    <para>
The eCos I2C support for any given platform is spread over a number of
different packages:
    </para>
    <itemizedlist>
      <listitem><para>
This package, <varname>CYGPKG_IO_I2C</varname>, exports a generic API
for accessing devices attached to an I2C bus. This API handles issues
such as locking between threads. The package does not contain any
hardware-specific code. Instead it will use a separate I2C bus driver
to handle the hardware, and it defines the interface that such bus
drivers should provide. The package only provides support for a bus
master, not for acting as a slave device.
      </para>
      <para>
<varname>CYGPKG_IO_I2C</varname> also provides the
hardware-independent portion of a bit-banged bus implementation. This
needs to be complemented by a hardware-specific function that actually
manipulates the SDA and SCL lines.
      </para></listitem>
      <listitem><para>
If the processor has a dedicated I2C device then there will be a bus
driver package for that hardware. The processor may be used on
many different platforms and the same bus driver can be used on each one.
The actual I2C devices attached to the bus will vary from one platform to
the next.
      </para></listitem>
      <listitem><para>
The generic API depends on <structname>cyg_i2c_device</structname>
data structures. These contain the information needed by a bus driver,
for example the device address. Usually the data structures are
provided by the platform HAL since it is that package which knows
about all the devices on the platform.
      </para>
      <para>
On some development boards the I2C lines are brought out to expansion
connectors, allowing end users to add extra devices. In such cases the
platform HAL may not know about all the devices on the board. Data
structures for the additional devices can instead be supplied by
application code.
      </para></listitem>
      <listitem><para>
If the board uses a bit-banged bus then typically the platform HAL
will also instantiate the bus instance, providing the function that
handles the low-level SDA and SCL manipulation. Usually this code
cannot be shared because each board may use different GPIO pins for
driving SCL and SDA, so the code belongs in the platform HAL rather
than in a separate package.
      </para></listitem>
      <listitem><para>
Some types of I2C devices may have their own driver package. For
example a common type of I2C device is a battery-backed wallclock, and
eCos defines how these devices should be supported. Such an I2C device
will have its own wallclock device driver and the device will not be
accessed directly by application code. For other types of device eCos
does not define an API and there will not be separate device driver
packages. Instead application code is expected to use the
<structname>cyg_i2c_device</structname> structures directly to access
the hardware.
      </para></listitem>
    </itemizedlist>
    <para>
Typically all appropriate packages will be loaded automatically when
you configure eCos for a given platform. If the application does not use
any of the I2C I/O facilities, directly or indirectly, then linker
garbage collection should eliminate all unnecessary code and data. All
necessary initialization should happen automatically. However the
exact details may depend on the platform, so the platform HAL
documentation should be checked for further details.
    </para>
    <para>
There is one important exception to this: if the I2C devices are
attached to an expansion connector then the platform HAL will not know
about these devices. Instead more work will have to be done by
application code.
    </para>
  </refsect1>

</refentry>

<refentry id="i2c-api">
  <refmeta>
    <refentrytitle>I2C Interface</refentrytitle>
  </refmeta>
  <refnamediv>
    <refname>I2C Functions</refname>
    <refpurpose>allow applications and other packages to access I2C devices</refpurpose>
  </refnamediv>
  <refsynopsisdiv>
    <funcsynopsis>
      <funcsynopsisinfo>
#include &lt;cyg/io/i2c.h&gt;
      </funcsynopsisinfo>
      <funcprototype>
        <funcdef>cyg_uint32 <function>cyg_i2c_tx</function></funcdef>
        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
        <paramdef>const cyg_uint8* <parameter>tx_data</parameter></paramdef>
        <paramdef>cyg_uint32 <parameter>count</parameter></paramdef>
      </funcprototype>
      <funcprototype>
        <funcdef>cyg_uint32 <function>cyg_i2c_rx</function></funcdef>
        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
        <paramdef>cyg_uint8* <parameter>rx_data</parameter></paramdef>
        <paramdef>cyg_uint32 <parameter>count</parameter></paramdef>
      </funcprototype>
      <funcprototype>
        <funcdef>void <function>cyg_i2c_transaction_begin</function></funcdef>
        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
      </funcprototype>
      <funcprototype>
        <funcdef>cyg_bool <function>cyg_i2c_transaction_begin_nb</function></funcdef>
        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
      </funcprototype>
      <funcprototype>
        <funcdef>cyg_uint32 <function>cyg_i2c_transaction_tx</function></funcdef>
        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
        <paramdef>cyg_bool <parameter>send_start</parameter></paramdef>
        <paramdef>const cyg_uint8* <parameter>tx_data</parameter></paramdef>
        <paramdef>cyg_uint32 <parameter>count</parameter></paramdef>
        <paramdef>cyg_bool <parameter>send_stop</parameter></paramdef>
      </funcprototype>
      <funcprototype>
        <funcdef>cyg_uint32 <function>cyg_i2c_transaction_rx</function></funcdef>
        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
        <paramdef>cyg_bool <parameter>send_start</parameter></paramdef>
        <paramdef>cyg_uint8* <parameter>rx_data</parameter></paramdef>
        <paramdef>cyg_uint32 <parameter>count</parameter></paramdef>
        <paramdef>cyg_bool <parameter>send_nack</parameter></paramdef>
        <paramdef>cyg_bool <parameter>send_stop</parameter></paramdef>
      </funcprototype>
      <funcprototype>
        <funcdef>void <function>cyg_i2c_transaction_stop</function></funcdef>
        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
      </funcprototype>
      <funcprototype>
        <funcdef>void <function>cyg_i2c_transaction_end</function></funcdef>
        <paramdef>const cyg_i2c_device* <parameter>device</parameter></paramdef>
      </funcprototype>
    </funcsynopsis>
  </refsynopsisdiv>

  <refsect1 id="i2c-api-description"><title>Description</title>
    <para>
All I2C functions take a pointer to a
<structname>cyg_i2c_device</structname> structure as their first
argument. These structures are usually provided by the platform HAL.
They contain the information needed by the I2C bus driver to interact
with the device, for example the device address.
    </para>
    <para>
An I2C transaction involves the following stages:
    </para>
    <orderedlist>
      <listitem><para>
Perform thread-level locking on the bus. Only one thread at a time is
allowed to access an I2C bus. This eliminates the need to worry about
locking at the bus driver level. If a platform involves multiple I2C
buses then each one will have its own lock.
      </para></listitem>
      <listitem><para>
Generate a start condition, send the address and direction bit, and
wait for an acknowledgement from the addressed device.
      </para></listitem>
      <listitem><para>
Either transmit data to or receive data from the addressed device.
      </para></listitem>
      <listitem><para>
The previous two steps may be repeated several times, allowing data to
move in both directions during a single transfer.
      </para></listitem>
      <listitem><para>
Generate a stop condition, ending the current data transfer. It is
now possible to start another data transfer while the bus is still
locked, if desired.
      </para></listitem>
      <listitem><para>
End the transaction by unlocking the bus, allowing other threads to
access other devices on the bus.
      </para></listitem>
    </orderedlist>
    <para>
The simple functions <function>cyg_i2c_tx</function> and
<function>cyg_i2c_rx</function> perform all these steps in a single
call, making them suitable for many I/O operations. The alternative
transaction-oriented functions provide greater control when
appropriate, for example if a repeated start is necessary for a
bi-directional data transfer.
    </para>
    <para>
With the exception of
<function>cyg_i2c_transaction_begin_nb</function> all the functions
will block until completion. The tx routines will return 0 if the
specified device does not respond to its address, or the number of
bytes actually transferred. This may be less than the number requested
if the device sends an early nack, for example because it has run out
of buffer space. The rx routines will return 0 or the number of bytes
received. Usually this will be the same as the
<varname>count</varname> parameter. A slave device has no way of
indicating to the master that no more data is available, so the rx
operation cannot complete early.
    </para>
    <para>
I2C operations should always be performed at thread-level or during
system initialization, and not inside an ISR or DSR. This greatly
simplifies locking. Also a typical ISR or DSR should not perform a
blocking operation such as an I2C transfer.
    </para>
  </refsect1>

<refsect1 id="i2c-api-transfer"><title>Simple Transfers</title>
    <para>
<function>cyg_i2c_tx</function> and <function>cyg_i2c_rx</function>
can be used for simple data transfers. They both go through the
following steps: lock the bus, generate the start condition, send the
device address and the direction bit, either send or receive the data,
generate the stop condition, and unlock the bus. At the end of a
transfer the bus is back in its idle state, ready for the next
transfer.
    </para>
    <para>
<function>cyg_i2c_tx</function> returns the number of bytes actually
transmitted. This may be 0 if the device does not respond when its
address is sent out. It may be less than the number of bytes requested
if the device generates an early nack, typically because it has run
out of buffer space.
    </para>
    <para>
<function>cyg_i2c_rx</function> returns 0 if the device does not
respond when its address is sent out, or the number of bytes actually
received. Usually this will be the number of bytes requested because
an I2C slave device has no way of aborting an rx operation early.
    </para>
  </refsect1>

  <refsect1 id="i2c-api-transaction"><title>Transactions</title>
    <para>
To allow multiple threads to access devices on the I2C some locking is
required. This is encapsulated inside transactions. The
<function>cyg_i2c_tx</function> and <function>cyg_i2c_rx</function>
functions implicitly use such transactions, but the functionality is
also available directly to application code. Amongst other things
transactions can be used for more complicated interactions with I2C
devices, in particular ones involving repeated starts.
    </para>
    <para>
<function>cyg_i2c_transaction_begin</function> must be used at the
start of a transaction. This performs thread-level locking on the bus,
blocking if it is currently in use by another thread.
    </para>
    <para>
<function>cyg_i2c_transaction_begin_nb</function> is a non-blocking
variant, useful for threads which cannot afford to block for an
indefinite period. If the bus is currently locked the function returns
false immediately. If the bus is not locked then it acts as
<filename>cyg_i2c_transaction_begin</filename> and returns true.
    </para>
    <para>
Once the bus has been locked it is possible to perform one or more
data transfers by calling
<function>cyg_i2c_transaction_tx</function>,
<function>cyg_i2c_transaction_rx</function> and
<function>cyg_i2c_transaction_stop</function>. Code should ensure that
a stop condition has been generated by the end of a transaction.
    </para>
    <para>
Once the transaction is complete
<function>cyg_i2c_transaction_end</function> should be called. This
unlocks the bus, allowing other threads to perform I2C I/O to devices
on the same bus.
    </para>
    <para>
As an example consider reading the registers in an FS6377 programmable
clock generator. The first step is to write a byte 0 to the device,
setting the current register to 0. Then a repeated start condition
should be generated and it is possible to read the 16 byte-wide
registers, starting with the current one. Typical code for this might
look like:
    </para>
    <programlisting width=72>
    cyg_uint8  tx_data[1];
    cyg_uint8  rx_data[16];

    cyg_i2c_transaction_begin(&amp;hal_alaia_i2c_fs6377);
    tx_data[0] = 0x00;
    cyg_i2c_transaction_tx(&amp;hal_alaia_i2c_fs6377,
                           true, tx_data, 1, false);
    cyg_i2c_transaction_rx(&amp;hal_alaia_i2c_fs6377,
                           true, rx_data, 16, true, true);
    cyg_i2c_transaction_end(&amp;hal_alaia_i2c_fs6377);
    </programlisting>
    <para>
Here <varname>hal_alaia_i2c_fs6377</varname> is a
<structname>cyg_i2c_device</structname> structure provided by the
platform HAL. A transaction is begun, locking the bus. Then there is a
transmit for a single byte. This transmit involves generating a start
condition and sending the address and direction bit, but not a stop
condition. Next there is a receive for 16 bytes. This also involves a
start condition, which the device will interpret as a repeated start
because it has not yet seen a stop. The start condition will be
followed by the address and direction bit, and then the device will
start transmitting the register contents. Once all 16 bytes have been
received the rx routine will send a nack rather than an ack, halting
the transfer, and then a stop condition is generated. Finally the
transaction is ended, unlocking the bus.
    </para>
    <para>
The arguments to <function>cyg_i2c_transaction_tx</function> are as
follows:
    </para>
    <variablelist>
      <varlistentry>
        <term><type>const cyg_i2c_device*</type> <varname>device</varname></term>
        <listitem><para>
This identifies the I2C device that should be used.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><type>cyg_bool</type> <varname>send_start</varname></term>
        <listitem><para>
If true, generate a start condition and send the address and direction
bit. If false, skip those steps and go straight to transmitting the
actual data. The latter can be useful if the data to be transmitted is
spread over several buffers. The first tx call will involve generating
the start condition but subsequent tx calls can skip this and just
continue from the previous one.
        </para><para>
<varname>send_start</varname> must be true if the tx call is the first
operation in a transaction, or if the previous call was an rx or stop.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><type>const cyg_uint8*</type> <varname>tx_data</varname></term>
        <term><type>cyg_uint32</type> <varname>count</varname></term>
        <listitem><para>
These arguments specify the data to be transmitted to the device.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><type>cyg_bool</type> <varname>send_stop</varname></term>
        <listitem><para>
If true, generate a stop condition at the end of the transmit. Usually
this is done only if the transmit is the last operation in a
transaction.
        </para></listitem>
      </varlistentry>
    </variablelist>
    <para>
The arguments to <function>cyg_i2c_transaction_rx</function> are as
follows:
    </para>
    <variablelist>
      <varlistentry>
        <term><type>const cyg_i2c_device*</type> <varname>device</varname></term>
        <listitem><para>
This identifies the I2C device that should be used.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><type>cyg_bool</type> <varname>send_start</varname></term>
        <listitem><para>
If true, generate a start condition and send the address and direction
bit. If false, skip those steps and go straight to receiving the
actual data. The latter can be useful if the incoming data should be
spread over several buffers. The first rx call will involve generating
the start condition but subsequent rx calls can skip this and just
continue from the previous one. Another use is for devices which can
send variable length data, consisting of an initial length and then
the actual data. The first rx will involve generating the start
condition and reading the length, a subsequent rx will then just read
the data.
        </para><para>
<varname>send_start</varname> must be true if the rx call is the first
operation in a transaction, if the previous call was a tx or stop, or
if the previous call was an an rx and the <varname>send_nack</varname>
flag was set.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><type>cyg_uint8*</type> <varname>rx_data</varname></term>
        <term><type>cyg_uint32</type> <varname>count</varname></term>
        <listitem><para>
These arguments specify how much data should be received and where it
should be placed.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><type>cyg_bool</type> <varname>send_nack</varname></term>
        <listitem><para>
If true generate a nack instead of an ack for the last byte received.
This causes the slave to end its transmit. The next operation should
either involve a repeated start or a stop.
<varname>send_nack</varname> should be set to false only if
<varname>send_stop</varname> is also false, the next operation will be
another rx, and that rx does not specify <varname>send_start</varname>.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><type>cyg_bool</type> <varname>send_stop</varname></term>
        <listitem><para>
If true, generate a stop condition at the end of the transmit. Usually
this is done only if the transmit is the last operation in a
transaction.
        </para></listitem>
      </varlistentry>
    </variablelist>
    <para>
The final transaction-oriented function is
<function>cyg_i2c_transaction_stop</function>. This just generates a
stop condition. It should be used if the previous operation was a tx
or rx that, for some reason, did not set the
<varname>send_stop</varname> flag. A stop condition must be generated
before the transaction is ended.
    </para>
  </refsect1>

  <refsect1 id="i2c-api-initialization"><title>Initialization</title>
    <para>
The generic package <varname>CYGPKG_IO_I2C</varname> arranges for all
I2C bus devices to be initialized via a single prioritized C++ static
constructor. This constructor will run early on during system startup,
before any application code, with priority
<literal>CYG_INIT_BUS_I2C</literal>. Other code should not try to
access any of the I2C devices until after the buses have been
initialized.
    </para>
  </refsect1>

</refentry>

<refentry id="i2c-porting">
  <refmeta>
    <refentrytitle>Porting to New Hardware</refentrytitle>
  </refmeta>
  <refnamediv>
    <refname>Porting</refname>
    <refpurpose>Adding I2C support to new hardware</refpurpose>
  </refnamediv>

  <refsect1 id="i2c-porting-description"><title>Description</title>
    <para>
Adding I2C support to an eCos port involves a number of steps. The
generic I2C package <varname>CYGPKG_IO_I2C</varname> should be
included in the appropriate <database>ecos.db</database> target entry
or entries. Next <structname>cyg_i2c_device</structname> structures
should be provided for every device on the bus. Usually this is the
responsibility of the platform HAL. In the case of development boards
where the I2C SDA and SCL lines are accessible via an expansion
connector, more devices may have been added and it will be the
application's responsibility to provide the structures. Finally
there is a need for one or more <structname>cyg_i2c_bus</structname>
structures. Amongst other things these structures provide functions
for actually driving the bus. If the processor has dedicated I2C
hardware then this structure will usually be provided by a device
driver package. If the bus is implemented by bit-banging then the bus
structure will usually be provided by the platform HAL.
    </para>
  </refsect1>

  <refsect1 id="i2c-porting-devices"><title>Adding a Device</title>
    <para>
The eCos I2C API works in terms of
<structname>cyg_i2c_device</structname> structures, and these provide
the information needed to access the hardware. A
<structname>cyg_i2c_device</structname> structure contains the
following fields:
    </para>
    <variablelist>
      <varlistentry>
        <term><type>cyg_i2c_bus*</type> <varname>i2c_bus</varname></term>
        <listitem><para>
This specifies the bus which the slave device is connected to. Most
boards will only have a single I2C bus, but multiple buses are possible.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><type>cyg_uint16</type> <varname>i2c_address</varname></term>
        <listitem><para>
For most devices this will be the 7-bit I2C address the device will
respond to. There is room for future expansion, for example to support
10-bit addresses.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><type>cyg_uint16</type> <varname>i2c_flags</varname></term>
        <listitem><para>
This field is not used at present. It exists for future expansion, for
example to allow for fast mode or high-speed mode, and incidentally
pads the structure to a 32-bit boundary.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><type>cyg_uint32</type> <varname>i2c_delay</varname></term>
        <listitem><para>
This holds the clock period which should be used when interacting with
the device, in nanoseconds. Usually this will be 10000 ns,
corresponding to a 100KHz clock, and the header <filename
class="headerfile">cyg/io/i2c.h</filename> provides a
<literal>#define</literal> <varname>CYG_I2C_DEFAULT_DELAY</varname>
for this. Sometimes it may be desirable to use a slower clock, for
example to reduce noise problems.
        </para></listitem>
      </varlistentry>
    </variablelist>
    <para>
The normal way to instantiate a <varname>cyg_i2c_device</varname>
structure uses the <function>CYG_I2C_DEVICE</function> macro, also
provided by <filename class="headerfile">cyg/io/i2c.h</filename>:
    </para>
    <programlisting width=72>
#include &lt;cyg/io/i2c.h&gt;

CYG_I2C_DEVICE(cyg_i2c_wallclock_ds1307,
               &amp;hal_alaia_i2c_bus,
               0x68,
               0x00,
               CYG_I2C_DEFAULT_DELAY);

CYG_I2C_DEVICE(hal_alaia_i2c_fs6377,
               &amp;hal_alaia_i2c_bus,
               0x58,
               0x00,
               CYG_I2C_DEFAULT_DELAY);
    </programlisting>
    <para>
The arguments to the macro are the variable name, an I2C bus pointer,
the device address, the flags field, and the delay field. The above
code fragment defines two I2C device variables,
<varname>cyg_i2c_wallclock_ds1307</varname> and
<varname>hal_alaia_i2c_fs6377</varname>, which can be used for the
first argument to the <literal>cyg_i2c_</literal> functions. Both
devices are on the same bus. The device addresses are 0x68 and 0x58
respectively, and the devices do not have any special requirements.
    </para>
    <para>
When the platform HAL provides these structures it should also export
them for use by the application and other packages. Usually this
involves an entry in <filename
class="headerfile">cyg/hal/plf_io.h</filename>, which gets included
automatically via one of the main exported HAL header files <filename
class="headerfile">cyg/hal/hal_io.h</filename>. Unfortunately
exporting the structures directly can be problematical because of
circular dependencies between the I2C header and the HAL headers.
Instead the platform HAL should define a macro
<varname>HAL_I2C_EXPORTED_DEVICES</varname>: 
    </para>
    <programlisting width=72>
# define HAL_I2C_EXPORTED_DEVICES                                   \
    extern cyg_i2c_bus                  hal_alaia_i2c_bus;          \
    extern cyg_i2c_device               cyg_i2c_wallclock_ds1307;   \
    extern cyg_i2c_device               hal_alaia_i2c_fs6377;
    </programlisting>
    <para>
This macro gets expanded automatically by <filename
class="headerfile">cyg/io/i2c.h</filename> once the data structures
themselves have been defined, so application code can just include
that header and all the buses and devices will be properly exported
and usable.
    </para>
    <para>
There is no single convention for naming the I2C devices. If the
device will be used by some other package then typically that
specifies the name that should be used. For example the DS1307
wallclock driver expects the I2C device to be called
<varname>cyg_i2c_wallclock_ds1307</varname>, so failing to observe
that convention will lead to compile-time and link-time errors. If the
device will not be used by any other package then it is up to the
platform HAL to select the name, and as long as reasonable care is
taken to avoid name space pollution the exact name does not matter.
    </para>
  </refsect1>

  <refsect1 id="i2c-porting-bitbang"><title>Bit-banged Bus</title>
    <para>
Some processors come with dedicated I2C hardware. On other hardware
the I2C bus involves simply connecting some GPIO pins to the SCL and
SDA lines and then using software to implement the I2C protocol. This
is usually referred to as bit-banging the bus. The generic I2C package
<varname>CYGPKG_IO_I2C</varname> provides the main code for a
bit-banged implementation, requiring one platform-specific function
that does the actual GPIO pin manipulation. This function is usually
hardware-specific because different boards will use different pins for
the I2C bus, so typically it is left to the platform HAL to provide
this function and instantiate the I2C bus object. There is no point in
creating a separate package for this because the code cannot be
re-used for other platforms.
    </para>
    <para>
Instantiating a bit-banged I2C bus requires the following:
    </para>
    <programlisting width=72>
#include &lt;cyg/io/i2c.h&gt;

static cyg_bool
hal_alaia_i2c_bitbang(cyg_i2c_bus* bus, cyg_i2c_bitbang_op op)
{
    cyg_bool result    = 0;
    switch(op) {
        &hellip;
    }
    return result;
}

CYG_I2C_BITBANG_BUS(hal_alaia_i2c_bus, &amp;hal_alaia_i2c_bitbang);
    </programlisting>
    <para>
This gives a structure <varname>hal_alaia_i2c_bus</varname> which can
be used when defining the <varname>cyg_i2c_device</varname>
structures. The second argument specifies the function which will
do the actual bit-banging. It takes two arguments. The first
identifies the bus, which can be useful if the hardware has multiple
I2C buses. The second specifies the bit-bang operation that should be
performed. To understand these operations consider how I2C devices
should be wired up according to the specification:
    </para>
    <informalfigure PgWide=1>
      <mediaobject>
        <imageobject>
          <imagedata fileref="i2c_hw.png" Scalefit=1 Align="Center">
        </imageobject>
      </mediaobject>
    </informalfigure>
    <para>
Master and slave devices are interfaced to the bus in exactly the same
way. The default state of the bus is to have both lines high via the
pull-up resistors. Any device on the bus can lower either line, when
allowed to do so by the protocol. Usually the SDA line only changes
while SCL is low, but the start and stop conditions involve SDA
changing while SCL is high. All devices have the ability to both read
and write both lines. In reality not all bit-banged hardware works
quite like this. Instead just two GPIO pins are used, and these are
switched between input and output mode as required.
    </para>
    <para>
The bitbang function should support the following operations:
    </para>
    <variablelist>
      <varlistentry>
        <term><literal>CYG_I2C_BITBANG_INIT</literal></term>
        <listitem><para>
This will be called during system initialization, as a side effect of
a prioritized C++ static constructor. The bitbang function should
ensure that both SCL and SDA are driven high.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><literal>CYG_I2C_BITBANG_SCL_HIGH</literal></term>
        <term><literal>CYG_I2C_BITBANG_SCL_LOW</literal></term>
        <term><literal>CYG_I2C_BITBANG_SDA_HIGH</literal></term>
        <term><literal>CYG_I2C_BITBANG_SDA_LOW</literal></term>
        <listitem><para>
These operations simply set the appropriate lines high or low.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><literal>CYG_I2C_BITBANG_SCL_HIGH_CLOCKSTRETCH</literal></term>
        <listitem><para>
In its simplest form this operation should simply set the SCL line
high, indicating that the data on the SDA line is stable. However
there is a complication: if a device is not ready yet then it can
throttle back the master by keeping the SCL line low. This is known as
clock-stretching. Hence for this operation the bitbang function should
allow the SCL line to float high, then poll it until it really has
become high. If a single pin is used for the SCL line then this pin
should be turned back into a high output at the end of the call.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><literal>CYG_I2C_BITBANG_SCL_LOW_SDA_INPUT</literal></term>
        <listitem><para>
This is used when there is a change of direction and the slave device
is about to start driving the SDA line. This can be significant if a
single pin is used to handle both input and output of SDA, to avoid
a situation where both the master and the slave are driving the SDA
line for an extended period of time. The operation combines dropping
the SCL line and switching SDA to an input in an atomic or near-atomic
operation.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><literal>CYG_I2C_BITBANG_SDA_READ</literal></term>
        <listitem><para>
The SDA line is currently set as an input and the bitbang function
should sample and return the current state.
        </para></listitem>
      </varlistentry>
    </variablelist>
    <para>
The bitbang function returns a boolean. For most operations this
return value is ignored. For
<literal>CYG_I2C_BITBANG_SDA_READ</literal> it should be the current
level of the SDA line.
    </para>
    <para>
Depending on the hardware some care may have to be taken when
manipulating the GPIO pins. Although the I2C subsystem performs the
required locking at the bus level, the device registers controlling
the GPIO pins may get used by other subsystems or by the application.
It is the responsibility of the bitbang function to perform
appropriate locking, whether via a mutex or by briefly disabling
interrupts around the register accesses.
    </para>
  </refsect1>

  <refsect1 id="i2c-porting-bus"><title>Full Bus Driver</title>
    <para>
If the processor has dedicated I2C hardware then usually this will
involve a separate device driver package in the
<filename>devs/i2c</filename> hierarchy of the eCos component
repository. That package should also be included in the appropriate
<database>ecos.db</database> target entry or entries. The device
driver may exist already, or it may have to be written from scratch.
    </para>
    <para>
A new I2C driver basically involves creating an
<structname>cyg_i2c_bus</structname> structure. The device driver
should supply the following fields:
    </para>
    <variablelist>
      <varlistentry>
        <term><function>i2c_init_fn</function></term>
        <listitem><para>
This function will be called during system initialization to set up
the I2C hardware. The generic I2C code creates a static object with a
prioritized constructor, and this constructor will invoke the init
functions for the various I2C buses in the system.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><function>i2c_tx_fn</function></term>
        <term><function>i2c_rx_fn</function></term>
        <term><function>i2c_stop_fn</function></term>
        <listitem><para>
These functions implement the core I2C functionality. The arguments
and results are the same as for the transaction functions
<function>cyg_i2c_transaction_tx</function>,
<function>cyg_i2c_transaction_rx</function> and
<function>cyg_i2c_transaction_stop</function>.
        </para></listitem>
      </varlistentry>
      <varlistentry>
        <term><type>void*</type> <varname>i2c_extra</varname></term>
        <listitem><para>
This field holds any extra information that may be needed by the
device driver. Typically it will be a pointer to some driver-specific
data structure.
        </para></listitem>
      </varlistentry>
    </variablelist>
    <para>
To assist with instantiating a <structname>cyg_i2c_bus</structname>
object the header file <filename
class="headerfile">cyg/io/i2c.h</filename> provides a macro. Typical
usage would be:
    </para>
    <programlisting width=72>
struct xyzzy_data {
    &hellip;
} xyzzy_object;

static void
xyzzy_i2c_init(struct cyg_i2c_bus* bus)
{
    &hellip;
}

static cyg_uint32
xyzzy_i2c_tx(const cyg_i2c_device* dev,
             cyg_bool send_start,
             const cyg_uint8* tx_data, cyg_uint32 count,
             cyg_bool send_stop)
{
    &hellip;
}

static cyg_uint32
xyzzy_i2c_rx(const cyg_i2c_device* dev,
             cyg_bool send_start,
             cyg_uint8* rx_data, cyg_uint32 count,
             cyg_bool send_nack, cyg_bool send_stop)
{
    &hellip;
}

static void
xyzzy_i2c_stop(const cyg_i2c_device* dev)
{
    &hellip;
}

CYG_I2C_BUS(cyg_i2c_xyzzy_bus,
            &amp;xyzzy_i2c_init,
            &amp;xyzzy_i2c_tx,
            &amp;xyzzy_i2c_rx,
            &amp;xyzzy_i2c_stop,
            (void*) &amp;xyzzy_object);
    </programlisting>
    <para>
The generic I2C code contains these functions for a bit-banged I2C bus
device. It can be used as a starting point for new drivers. Note that
the bit-bang code uses the <varname>i2c_extra</varname> field to hold
the hardware-specific bitbang function rather than a pointer to some
data structure.
    </para>

  </refsect1>

</refentry>
</part>

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.