Mercurial > ecos
changeset 1951:ef6fdda1d7ee
Add generic I2C support
| author | bartv |
|---|---|
| date | Wed, 20 Apr 2005 12:25:28 +0000 |
| parents | 161789c1afd9 |
| children | 85b770765317 |
| files | packages/ecos.db packages/io/i2c/current/ChangeLog packages/io/i2c/current/cdl/i2c.cdl packages/io/i2c/current/doc/i2c.sgml packages/io/i2c/current/doc/i2c_hw.fig packages/io/i2c/current/doc/i2c_hw.png packages/io/i2c/current/include/i2c.h packages/io/i2c/current/src/i2c.cxx |
| diffstat | 8 files changed, 1965 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/ecos.db +++ b/packages/ecos.db @@ -10,8 +10,8 @@ #####ECOSGPLCOPYRIGHTBEGIN#### ## ------------------------------------------- ## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2004, 2005 eCosCentric Limited ## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. -## 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 @@ -2085,6 +2085,18 @@ package CYGPKG_IO_SPI { 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
new file mode 100644 --- /dev/null +++ b/packages/io/i2c/current/ChangeLog @@ -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#### +//===========================================================================
new file mode 100644 --- /dev/null +++ b/packages/io/i2c/current/cdl/i2c.cdl @@ -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." + } + } +}
new file mode 100644 --- /dev/null +++ b/packages/io/i2c/current/doc/i2c.sgml @@ -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>
new file mode 100644 --- /dev/null +++ b/packages/io/i2c/current/doc/i2c_hw.fig @@ -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
new file mode 100644 index 0000000000000000000000000000000000000000..691aac449fa0f3a55082c8b9112bd70bcf7e21fa GIT binary patch literal 5318 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
new file mode 100644 --- /dev/null +++ b/packages/io/i2c/current/include/i2c.h @@ -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
new file mode 100644 --- /dev/null +++ b/packages/io/i2c/current/src/i2c.cxx @@ -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); +}
