# HG changeset patch # User bartv # Date 1113999928 0 # Node ID ef6fdda1d7ee9997cfe6d41bbd705c7bc11ba77c # Parent 161789c1afd92ed4b7ef8d3f600ad88f6769e7c6 Add generic I2C support diff --git a/packages/ecos.db b/packages/ecos.db --- 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 diff --git a/packages/io/i2c/current/ChangeLog b/packages/io/i2c/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/io/i2c/current/ChangeLog @@ -0,0 +1,40 @@ +2005-03-03 Bart Veer + + * 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 + + * 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#### +//=========================================================================== diff --git a/packages/io/i2c/current/cdl/i2c.cdl b/packages/io/i2c/current/cdl/i2c.cdl 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." + } + } +} diff --git a/packages/io/i2c/current/doc/i2c.sgml b/packages/io/i2c/current/doc/i2c.sgml new file mode 100644 --- /dev/null +++ b/packages/io/i2c/current/doc/i2c.sgml @@ -0,0 +1,941 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +I2C Support + + + + Overview + + + Overview + eCos Support for I2C, the Inter IC Bus + + + Description + +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. + + +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. + + +An I2C data transfer involves a number of stages: + + + +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. + + +The bus master clocks the 7-bit slave address onto the SDA line, +followed by a direction bit to distinguish between reads and writes. + + +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. + + +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. + + +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. + + +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. + + +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. + + + +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. + + +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. + + + + eCos Support for I2C + +The eCos I2C support for any given platform is spread over a number of +different packages: + + + +This package, CYGPKG_IO_I2C, 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. + + +CYGPKG_IO_I2C 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. + + +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. + + +The generic API depends on cyg_i2c_device +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. + + +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. + + +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. + + +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 +cyg_i2c_device structures directly to access +the hardware. + + + +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. + + +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. + + + + + + + + I2C Interface + + + I2C Functions + allow applications and other packages to access I2C devices + + + + +#include <cyg/io/i2c.h> + + + cyg_uint32 cyg_i2c_tx + const cyg_i2c_device* device + const cyg_uint8* tx_data + cyg_uint32 count + + + cyg_uint32 cyg_i2c_rx + const cyg_i2c_device* device + const cyg_uint8* tx_data + cyg_uint32 count + + + void cyg_i2c_transaction_begin + const cyg_i2c_device* device + + + cyg_bool cyg_i2c_transaction_begin_nb + const cyg_i2c_device* device + + + cyg_uint32 cyg_i2c_transaction_tx + const cyg_i2c_device* device + cyg_bool send_start + const cyg_uint8* tx_data + cyg_uint32 count + cyg_bool send_stop + + + cyg_uint32 cyg_i2c_transaction_rx + const cyg_i2c_device* device + cyg_bool send_start + const cyg_uint8* tx_data + cyg_uint32 count + cyg_bool send_nack + cyg_bool send_stop + + + void cyg_i2c_transaction_stop + const cyg_i2c_device* device + + + void cyg_i2c_transaction_end + const cyg_i2c_device* device + + + + + Description + +All I2C functions take a pointer to a +cyg_i2c_device 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. + + +An I2C transaction involves the following stages: + + + +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. + + +Generate a start condition, send the address and direction bit, and +wait for an acknowledgement from the addressed device. + + +Either transmit data to or receive data from the addressed device. + + +The previous two steps may be repeated several times, allowing data to +move in both directions during a single transfer. + + +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. + + +End the transaction by unlocking the bus, allowing other threads to +access other devices on the bus. + + + +The simple functions cyg_i2c_tx and +cyg_i2c_rx 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. + + +With the exception of +cyg_i2c_transaction_begin_nb 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 +count 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. + + +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. + + + +Simple Transfers + +cyg_i2c_tx and cyg_i2c_rx +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. + + +cyg_i2c_tx 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. + + +cyg_i2c_rx 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. + + + + Transactions + +To allow multiple threads to access devices on the I2C some locking is +required. This is encapsulated inside transactions. The +cyg_i2c_tx and cyg_i2c_rx +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. + + +cyg_i2c_transaction_begin 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. + + +cyg_i2c_transaction_begin_nb 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 +cyg_i2c_transaction_begin and returns true. + + +Once the bus has been locked it is possible to perform one or more +data transfers by calling +cyg_i2c_transaction_tx, +cyg_i2c_transaction_rx and +cyg_i2c_transaction_stop. Code should ensure that +a stop condition has been generated by the end of a transaction. + + +Once the transaction is complete +cyg_i2c_transaction_end should be called. This +unlocks the bus, allowing other threads to perform I2C I/O to devices +on the same bus. + + +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: + + + 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); + + +Here hal_alaia_i2c_fs6377 is a +cyg_i2c_device 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. + + +The arguments to cyg_i2c_transaction_tx are as +follows: + + + + const cyg_i2c_device* device + +This identifies the I2C device that should be used. + + + + cyg_bool send_start + +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. + +send_start must be true if the tx call is the first +operation in a transaction, or if the previous call was an rx or stop. + + + + const cyg_uint8* tx_data + cyg_uint32 count + +These arguments specify the data to be transmitted to the device. + + + + cyg_bool send_stop + +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. + + + + +The arguments to cyg_i2c_transaction_rx are as +follows: + + + + const cyg_i2c_device* device + +This identifies the I2C device that should be used. + + + + cyg_bool send_start + +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. + +send_start 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 send_nack +flag was set. + + + + cyg_uint8* rx_data + cyg_uint32 count + +These arguments specify how much data should be received and where it +should be placed. + + + + cyg_bool send_nack + +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. +send_nack should be set to false only if +send_stop is also false, the next operation will be +another rx, and that rx does not specify send_start. + + + + cyg_bool send_stop + +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. + + + + +The final transaction-oriented function is +cyg_i2c_transaction_stop. 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 +send_stop flag. A stop condition must be generated +before the transaction is ended. + + + + Initialization + +The generic package CYGPKG_IO_I2C 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 +CYG_INIT_DRIVERS, but this can be changed via the +configuration option CYGNUM_I2C_INIT_PRIORITY. +Other code should not try to access any of the I2C devices until after +the buses have been initialized. + + + + + + + + Porting to New Hardware + + + Porting + Adding I2C support to new hardware + + + Description + +Adding I2C support to an eCos port involves a number of steps. The +generic I2C package CYGPKG_IO_I2C should be +included in the appropriate ecos.db target entry +or entries. Next cyg_i2c_device 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 cyg_i2c_bus +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. + + + + Adding a Device + +The eCos I2C API works in terms of +cyg_i2c_device structures, and these provide +the information needed to access the hardware. A +cyg_i2c_device structure contains the +following fields: + + + + cyg_i2c_bus* i2c_bus + +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. + + + + cyg_uint16 i2c_address + +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. + + + + cyg_uint16 i2c_flags + +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. + + + + cyg_uint32 i2c_delay + +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 cyg/io/i2c.h provides a +#define CYG_I2C_DEFAULT_DELAY +for this. Sometimes it may be desirable to use a slower clock, for +example to reduce noise problems. + + + + +The normal way to instantiate a cyg_i2c_device +structure uses the CYG_I2C_DEVICE macro, also +provided by cyg/io/i2c.h: + + +#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); + + +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, +cyg_i2c_wallclock_ds1307 and +hal_alaia_i2c_fs6377, which can be used for the +first argument to the cyg_i2c_ 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. + + +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 cyg/hal/plf_io.h, which gets included +automatically via one of the main exported HAL header files cyg/hal/hal_io.h. 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 +HAL_I2C_EXPORTED_DEVICES: + + +# 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; + + +This macro gets expanded automatically by cyg/io/i2c.h 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. + + +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 +cyg_i2c_wallclock_ds1307, 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. + + + + Bit-banged Bus + +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 +CYGPKG_IO_I2C 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. + + +Instantiating a bit-banged I2C bus requires the following: + + +#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); + + +This gives a structure hal_alaia_i2c_bus which can +be used when defining the cyg_i2c_device +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: + + + + + + + + + +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. + + +The bitbang function should support the following operations: + + + + CYG_I2C_BITBANG_INIT + +This will be called during system initialization, as a side effect of +a prioritized C++ static constructor. By default this constructor will +run at CYG_INIT_DRIVERS priority. The bitbang +function should ensure that both SCL and SDA are driven high. + + + + CYG_I2C_BITBANG_SCL_HIGH + CYG_I2C_BITBANG_SCL_LOW + CYG_I2C_BITBANG_SDA_HIGH + CYG_I2C_BITBANG_SDA_LOW + +These operations simply set the appropriate lines high or low. + + + + CYG_I2C_BITBANG_SCL_HIGH_CLOCKSTRETCH + +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. + + + + CYG_I2C_BITBANG_SCL_LOW_SDA_INPUT + +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. + + + + CYG_I2C_BITBANG_SDA_READ + +The SDA line is currently set as an input and the bitbang function +should sample and return the current state. + + + + +The bitbang function returns a boolean. For most operations this +return value is ignored. For +CYG_I2C_BITBANG_SDA_READ it should be the current +level of the SDA line. + + +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. + + + + Full Bus Driver + +If the processor has dedicated I2C hardware then usually this will +involve a separate device driver package in the +devs/i2c hierarchy of the eCos component +repository. That package should also be included in the appropriate +ecos.db target entry or entries. The device +driver may exist already, or it may have to be written from scratch. + + +A new I2C driver basically involves creating an +cyg_i2c_bus structure. The device driver +should supply the following fields: + + + + i2c_init_fn + +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. + + + + i2c_tx_fn + i2c_rx_fn + i2c_stop_fn + +These functions implement the core I2C functionality. The arguments +and results are the same as for the transaction functions +cyg_i2c_transaction_tx, +cyg_i2c_transaction_rx and +cyg_i2c_transaction_stop. + + + + void* i2c_extra + +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. + + + + +To assist with instantiating a cyg_i2c_bus +object the header file cyg/io/i2c.h provides a macro. Typical +usage would be: + + +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); + + +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 i2c_extra field to hold +the hardware-specific bitbang function rather than a pointer to some +data structure. + + + + + + diff --git a/packages/io/i2c/current/doc/i2c_hw.fig b/packages/io/i2c/current/doc/i2c_hw.fig 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 diff --git a/packages/io/i2c/current/doc/i2c_hw.png b/packages/io/i2c/current/doc/i2c_hw.png new file mode 100644 index 0000000000000000000000000000000000000000..691aac449fa0f3a55082c8b9112bd70bcf7e21fa GIT binary patch literal 5318 zc%1E5c~p~E7XQL7qPSGqWKl#MYd~m6)DYr|EE*L_fhb92vymmp4$2~k6ckaGgjNDt zi`Yg;gb>OSAhHCEqQW!;!lsA_Nr;3sgk@qrZD-DznLlQxJ^jN!?)lEW-}~Kn-(BBJ zJ&ruQLQ`K80Du*a4hLNTK*<&WAdwnMa*2h$+LHXUEZDaci2z9&d~TQO2DGz>fB z9(I!87Z!OY#1}Y>M424^nh+Wm>K%d)4l}W_*kSYFQ?|2O zyIv)JCP-$M}rO3pX~m(QzShMTas;)L>)b zu#);m%>P0ZM6(lNES_JBoN~ZUgK4Yl!MmTA4)icC(x*=m6QU1`Qe-|;pA*c~BxzeumyHWG8)+BCt@6s?MaJZl* zR!i;#S2U+2C8#`XYf-poXE<}As9EXu&2~=G%*0O@%tai>6OebRhYF`AV+K0dz+PD4 zS+p5b6*wyQ+3h~10W{TF5Rug+8eI5medcs~FmUnsNN-X~UpFHV>&IyjB?Iry%a>HZ zw&%z7iF5(_prO@oVLuULs&@L-2$|v**u21ej6;*enQZcNW;`&7=kGjx-FR?EO)^JY zP}Qx~=S2#5DT!r?>2J~I@q2Kx0)HE2i%6-(kN=1Cdx&8|f(utJ>5eZv`?ldSW-Jq7 zrv1IVfn)~U@uc-~llIMNRQs5Aj%8YCEBzYcKNl^qt4iDmk8)L<4YN8Nf@n?xY$b)a zo8`iO{pA|N2kmq6|9plgbDi>{ zoUCN4-fFSvQm%e&mgeN&)f9`(jMdDzlh^MO9i4cG66K!UkWVWSEJTwLuxd6S%!JdN zyg2og%kc0c zSq4F~PHH?<;?=tCGrRKj(qXla8#_It_w_nHCX1a5a%$6{U%fPy7O8*uIio0fPAJ0V z=hx>NPRdVOy@Wy;nDP!rv>Nfu>V)BV(J@!&Ab1t_o7p$x-OeMFS^lq#7rAre|=#{4$cHZ~ z1gR-ci}#S|HTx10aI({-b`_&ZYPorKD^>-xMjFijRD;sl7vD7jmQ5$ znyrjcu9DYB$1?(ddI+j_?StvOXlMVrs5+>7tM@L@IZ3K8Z+tJY$mh&-hDT@lM0rgf zxYn9C;zWsoGXg&^NLU7z_=zO)efnapRs~fwzvHzG^d^th=7KgG>8Fn7BT6S4Zdk5W z=xtDkH9^J32n>%Qs?GZX6n>#{D(qf3?-hIa-Iw=}NGPo9nkJc@Zy*Hr6r_ie1MHLA zyq~R@h8B|4#U8eB>P0Pq_-W6QQja}j#J){Wrjuw`pI3Ii&7#Y}t=dOk?GygD>&OU9 zoQpJhSU21q2ML2247ZyX@p&CdP&fI0{2s~iv5a|t#S^?r2mS{Wf^Hb3Q;DxVqmzB<&sUMpnE!;IB( zi}TVY1(K4uEkzAHwg#ieAW}Qu*m^?`cVKAHGJetc#bjgj8=kCk(~F{S&kp8!mfkdq zXzeTv*N)vC+yB$T#sv*d!v(d#A<5X=FCW=;G;OHszsAU94@t@@)3ZgdmF$ zHESmxeYs)UH0Ek-E>8V9KcJ?k+iFWu1utb8qesvl^IePMkrdQl=JU-8zD?sv)4P7Y z%!64q!MrZ+(pR zbo`UZ+#%^MMR9E6MTW~Iwpnll5PkYh1qP~nR8b`w)1G7^S4S%}xxxl4Y8vkB_F@*1 zW(=||xL(Pk-r8D=1sJG%h_ww0cwO^Alj{5~xPprw;n>@uz?C29b_0^DHFv?m0#}iD zlcQ1Gc|&j!1Ggl-+U7>E{~Q~4E9+90t*$B0Xr+PiR3)mwy7_h3*Vk~)`AyfT9Wf)t zRVID@YkZ5#d%AwH-os86v z6H`7w)l;2y_bJ@{S`F5jt_vJI`3E>y$#!7tt~zC4AO{b*aRv?e +#include +#include +#include +#include + +// 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 diff --git a/packages/io/i2c/current/src/i2c.cxx b/packages/io/i2c/current/src/i2c.cxx 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 +#include +#include +#include +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +// 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); +}