📄 i2c.cxx
字号:
//==========================================================================
//
// 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 (*)(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_transaction_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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -