This is the mail archive of the
ecos-patches@sources.redhat.com
mailing list for the eCos project.
I2C support
- From: Bart Veer <bartv at ecoscentric dot com>
- To: ecos-patches at ecos dot sourceware dot org
- Date: Wed, 20 Apr 2005 13:10:06 +0100 (BST)
- Subject: I2C support
This patch adds generic I2C support to eCos. Previously this was only
available as part of eCosPro. It contains:
1) an application-level API for accessing I2C devices, not dissimilar
to the SPI code contributed last year
2) a specification of how to write I2C device drivers and include
the platform-specific support for attached I2C devices
3) a generic bit-bang implementation of I2C, handling all the timing
and protocol issues, requiring just one platform-specific function
to do the actual line waggling
4) full documentation.
Bart
Index: ecos.db
===================================================================
RCS file: /cvs/ecos/ecos/packages/ecos.db,v
retrieving revision 1.141
diff -u -r1.141 ecos.db
--- ecos.db 17 Apr 2005 12:44:37 -0000 1.141
+++ ecos.db 20 Apr 2005 11:58:02 -0000
@@ -2085,6 +2085,18 @@
bus drivers and for defining SPI device structures."
}
+package CYGPKG_IO_I2C {
+ alias { "Generic I2C support" i2c io_i2c i2c_io }
+ directory io/i2c
+ script i2c.cdl
+ hardware
+ description "
+ The generic I2C package provides an API for accessing devices
+ attached to an I2C bus. It specifies how I2C bus drivers should
+ be written and how I2C devices should be defined. There is also
+ support for bit-banged I2C buses."
+}
+
package CYGPKG_KERNEL {
alias { "eCos kernel" kernel }
directory kernel
Index: io/i2c/current/ChangeLog
===================================================================
RCS file: io/i2c/current/ChangeLog
diff -N io/i2c/current/ChangeLog
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/ChangeLog 20 Apr 2005 11:55:29 -0000
@@ -0,0 +1,40 @@
+2005-03-03 Bart Veer <bartv@ecoscentric.com>
+
+ * src/i2c.cxx: cope with ports which define HAL_DELAY_US() in
+ hal_diag.h instead of hal_intr.h
+
+2004-10-05 Bart Veer <bartv@ecoscentric.com>
+
+ * Generic I2C package created
+
+//===========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2004, 2005 eCosCentric Limited
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//===========================================================================
Index: io/i2c/current/cdl/i2c.cdl
===================================================================
RCS file: io/i2c/current/cdl/i2c.cdl
diff -N io/i2c/current/cdl/i2c.cdl
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/cdl/i2c.cdl 20 Apr 2005 11:55:30 -0000
@@ -0,0 +1,102 @@
+# ====================================================================
+#
+# i2c.cdl
+#
+# Generic I2C package configuration data
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2004 eCosCentric Limited
+##
+## eCos is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free
+## Software Foundation; either version 2 or (at your option) any later version.
+##
+## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+## for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s): bartv
+# Date: 2004-10-05
+#
+#####DESCRIPTIONEND####
+#========================================================================
+
+cdl_package CYGPKG_IO_I2C {
+ display "I2C support"
+ requires CYGPKG_INFRA CYGPKG_HAL
+ hardware
+ include_dir cyg/io
+ compile i2c.cxx
+
+ description "
+ The generic I2C package provides an API for accessing devices
+ attached to an I2C bus. It specifies how I2C bus drivers should
+ be written and how I2C devices should be defined. There is also
+ support for bit-banged I2C buses."
+
+ cdl_option CYGNUM_I2C_INIT_PRIORITY {
+ display "I2C initialization priority"
+ flavor data
+ default_value { "CYG_INIT_DRIVERS" }
+
+ description "
+ The generic I2C package will initialize each I2C bus during
+ system startup, using a prioritized static constructor. This
+ option controls the priority that is used. The default value,
+ CYG_INIT_DRIVERS, means that I2C buses get initialized early
+ on."
+ }
+
+ cdl_component CYGPKG_IO_I2C_OPTIONS {
+ display "I2C build options"
+ flavor none
+ description "
+ Package specific build options including control over
+ compiler flags used only in building the generic I2C
+ package, and details of which tests are built."
+
+ cdl_option CYGPKG_IO_I2C_CFLAGS_ADD {
+ display "Additional compiler flags"
+ flavor data
+ no_define
+ default_value { "" }
+ description "
+ This option modifies the set of compiler flags for
+ building the generic I2C package. These flags are
+ used in addition to the set of global flags."
+ }
+
+ cdl_option CYGPKG_IO_I2C_CFLAGS_REMOVE {
+ display "Suppressed compiler flags"
+ flavor data
+ no_define
+ default_value { "" }
+ description "
+ This option modifies the set of compiler flags for
+ building the generic I2C package. These flags are
+ removed from the set of global flags if present."
+ }
+ }
+}
Index: io/i2c/current/doc/i2c.sgml
===================================================================
RCS file: io/i2c/current/doc/i2c.sgml
diff -N io/i2c/current/doc/i2c.sgml
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/doc/i2c.sgml 20 Apr 2005 11:55:36 -0000
@@ -0,0 +1,941 @@
+<!-- DOCTYPE part PUBLIC "-//OASIS//DTD DocBook V3.1//EN" -->
+
+<!-- {{{ Banner -->
+
+<!-- =============================================================== -->
+<!-- -->
+<!-- i2c.sgml -->
+<!-- -->
+<!-- Generic I2C documentation. -->
+<!-- -->
+<!-- =============================================================== -->
+<!-- ####COPYRIGHTBEGIN#### -->
+<!-- -->
+<!-- =============================================================== -->
+<!-- Copyright (C) 2004 eCosCentric Limited -->
+<!-- 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 -->
+<!-- =============================================================== -->
+<!-- -->
+<!-- ####COPYRIGHTEND#### -->
+<!-- =============================================================== -->
+<!-- #####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 <cyg/io/i2c.h>
+ </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>const cyg_uint8* <parameter>tx_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>const cyg_uint8* <parameter>tx_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(&hal_alaia_i2c_fs6377);
+ tx_data[0] = 0x00;
+ cyg_i2c_transaction_tx(&hal_alaia_i2c_fs6377,
+ true, tx_data, 1, false);
+ cyg_i2c_transaction_rx(&hal_alaia_i2c_fs6377,
+ true, rx_data, 16, true, true);
+ cyg_i2c_transaction_end(&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. Usually this constructor will run early on during system
+startup, before any application code. The default priority is
+<literal>CYG_INIT_DRIVERS</literal>, but this can be changed via the
+configuration option <varname>CYGNUM_I2C_INIT_PRIORITY</varname>.
+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 <cyg/io/i2c.h>
+
+CYG_I2C_DEVICE(cyg_i2c_wallclock_ds1307, \
+ &hal_alaia_i2c_bus, \
+ 0x68, \
+ 0x00, \
+ CYG_I2C_DEFAULT_DELAY);
+
+CYG_I2C_DEVICE(hal_alaia_i2c_fs6377, \
+ &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 <cyg/io/i2c.h>
+
+static cyg_bool
+hal_alaia_i2c_bitbang(cyg_i2c_bus* bus, cyg_i2c_bitbang_op op)
+{
+ cyg_bool result = 0;
+ switch(op) {
+ …
+ }
+ return result;
+}
+
+CYG_I2C_BITBANG_BUS(&hal_alaia_i2c_bus, &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. By default this constructor will
+run at <literal>CYG_INIT_DRIVERS</literal> priority. 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 {
+ …
+} xyzzy_object;
+
+static void
+xyzzy_i2c_init(struct cyg_i2c_bus* bus)
+{
+ …
+}
+
+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)
+{
+ …
+}
+
+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)
+{
+ …
+}
+
+static void
+xyzzy_i2c_stop(const cyg_i2c_device* dev)
+{
+ …
+}
+
+CYG_I2C_BUS(cyg_i2c_xyzzy_bus,
+ &xyzzy_i2c_init,
+ &xyzzy_i2c_tx,
+ &xyzzy_i2c_rx,
+ &xyzzy_i2c_stop,
+ (void*) &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>
Index: io/i2c/current/doc/i2c_hw.fig
===================================================================
RCS file: io/i2c/current/doc/i2c_hw.fig
diff -N io/i2c/current/doc/i2c_hw.fig
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/doc/i2c_hw.fig 20 Apr 2005 11:55:36 -0000
@@ -0,0 +1,182 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 450 1725 4275 6000
+6 450 1725 4275 6000
+6 450 1725 4275 6000
+6 450 1725 4275 6000
+6 600 1725 4125 5700
+6 2925 2325 4125 5700
+6 2925 2400 4125 5700
+6 2925 2400 4125 5700
+6 2925 3300 4125 5700
+6 2925 3300 4125 5700
+6 2925 3300 4125 5700
+6 2925 3300 4125 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 2925 3600 3225 3600 3525 3300 3525 3900 3225 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 3525 3600 3825 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 3525 5400 4125 5400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 3645 5550 4005 5550
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 3750 5700 3900 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 2925 4650 3525 4650
+-6
+1 4 0 1 0 7 50 -1 -1 0.000 1 0.0000 3525 4650 300 300 3525 4350 3525 4950
+-6
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+ 3525 4800 3825 4800 3825 5400
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+ 3825 2400 3825 4500 3525 4500
+-6
+2 1 0 4 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 3525 4425 3525 4875
+-6
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 3825 2400 18 18 3807 2400 3843 2400
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 3825 3600 18 18 3807 3600 3843 3600
+-6
+1 4 0 1 0 7 50 -1 -1 0.000 1 0.0000 1800 4650 300 300 1800 4350 1800 4950
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 2100 1800 18 18 2082 1800 2118 1800
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1200 3600 1500 3600 1800 3300 1800 3900 1500 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 1800 3600 2100 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 1800 5400 2400 5400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 1920 5550 2280 5550
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 2025 5700 2175 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 1200 4650 1800 4650
+4 0 0 50 -1 0 12 0.0000 4 135 555 600 3525 SDA in\001
+4 0 0 50 -1 0 12 0.0000 4 135 660 600 4575 SDA out\001
+4 0 0 50 -1 0 12 0.0000 4 135 510 2550 3525 SCL in\001
+4 0 0 50 -1 0 12 0.0000 4 135 660 2475 4575 SDA out\001
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+ 2100 1800 2100 4500 1800 4500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 450 2700 4275 2700 4275 6000 450 6000 450 2700
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+ 1800 4800 2100 4800 2100 5400
+-6
+2 1 0 4 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 1800 4425 1800 4875
+-6
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 2100 3600 18 18 2082 3600 2118 3600
+-6
+6 5025 1725 8850 6000
+6 5025 1725 8850 6000
+6 5025 1725 8850 6000
+6 5025 1725 8850 6000
+6 5175 1725 8700 5700
+6 7500 2325 8700 5700
+6 7500 2400 8700 5700
+6 7500 2400 8700 5700
+6 7500 3300 8700 5700
+6 7500 3300 8700 5700
+6 7500 3300 8700 5700
+6 7500 3300 8700 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 7500 3600 7800 3600 8100 3300 8100 3900 7800 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 8100 3600 8400 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 8100 5400 8700 5400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 8220 5550 8580 5550
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 8325 5700 8475 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 7500 4650 8100 4650
+-6
+1 4 0 1 0 7 50 -1 -1 0.000 1 0.0000 8100 4650 300 300 8100 4350 8100 4950
+-6
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+ 8100 4800 8400 4800 8400 5400
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+ 8400 2400 8400 4500 8100 4500
+-6
+2 1 0 4 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 8100 4425 8100 4875
+-6
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 8400 2400 18 18 8382 2400 8418 2400
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 8400 3600 18 18 8382 3600 8418 3600
+-6
+1 4 0 1 0 7 50 -1 -1 0.000 1 0.0000 6375 4650 300 300 6375 4350 6375 4950
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 6675 1800 18 18 6657 1800 6693 1800
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 5775 3600 6075 3600 6375 3300 6375 3900 6075 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6375 3600 6675 3600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6375 5400 6975 5400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6495 5550 6855 5550
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6600 5700 6750 5700
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
+ 1 1 1.00 60.00 120.00
+ 5775 4650 6375 4650
+4 0 0 50 -1 0 12 0.0000 4 135 555 5175 3525 SDA in\001
+4 0 0 50 -1 0 12 0.0000 4 135 660 5175 4575 SDA out\001
+4 0 0 50 -1 0 12 0.0000 4 135 510 7125 3525 SCL in\001
+4 0 0 50 -1 0 12 0.0000 4 135 660 7050 4575 SDA out\001
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+ 6675 1800 6675 4500 6375 4500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 5025 2700 8850 2700 8850 6000 5025 6000 5025 2700
+-6
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+ 6375 4800 6675 4800 6675 5400
+-6
+2 1 0 4 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6375 4425 6375 4875
+-6
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 6675 3600 18 18 6657 3600 6693 3600
+-6
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 3900 600 18 18 3882 600 3918 600
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 4500 600 18 18 4482 600 4518 600
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 3900 1800 18 18 3882 1800 3918 1800
+1 4 0 1 0 0 50 -1 20 0.000 1 0.0000 4500 2400 18 18 4482 2400 4518 2400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 525 600 9000 600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 3900 600 3900 900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4500 600 4500 900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 3750 900 4050 900 4050 1500 3750 1500 3750 900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4350 900 4650 900 4650 1500 4350 1500 4350 900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 525 1800 9000 1800
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 525 2400 9000 2400
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 3900 1500 3900 1800
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4500 1500 4500 2400
+4 0 0 50 -1 0 12 0.0000 4 135 405 600 525 VDD\001
+4 0 0 50 -1 0 12 0.0000 4 135 375 600 1725 SDA\001
+4 0 0 50 -1 0 12 0.0000 4 135 330 600 2325 SCL\001
Index: io/i2c/current/doc/i2c_hw.png
===================================================================
RCS file: io/i2c/current/doc/i2c_hw.png
diff -N io/i2c/current/doc/i2c_hw.png
Binary files /dev/null and i2c_hw.png differ
Index: io/i2c/current/include/i2c.h
===================================================================
RCS file: io/i2c/current/include/i2c.h
diff -N io/i2c/current/include/i2c.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/include/i2c.h 20 Apr 2005 11:55:40 -0000
@@ -0,0 +1,168 @@
+#ifndef CYGONCE_IO_I2C_H
+#define CYGONCE_IO_I2C_H
+
+//=============================================================================
+//
+// i2c.h
+//
+// Generic API for accessing devices on an I2C bus
+//
+//=============================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2004 eCosCentric Limited
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//=============================================================================
+//####DESCRIPTIONBEGIN####
+//
+// Author(s): bartv
+// Date: 2004-10-05
+//####DESCRIPTIONEND####
+//=============================================================================
+
+#include <pkgconf/infra.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/hal/drv_api.h>
+#include <cyg/hal/hal_tables.h>
+#include <cyg/hal/hal_io.h>
+
+// The information needed for interacting with a specific I2C device.
+// The bus provides the tx and rx functions. The address is (usually)
+// a 7-bit number sent at the start of each operation. The flags is
+// not currently used, but provides room for future expansion such as
+// 10-bit addresses. The delay should be a clock period in
+// nanoseconds. For the default 100KHz clock that gives a value of
+// 10000
+
+typedef struct cyg_i2c_device {
+ struct cyg_i2c_bus* i2c_bus;
+ cyg_uint16 i2c_address;
+ cyg_uint16 i2c_flags;
+ cyg_uint32 i2c_delay;
+} cyg_i2c_device;
+
+#define CYG_I2C_DEFAULT_DELAY 10000
+
+// A utility macro for defining an I2C device
+#define CYG_I2C_DEVICE(_name_, _bus_, _address_, _flags_, _delay_) \
+ cyg_i2c_device _name_ = { \
+ .i2c_bus = _bus_, \
+ .i2c_address = _address_, \
+ .i2c_flags = _flags_, \
+ .i2c_delay = _delay_ \
+ }
+
+// The information needed for interacting over a particular I2C bus.
+// Most hardware will only have one bus, but multiple buses are
+// supported. Thread synchronization happens on a per-bus level.
+typedef struct cyg_i2c_bus {
+ cyg_drv_mutex_t i2c_lock;
+#ifdef CYGDBG_USE_ASSERTS
+ const cyg_i2c_device* i2c_current_device;
+#endif
+ // The hardware-specific functions that do the real work
+ void (*i2c_init_fn)(struct cyg_i2c_bus*);
+ cyg_uint32 (*i2c_tx_fn)(const cyg_i2c_device*, cyg_bool, const cyg_uint8*, cyg_uint32, cyg_bool);
+ cyg_uint32 (*i2c_rx_fn)(const cyg_i2c_device*, cyg_bool, cyg_uint8*, cyg_uint32, cyg_bool, cyg_bool);
+ void (*i2c_stop_fn)(const cyg_i2c_device*);
+ // A spare field for use by the driver
+ void* i2c_extra;
+} cyg_i2c_bus;
+
+#define CYG_I2C_BUS(_name_, _init_fn_, _tx_fn_, _rx_fn_, _stop_fn_, _extra_) \
+ cyg_i2c_bus _name_ CYG_HAL_TABLE_ENTRY( i2c_buses ) = { \
+ .i2c_init_fn = _init_fn_, \
+ .i2c_tx_fn = _tx_fn_, \
+ .i2c_rx_fn = _rx_fn_, \
+ .i2c_stop_fn = _stop_fn_, \
+ .i2c_extra = _extra_ \
+ }
+
+// To support static initialization all buses are held in a table.
+// Of course usually this table will only contain a single entry
+extern cyg_i2c_bus cyg_i2c_buses[], cyg_i2c_buses_end;
+
+// The main exported interface. There are high-level operations for
+// simple transmits and receives, and transaction-oriented routines
+// for more complicated operations including those involving repeated
+// starts.
+externC cyg_uint32 cyg_i2c_tx(const cyg_i2c_device*, const cyg_uint8*, cyg_uint32);
+externC cyg_uint32 cyg_i2c_rx(const cyg_i2c_device*, cyg_uint8*, cyg_uint32);
+externC void cyg_i2c_transaction_begin(const cyg_i2c_device*);
+externC cyg_bool cyg_i2c_transaction_begin_nb(const cyg_i2c_device*);
+externC cyg_uint32 cyg_i2c_transaction_tx(const cyg_i2c_device*, cyg_bool, const cyg_uint8*, cyg_uint32, cyg_bool);
+externC cyg_uint32 cyg_i2c_transaction_rx(const cyg_i2c_device*, cyg_bool, cyg_uint8*, cyg_uint32, cyg_bool, cyg_bool);
+externC void cyg_i2c_transaction_stop(const cyg_i2c_device*);
+externC void cyg_i2c_transaction_end(const cyg_i2c_device*);
+
+// Bit-bang support. This merely requires the platform HAL to provide
+// a function for the actual bit-bang operation. The rest is handled
+// by the generic code.
+typedef enum cyg_i2c_bitbang_op {
+ CYG_I2C_BITBANG_INIT = 0,
+ CYG_I2C_BITBANG_SCL_HIGH = 1,
+ CYG_I2C_BITBANG_SCL_LOW = 2,
+ CYG_I2C_BITBANG_SCL_HIGH_CLOCKSTRETCH = 3,
+ CYG_I2C_BITBANG_SCL_LOW_SDA_INPUT = 4,
+ CYG_I2C_BITBANG_SDA_OUTPUT = 5,
+ CYG_I2C_BITBANG_SDA_HIGH = 6,
+ CYG_I2C_BITBANG_SDA_LOW = 7,
+ CYG_I2C_BITBANG_SDA_READ = 8
+} cyg_i2c_bitbang_op;
+
+typedef cyg_bool (*cyg_i2c_bitbang_fn)(cyg_i2c_bus*, cyg_i2c_bitbang_op);
+
+// A bitbang bus can be instantiated by just providing the bitbang function.
+// That is held in the extra field.
+#define CYG_I2C_BITBANG_BUS(_name_, _bitbang_fn_) \
+ CYG_I2C_BUS(_name_, \
+ &cyg_i2c_bitbang_init, \
+ &cyg_i2c_bitbang_tx, \
+ &cyg_i2c_bitbang_rx, \
+ &cyg_i2c_bitbang_stop, \
+ (void*) _bitbang_fn_)
+
+// The generic bit-bang functions. These are not called directly,
+// but must be exported for use by the above macro.
+externC void cyg_i2c_bitbang_init(cyg_i2c_bus*);
+externC cyg_uint32 cyg_i2c_bitbang_tx(const cyg_i2c_device*, cyg_bool, const cyg_uint8*, cyg_uint32, cyg_bool);
+externC cyg_uint32 cyg_i2c_bitbang_rx(const cyg_i2c_device*, cyg_bool, cyg_uint8*, cyg_uint32, cyg_bool, cyg_bool);
+externC void cyg_i2c_bitbang_stop(const cyg_i2c_device*);
+
+// Allow the HAL to export the buses and named devices. There are
+// circular dependencies between this header and the HAL ones so
+// it is not easy for the HAL headers to define cyg_i2c_device
+// externs directly. Instead the HAL can define this macro which
+// provides the externs.
+#ifdef HAL_I2C_EXPORTED_DEVICES
+ HAL_I2C_EXPORTED_DEVICES
+#endif
+
+//-----------------------------------------------------------------------------
+#endif // ifndef CYGONCE_IO_I2C_H
+// End of i2c.h
Index: io/i2c/current/src/i2c.cxx
===================================================================
RCS file: io/i2c/current/src/i2c.cxx
diff -N io/i2c/current/src/i2c.cxx
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ io/i2c/current/src/i2c.cxx 20 Apr 2005 11:55:40 -0000
@@ -0,0 +1,519 @@
+//==========================================================================
+//
+// i2c.cxx
+//
+// Generic API for accessing devices attached to an I2C bus
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2004, 2005 eCosCentric Limited
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//###DESCRIPTIONBEGIN####
+//
+// Author(s): bartv
+// Date: 2004-10-05
+//
+//###DESCRIPTIONEND####
+//========================================================================
+
+#include <pkgconf/infra.h>
+#include <pkgconf/io_i2c.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/diag.h>
+#include <cyg/io/i2c.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/hal_diag.h>
+
+// ----------------------------------------------------------------------------
+// Static initialization.
+//
+// This is somewhat tricky. There is a strong argument for leaving the
+// initialization to the platform HAL since with most I2C buses that
+// code will be straightforward. However that would mean that the
+// cyg_i2c_bus structure(s) and associated code would always get
+// linked in, even if the application does not use any i2c devices.
+// Instead to get the maximum benefit of linker garbage collection
+// initialization is handled by the generic I2C code, using the usual
+// dummy C++ object with a prioritized constructor. There is a dummy
+// references to this object from the transaction end function. The
+// result should be that if the application uses any I2C functionality
+// then all required code and data should get included, otherwise it
+// will all be elided.
+//
+// The init priority is configurable, defaulting to CYG_INIT_DRIVERS.
+// Arguably it should happen a bit earlier to allow other drivers to
+// perform I2C operations, but there is no CYG_INIT_BUS.
+//
+// All I2C buses are kept in a table, so that the init code can
+// iterate through each one.
+
+CYG_HAL_TABLE_BEGIN(cyg_i2c_buses, i2c_buses);
+CYG_HAL_TABLE_END(cyg_i2c_buses_end, i2c_buses);
+
+class cyg_i2c_init {
+ public:
+ cyg_uint8 i2c_dummy;
+ cyg_i2c_init();
+};
+
+static cyg_i2c_init cyg_i2c_init_object CYGBLD_ATTRIB_INIT_PRI(CYGNUM_I2C_INIT_PRIORITY);
+
+cyg_i2c_init::cyg_i2c_init()
+{
+ cyg_i2c_bus* bus;
+
+ cyg_i2c_init_object.i2c_dummy = 0;
+ for (bus = &(cyg_i2c_buses[0]); bus != &cyg_i2c_buses_end; bus++) {
+ cyg_drv_mutex_init(&(bus->i2c_lock));
+#ifdef CYGDBG_USE_ASSERTS
+ bus->i2c_current_device = (const cyg_i2c_device*) 0;
+#endif
+ if ((void (*)(const cyg_i2c_bus*))0 != bus->i2c_init_fn) {
+ (*bus->i2c_init_fn)(bus);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// The main exported routines just operate in terms of the transaction ones.
+
+extern "C" cyg_uint32
+cyg_i2c_tx(const cyg_i2c_device* dev, const cyg_uint8* buf, cyg_uint32 count)
+{
+ cyg_uint32 result;
+ cyg_i2c_transaction_begin(dev);
+ result = cyg_i2c_transaction_tx(dev, true, buf, count, true);
+ cyg_i2c_transaction_end(dev);
+ return result;
+}
+
+extern "C" cyg_uint32
+cyg_i2c_rx(const cyg_i2c_device* dev, cyg_uint8* buf, cyg_uint32 count)
+{
+ cyg_uint32 result;
+ cyg_i2c_transaction_begin(dev);
+ result = cyg_i2c_transaction_rx(dev, true, buf, count, true, true);
+ cyg_i2c_transaction_end(dev);
+ return result;
+}
+
+// Transaction begin/end relate to the per-bus locking. There does not
+// seem to be any need to interact with the hardware at this point.
+extern "C" void
+cyg_i2c_transaction_begin(const cyg_i2c_device* dev)
+{
+ cyg_i2c_bus* bus;
+
+ CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+ bus = dev->i2c_bus;
+ CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+
+ while (! cyg_drv_mutex_lock(&(bus->i2c_lock)));
+#ifdef CYGDBG_USE_ASSERTS
+ bus->i2c_current_device = dev;
+#endif
+ // All done, return with the bus locked.
+}
+
+extern "C" cyg_bool
+cyg_i2c_transaction_begin_nb(const cyg_i2c_device* dev)
+{
+ cyg_bool result = false;
+ cyg_i2c_bus* bus;
+
+ CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+ bus = dev->i2c_bus;
+ CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+
+ if (cyg_drv_mutex_trylock(&(bus->i2c_lock))) {
+#ifdef CYGDBG_USE_ASSERTS
+ bus->i2c_current_device = dev;
+#endif
+ result = true;
+ }
+
+ return result;
+}
+
+extern "C" void
+cyg_i2c_transaction_end(const cyg_i2c_device* dev)
+{
+ cyg_i2c_bus* bus;
+
+ CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+ bus = dev->i2c_bus;
+ CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+ CYG_PRECONDITION(bus->i2c_current_device == dev, "I2C transaction end requested without claiming the bus");
+
+ cyg_drv_mutex_unlock(&(bus->i2c_lock));
+#ifdef CYGDBG_USE_ASSERTS
+ bus->i2c_current_device = (const cyg_i2c_device*)0;
+#endif
+ // Avoid problems with linker garbage collection
+ cyg_i2c_init_object.i2c_dummy = 0;
+}
+
+// The I/O operations just indirect through the per-bus function pointers
+extern "C" cyg_uint32
+cyg_i2c_transaction_tx(const cyg_i2c_device* dev, cyg_bool start, const cyg_uint8* buf, cyg_uint32 count, cyg_bool stop)
+{
+ cyg_i2c_bus* bus;
+ cyg_uint32 result;
+
+ CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+ bus = dev->i2c_bus;
+ CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+ CYG_PRECONDITION(bus->i2c_current_device == dev, "I2C transaction end requested without claiming the bus");
+ CYG_CHECK_FUNC_PTR(bus->i2c_tx_fn, "I2C bus has not provided a tx function");
+
+ result = (*(bus->i2c_tx_fn))(dev, start, buf, count, stop);
+ return result;
+}
+
+extern "C" cyg_uint32
+cyg_i2c_transaction_rx(const cyg_i2c_device* dev, cyg_bool start, cyg_uint8* buf, cyg_uint32 count, cyg_bool nak, cyg_bool stop)
+{
+ cyg_i2c_bus* bus;
+ cyg_uint32 result;
+
+ CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+ bus = dev->i2c_bus;
+ CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+ CYG_PRECONDITION(bus->i2c_current_device == dev, "I2C transaction end requested without claiming the bus");
+ CYG_CHECK_FUNC_PTR(bus->i2c_rx_fn, "I2C bus has not provided an rx function");
+
+ result = (*(bus->i2c_rx_fn))(dev, start, buf, count, nak, stop);
+ return result;
+}
+
+extern "C" void
+cyg_i2c_stop(const cyg_i2c_device* dev)
+{
+ cyg_i2c_bus* bus;
+
+ CYG_CHECK_DATA_PTR(dev, "valid I2C device pointer required");
+ bus = dev->i2c_bus;
+ CYG_CHECK_DATA_PTR(bus, "I2C device does not point at a valid bus structure");
+ CYG_PRECONDITION(bus->i2c_current_device == dev, "I2C transaction end requested without claiming the bus");
+ CYG_CHECK_FUNC_PTR(bus->i2c_stop_fn, "I2C bus has not provided a stop function");
+
+ (*(bus->i2c_stop_fn))(dev);
+}
+
+// ----------------------------------------------------------------------------
+// The bit-banging support
+//
+// Optional debug support, useful while sorting out the platform-specific
+// bitbang function.
+#if 1
+# define DEBUG(_format_, ...)
+#else
+# define DEBUG(_format_, ...) diag_printf(format, ## __VA_ARGS__)
+#endif
+
+
+// Initialization calls into the h/w specific bit-bang function to set
+// up the GPIO pins and to set both SCL and SDA as outputs and high.
+
+extern "C" void
+cyg_i2c_bitbang_init(cyg_i2c_bus* mash)
+{
+ cyg_i2c_bitbang_fn banger = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
+ CYG_CHECK_FUNC_PTR(banger, "bitbanged i2c bus has not provided a bitbang function");
+
+ DEBUG("i2c bitbang init\n");
+ (*banger)(mash, CYG_I2C_BITBANG_INIT);
+}
+
+// Send a single byte out of the bus and get back the acknowledgement.
+// This may be the addressing byte or a data byte.
+// Preconditions:
+// SCL output low. The previous operation has completed, the
+// acknowledgement bit has been received and processed.
+// None of the devices should be driving the bus.
+// SDA output, indeterminate value.
+// Postconditions:
+// SCL output low, the acknowledgement has been received.
+// SDA may be left as an input or an output, depending on the
+// last argument
+//
+// The return value is the acknowledge bit, i.e. 0 for ack, 1 for
+// nak.
+
+static cyg_bool
+cyg_i2c_bitbang_send_byte(cyg_i2c_bus* mash, const cyg_i2c_device* dev, cyg_uint32 delay, cyg_uint8 data, cyg_bool leave_as_input)
+{
+ cyg_i2c_bitbang_fn banger = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
+ cyg_uint8 mask;
+ cyg_bool result;
+
+ DEBUG("i2c bitbang send_byte %02x, leave_as_input %d\n", data, leave_as_input);
+
+ for (mask = 0x0080; 0x00 != mask; ) {
+ // Transfer the next bit of data
+ (*banger)(mash, (0 != (data & mask)) ? CYG_I2C_BITBANG_SDA_HIGH : CYG_I2C_BITBANG_SDA_LOW);
+ mask = mask >> 1;
+ // We should now be able to just set the clock high and wait
+ // for the delay period. However if the device is having
+ // difficulty keeping up then it may perform clock stretching,
+ // basically keeping the clock low for a period of time.
+ // SCL_CLOCKSTRETCH means set the clock high, then wait
+ // until it really has gone high.
+ (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH_CLOCKSTRETCH);
+ // The device is now busy sampling the bit.
+ HAL_DELAY_US(delay);
+ // Unless this was the last bit, just drop the clock. If it is
+ // the last bit switch SDA to an input as well.
+ (*banger)(mash, (0 != mask) ? CYG_I2C_BITBANG_SCL_LOW : CYG_I2C_BITBANG_SCL_LOW_SDA_INPUT);
+ HAL_DELAY_US(delay);
+ }
+ // The last bit has been sent, SCL is low, and SDA has been turned
+ // into an input. The device should be placing the acknowledge bit
+ // onto SDA. Reading the acknowledge bit still needs a clock cycle.
+ (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
+ HAL_DELAY_US(delay);
+ result = (*banger)(mash, CYG_I2C_BITBANG_SDA_READ);
+ DEBUG("i2c bitbang send_byte, got ack %d\n", result);
+
+ // Drop SCL again, and leave SDA as either an input or an output
+ // depending on the calling code's requirements and whether or not
+ // the device ack'd.
+ (*banger)(mash, CYG_I2C_BITBANG_SCL_LOW);
+ HAL_DELAY_US(delay);
+ if ((1 == result) || !leave_as_input) {
+ (*banger)(mash, CYG_I2C_BITBANG_SDA_OUTPUT);
+ }
+
+ return result;
+}
+
+// Read a single byte from the bus and generate an ack/nak.
+// Preconditions:
+// SCL output low. The previous operation has completed.
+// The device should have placed the data on the bus.
+// SDA input
+// Postconditions:
+// SCL output low. The acknowledgement has been sent.
+// SDA may be left as an input or an output, depending on
+// whether or not this is the last byte in a transfer.
+// For the last byte we want to send a nak.
+//
+// The result is the byte read.
+
+static cyg_uint8
+cyg_i2c_bitbang_read_byte(cyg_i2c_bus* mash, const cyg_i2c_device* dev, cyg_uint32 delay, cyg_bool nak)
+{
+ cyg_i2c_bitbang_fn banger = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
+ cyg_uint32 i;
+ cyg_uint8 result = 0;
+
+ DEBUG("i2c bitbang read_byte, nak %d\n", nak);
+
+ for (i = 0; i < 8; i++) {
+ (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
+ HAL_DELAY_US(delay);
+ result = result << 1;
+ if (0 != (*banger)(mash, CYG_I2C_BITBANG_SDA_READ)) {
+ result |= 0x01;
+ }
+ (*banger)(mash, CYG_I2C_BITBANG_SCL_LOW);
+ HAL_DELAY_US(delay);
+ }
+ // We have read the last bit. SDA is still an input, SCL is low.
+ // We need to switch SDA to an output and send the ack/nak
+ (*banger)(mash, CYG_I2C_BITBANG_SDA_OUTPUT);
+ (*banger)(mash, nak ? CYG_I2C_BITBANG_SDA_HIGH : CYG_I2C_BITBANG_SDA_LOW);
+ (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
+ HAL_DELAY_US(delay);
+ (*banger)(mash, nak ? CYG_I2C_BITBANG_SCL_LOW : CYG_I2C_BITBANG_SCL_LOW_SDA_INPUT);
+ HAL_DELAY_US(delay);
+
+ DEBUG("i2c bitbang read_byte, result %02x\n", result);
+ return result;
+}
+
+// Generate a start condition.
+//
+// Preconditions:
+// SCL output, indeterminate
+// SDA output, indeterminate
+// Postconditions:
+// SCL output low
+// SDA output low
+//
+// At the start of a transaction we know that both SCL and SDA will be
+// high, but for a repeated start
+static void
+cyg_i2c_bitbang_send_start(cyg_i2c_bus* mash, cyg_uint32 delay)
+{
+ cyg_i2c_bitbang_fn banger = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
+
+ DEBUG("i2c bitbang, generating start\n");
+ // First get both SCL and SDA back into a known state
+ (*banger)(mash, CYG_I2C_BITBANG_SDA_HIGH);
+ (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
+ HAL_DELAY_US(delay);
+ // A start condition involves dropping SDA while SCL stays high.
+ (*banger)(mash, CYG_I2C_BITBANG_SDA_LOW);
+ // Make sure that all devices have seen the start condition and
+ // reacted
+ HAL_DELAY_US(delay);
+ // Drop the clock, so that we can send the address/direction byte
+ (*banger)(mash, CYG_I2C_BITBANG_SCL_LOW);
+ HAL_DELAY_US(delay);
+}
+
+// Generate a stop condition
+// Preconditions:
+// SCL output, low
+// SDA output, indeterminate
+// Postconditions:
+// SCL output, high
+// SDA output, high
+static void
+cyg_i2c_bitbang_send_stop(cyg_i2c_bus* mash, cyg_uint32 delay)
+{
+ cyg_i2c_bitbang_fn banger = (cyg_i2c_bitbang_fn)(mash->i2c_extra);
+
+ DEBUG("i2c bitbang, generating stop\n");
+ // We need SDA low, then we can generate the stop signal
+ (*banger)(mash, CYG_I2C_BITBANG_SDA_LOW);
+ (*banger)(mash, CYG_I2C_BITBANG_SCL_HIGH);
+ HAL_DELAY_US(delay);
+ (*banger)(mash, CYG_I2C_BITBANG_SDA_HIGH);
+ // Ensure that the minimum delay between stop and start is observed
+ HAL_DELAY_US(delay);
+}
+
+// A full transfer to a given device, within in a transaction.
+extern "C" cyg_uint32
+cyg_i2c_bitbang_tx(const cyg_i2c_device* dev, cyg_bool send_start, const cyg_uint8* buf, cyg_uint32 count, cyg_bool send_stop)
+{
+ cyg_uint32 result = 0;
+ // The bit-bang code works in terms of us delays rather than ns
+ cyg_uint32 delay = (0 == dev->i2c_delay) ? 1 : (dev->i2c_delay + 1999) / 2000;
+
+ CYG_CHECK_DATA_PTR(buf, "i2c tx operation requested but no data supplied");
+ CYG_PRECONDITION(count > 0, "at least one byte should be sent");
+
+ DEBUG("i2c bitbang tx, dev %08x, addr %02x, delay %d, send_start %d, buf %08x, count %d, send_stop %d\n",
+ dev, dev->i2c_address, delay, send_start, buf, count, send_stop);
+
+ if (send_start) {
+ cyg_i2c_bitbang_send_start(dev->i2c_bus, delay);
+
+ // Now send a single byte, holding the address shifted left and a
+ // 0 to indicate a write. A nak indicates that the device has not
+ // responded. SDA should stay as an output.
+ if (0 != cyg_i2c_bitbang_send_byte(dev->i2c_bus, dev, delay, (dev->i2c_address << 1) | 0x00, false) ) {
+ // Get the bus back in a consistent state
+ DEBUG("i2 bitbang tx, no device has responded to address %02x\n", dev->i2c_address);
+ cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
+ return 0;
+ }
+ // The device has ack'd so we can continue sending
+ }
+ // Send all bytes, unless we receive a nak. SDA should remain an output
+ do {
+ result += 1;
+ if (0 != cyg_i2c_bitbang_send_byte(dev->i2c_bus, dev, delay, buf[result - 1], false)) {
+ break;
+ }
+ } while (result < count);
+
+ // At this point we have SCL low and SDA an indeterminate output
+ if (send_stop) {
+ cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
+ }
+ DEBUG("i2c bitbang tx, %d bytes sent out of %d\n", result, count);
+
+ return result;
+}
+
+// A full transfer from a given device. The bus has already been locked
+// by higher-level code. On entry both SDA and SCL should be outputs
+// and both should be high. The same conditions should hold on exit.
+extern "C" cyg_uint32
+cyg_i2c_bitbang_rx(const cyg_i2c_device* dev, cyg_bool send_start, cyg_uint8* buf, cyg_uint32 count, cyg_bool send_nak, cyg_bool send_stop)
+{
+ cyg_uint32 result = 0;
+ // The bit-bang code works in terms of us delays rather than ns
+ cyg_uint32 delay = (0 == dev->i2c_delay) ? 1 : (dev->i2c_delay + 1999) / 2000;
+
+ DEBUG("i2c bitbang rx, dev %08x, addr %02x, delay %d, send_start %d, buf %08x, count %d, send_nak %d, send_stop %d\n",
+ dev, dev->i2c_address, delay, send_start, buf, count, send_nak, send_stop);
+
+ CYG_CHECK_DATA_PTR(buf, "i2c rx operation requested but no data supplied");
+ CYG_PRECONDITION(count > 0, "at least one byte should be sent");
+ CYG_PRECONDITION(send_nak || !send_stop, "a stop can only be generated after the receive has been nak'd");
+
+ if (send_start) {
+ cyg_i2c_bitbang_send_start(dev->i2c_bus, delay);
+
+ // Now send a single byte, holding the address shifted left and a
+ // 1 to indicate a read. A nak indicates that the device has not
+ // responded. SDA should become an output.
+ if (0 != cyg_i2c_bitbang_send_byte(dev->i2c_bus, dev, delay, (dev->i2c_address << 1) | 0x01, true) ) {
+ // Get the bus back in a consistent state
+ DEBUG("i2c bitbang rx, no device has responded to address %02x\n", dev->i2c_address);
+ cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
+ return 0;
+ }
+ // The device has ack'd so we can continue sending
+ }
+
+ // The device has ack'd. Read the number of bytes specified. The
+ // device cannot stop sending, although it may clock-stretch. For
+ // the last byte we usually we want to NAK and SDA should become
+ // an input, but if the data stream is variable length with e.g.
+ // the first byte indicating the length then we want to support
+ // partial reads.
+ for (result = 0; result < count; result++) {
+ buf[result] = cyg_i2c_bitbang_read_byte(dev->i2c_bus, dev, delay, (send_nak && (result == (count - 1))) ? true : false);
+ }
+
+ // At this point we have SCL low and SDA an indeterminate output
+ if (send_stop) {
+ cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
+ }
+
+ DEBUG("i2c bitbang rx, read %d bytes\n", result);
+ return result;
+}
+
+extern "C" void
+cyg_i2c_bitbang_stop(const cyg_i2c_device* dev)
+{
+ // The bit-bang code works in terms of us delays rather than ns
+ cyg_uint32 delay = (0 == dev->i2c_delay) ? 1 : (dev->i2c_delay + 1999) / 2000;
+ cyg_i2c_bitbang_send_stop(dev->i2c_bus, delay);
+}