From 4ba8614c006f9828f0796c140bc3e13c9e67938c Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:06:01 +0200 Subject: Added utils --- utils/softi2c.c | 1316 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1316 insertions(+) create mode 100644 utils/softi2c.c (limited to 'utils/softi2c.c') diff --git a/utils/softi2c.c b/utils/softi2c.c new file mode 100644 index 0000000..9828534 --- /dev/null +++ b/utils/softi2c.c @@ -0,0 +1,1316 @@ +//***************************************************************************** +// +// softi2c.c - Driver for the SoftI2C. +// +// Copyright (c) 2010-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris Firmware Development Package. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup softi2c_api +//! @{ +// +//***************************************************************************** + +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/gpio.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "utils/softi2c.h" + +//***************************************************************************** +// +// The states in the SoftI2C state machine. The code depends upon the fact +// that the value of STATE_X1 is exactly one greater than STATE_X0 (for any +// value of X and for any following digit)...however there is no dependence on +// the values of STATE_Xn and STATE_Yn. +// +//***************************************************************************** +#define SOFTI2C_STATE_IDLE 0 +#define SOFTI2C_STATE_START0 1 +#define SOFTI2C_STATE_START1 2 +#define SOFTI2C_STATE_START2 3 +#define SOFTI2C_STATE_START3 4 +#define SOFTI2C_STATE_START4 5 +#define SOFTI2C_STATE_START5 6 +#define SOFTI2C_STATE_START6 7 +#define SOFTI2C_STATE_START7 8 +#define SOFTI2C_STATE_ADDR0 9 +#define SOFTI2C_STATE_ADDR1 10 +#define SOFTI2C_STATE_ADDR2 11 +#define SOFTI2C_STATE_ADDR3 12 +#define SOFTI2C_STATE_SEND0 13 +#define SOFTI2C_STATE_SEND1 14 +#define SOFTI2C_STATE_SEND2 15 +#define SOFTI2C_STATE_SEND3 16 +#define SOFTI2C_STATE_RECV0 17 +#define SOFTI2C_STATE_RECV1 18 +#define SOFTI2C_STATE_RECV2 19 +#define SOFTI2C_STATE_RECV3 20 +#define SOFTI2C_STATE_STOP0 21 +#define SOFTI2C_STATE_STOP1 22 +#define SOFTI2C_STATE_STOP2 23 +#define SOFTI2C_STATE_STOP3 24 +#define SOFTI2C_STATE_STOP4 25 + +//***************************************************************************** +// +// The flags in the SoftI2C ucFlags structure member. The first four flags, +// RUN, START, STOP, and ACK, must match with the definitions of the +// SOFTI2C_CMD_* commands in softi2c.h. +// +//***************************************************************************** +#define SOFTI2C_FLAG_RUN 0 +#define SOFTI2C_FLAG_START 1 +#define SOFTI2C_FLAG_STOP 2 +#define SOFTI2C_FLAG_ACK 3 +#define SOFTI2C_FLAG_ADDR_ACK 5 +#define SOFTI2C_FLAG_DATA_ACK 6 +#define SOFTI2C_FLAG_RECEIVE 7 + +//***************************************************************************** +// +//! Performs the periodic update of the SoftI2C module. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! +//! This function performs the periodic, time-based updates to the SoftI2C +//! module. The transmission and reception of data over the SoftI2C link is +//! performed by the state machine in this function. +//! +//! This function must be called at four times the desired SoftI2C clock rate. +//! For example, to run the SoftI2C clock at 10 KHz, this function must be +//! called at a 40 KHz rate. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CTimerTick(tSoftI2C *pI2C) +{ + // + // Determine the current state of the state machine. + // + switch(pI2C->ucState) + { + // + // The state machine is idle. + // + case SOFTI2C_STATE_IDLE: + { + // + // See if the START flag is set, indicating that a start condition + // should be generated. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_START) == 1) + { + // + // Based on the current state of the SCL and SDA pins, pick the + // appropriate place within the state machine to begin the + // start/repeated-start signalling. + // + if(HWREG(pI2C->ulSCLGPIO) != 0) + { + pI2C->ucState = SOFTI2C_STATE_START4; + } + else if(HWREG(pI2C->ulSDAGPIO) == 0) + { + pI2C->ucState = SOFTI2C_STATE_START0; + } + else + { + pI2C->ucState = SOFTI2C_STATE_START2; + } + } + + // + // Otherwise, see if the RUN flag is set, indicating that a data + // byte should be transferred. + // + else if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RUN) == 1) + { + // + // Start the transfer from the first bit. + // + pI2C->ucCurrentBit = 0; + + // + // See if a byte should be sent or received. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RECEIVE) == 0) + { + // + // A byte should be sent. + // + pI2C->ucState = SOFTI2C_STATE_SEND0; + } + else + { + // + // A byte should be received. Clear out the receive data + // buffer in preparation for receiving the new byte. + // + pI2C->ucData = 0; + pI2C->ucState = SOFTI2C_STATE_RECV0; + } + } + + // + // Otherwise, see if the STOP flag is set, indicating that a stop + // condition should be generated. + // + else if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Generate a stop condition. + // + pI2C->ucState = SOFTI2C_STATE_STOP0; + } + + // + // See if the SoftI2C state machine has left the idle state. + // + if(pI2C->ucState != SOFTI2C_STATE_IDLE) + { + // + // The address and data ACK error flags should be cleared; they + // will be set if appropriate while the current command is + // being executed. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_ADDR_ACK) = 0; + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_DATA_ACK) = 0; + } + + // + // This state has been handled. + // + break; + } + + // + // The beginning of the start condition sequence when SDA and SCL are + // low. SDA must be driven high prior to driving SCL high so that a + // repeated-start is generated, instead of a stop then start. + // + case SOFTI2C_STATE_START0: + { + // + // Set SDA high. + // + HWREG(pI2C->ulSDAGPIO) = 255; + + // + // Advance to the next state. + // + pI2C->ucState = SOFTI2C_STATE_START1; + + // + // This state has been handled. + // + break; + } + + // + // Each of these states exists only to provide some timing delay in + // order to conform with the signalling requirements of I2C. This + // depends upon STATE_Xn and STATE_X(n+1) being consecutively numbered. + // + case SOFTI2C_STATE_START1: + case SOFTI2C_STATE_START3: + case SOFTI2C_STATE_START5: + case SOFTI2C_STATE_STOP1: + case SOFTI2C_STATE_STOP3: + { + // + // Advance to the next state. + // + pI2C->ucState++; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, SCL must be driven high. This depends upon + // STATE_Xn and STATE_X(n+1) being consecutively numbered. + // + case SOFTI2C_STATE_START2: + case SOFTI2C_STATE_ADDR1: + case SOFTI2C_STATE_SEND1: + case SOFTI2C_STATE_RECV1: + case SOFTI2C_STATE_STOP2: + { + // + // Set SCL high. + // + HWREG(pI2C->ulSCLGPIO) = 255; + + // + // Advance to the next state. + // + pI2C->ucState++; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, SDA must be driven low. This depends upon + // STATE_Xn and STATE_X(n+1) being consecutively numbered. + // + case SOFTI2C_STATE_START4: + case SOFTI2C_STATE_STOP0: + { + // + // Set SDA low. + // + HWREG(pI2C->ulSDAGPIO) = 0; + + // + // Advance to the next state. + // + pI2C->ucState++; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. + // + case SOFTI2C_STATE_START6: + { + // + // Set SCL low. + // + HWREG(pI2C->ulSCLGPIO) = 0; + + // + // Advance to the next state. + // + pI2C->ucState = SOFTI2C_STATE_START7; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the start condition has been generated. + // + case SOFTI2C_STATE_START7: + { + // + // Start with the first bit of the address. + // + pI2C->ucCurrentBit = 0; + + // + // Advance to the address output state. + // + pI2C->ucState = SOFTI2C_STATE_ADDR0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the next bit of the slave address must be sent. + // + case SOFTI2C_STATE_ADDR0: + { + // + // See if this is one of the first seven bits of the address phase. + // + if(pI2C->ucCurrentBit < 7) + { + // + // Write the next bit of the slave address to SDA. + // + HWREG(pI2C->ulSDAGPIO) = ((pI2C->ucSlaveAddr & + (1 << (6 - pI2C->ucCurrentBit))) ? + 255 : 0); + } + + // + // Otherwise, see if this is the eight bit of the address phase + // (which is the read/not write bit). + // + else if(pI2C->ucCurrentBit == 7) + { + // + // Write the read/not write bit to SDA. + // + HWREG(pI2C->ulSDAGPIO) = + (HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RECEIVE) ? + 255 : 0); + } + + // + // Otherwise, this is the ninth bit of the address phase (in other + // words, the ACK bit). + // + else + { + // + // Change the SDA GPIO into an input so that the ACK or NAK + // provided by the slave can be read. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_IN); + } + + // + // Advance to the next state. + // + pI2C->ucState = SOFTI2C_STATE_ADDR1; + + // + // This state has been handled. + // + break; + } + + // + // In each of these states, wait until SCL has gone high (it has been + // released by the SoftI2C master, but may be held low by the slave). + // This depends upon STATE_Xn and STATE_X(n+1) being consecutively + // numbered. + // + case SOFTI2C_STATE_ADDR2: + case SOFTI2C_STATE_SEND2: + case SOFTI2C_STATE_RECV2: + { + // + // See if SCL has gone high. + // + if(HWREG(pI2C->ulSCLGPIO) != 0) + { + // + // Advance to the next state now that SCL has gone high. + // + pI2C->ucState++; + } + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. If on the ninth bit of the + // address transfer, the ACK/NAK status is read from the slave. + // + case SOFTI2C_STATE_ADDR3: + { + // + // See if this is the ninth bit of the address phase (in other + // words, the ACK bit). + // + if(pI2C->ucCurrentBit == 8) + { + // + // See if the SDA line is high. + // + if(HWREG(pI2C->ulSDAGPIO) != 0) + { + // + // Since the SDA line is high, the address byte has not + // been ACKed by any slave. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_ADDR_ACK) = 1; + } + + // + // Change the SDA GPIO back into an output. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + + // + // The start phase (start or repeated-start, plus the address + // byte) have completed, so clear the START flag. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_START) = 0; + + // + // See if the RUN flag is set, indicating that a data byte + // should be transferred as well. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RUN) == 1) + { + // + // Reset the current bit to zero for the start of the data + // phase. + // + pI2C->ucCurrentBit = 0; + + // + // See if the data byte is being sent or received. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RECEIVE) == 0) + { + // + // The data byte is being sent, so advance to the data + // send state. + // + pI2C->ucState = SOFTI2C_STATE_SEND0; + } + else + { + // + // The data byte is being received, so clear the data + // buffer and advance to the data receive state. + // + pI2C->ucData = 0; + pI2C->ucState = SOFTI2C_STATE_RECV0; + } + } + + // + // Otherwise, see if the STOP flag is set, indicating that a + // stop condition should be generated. + // + else if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + pI2C->ucState = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + pI2C->ucIntStatus = 1; + + // + // Advance to the idle state. + // + pI2C->ucState = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the address should be transferred. + // + else + { + // + // Increment the bit count. + // + pI2C->ucCurrentBit++; + + // + // Advance to the address tranfer state. + // + pI2C->ucState = SOFTI2C_STATE_ADDR0; + } + + // + // Set SCL low. + // + HWREG(pI2C->ulSCLGPIO) = 0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the next bit of the data byte must be sent. + // + case SOFTI2C_STATE_SEND0: + { + // + // See if this is one of the first eight bits of the data phase. + // + if(pI2C->ucCurrentBit < 8) + { + // + // Write the next bit of the data byte to SDA. + // + HWREG(pI2C->ulSDAGPIO) = ((pI2C->ucData & + (1 << (7 - pI2C->ucCurrentBit))) ? + 255 : 0); + } + + // + // Otherwise, this is the ninth bit of the data phase (in other + // words, the ACK bit). + // + else + { + // + // Change the SDA GPIO into an input so that the ACK or NAK + // provided by the slave can be read. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_IN); + } + + // + // Advance to the next state. + // + pI2C->ucState = SOFTI2C_STATE_SEND1; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. If on the ninth bit of the + // data transfer, the ACK/NAK status is read from the slave. + // + case SOFTI2C_STATE_SEND3: + { + // + // See if this is the ninth bit of the data phase (in other words, + // the ACK bit). + // + if(pI2C->ucCurrentBit == 8) + { + // + // See if the SDA line is high. + // + if(HWREG(pI2C->ulSDAGPIO) != 0) + { + // + // Since the SDA line is high, the data byte has not been + // ACKed by the slave. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_DATA_ACK) = 1; + } + + // + // Change the SDA GPIO back into an output. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + + // + // The data phase has completed, so clear the RUN flag. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RUN) = 0; + + // + // See if the STOP flag is set, indicating that a stop + // condition should be generated. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + pI2C->ucState = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + pI2C->ucIntStatus = 1; + + // + // Advance to the idle state. + // + pI2C->ucState = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the data should be transferred. + // + else + { + // + // Increment the bit count. + // + pI2C->ucCurrentBit++; + + // + // Advance to the data transmit state. + // + pI2C->ucState = SOFTI2C_STATE_SEND0; + } + + // + // Set SCL low. + // + HWREG(pI2C->ulSCLGPIO) = 0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, the next bit of the data byte must be received. + // + case SOFTI2C_STATE_RECV0: + { + // + // See if this is the first bit of the data phase. + // + if(pI2C->ucCurrentBit == 0) + { + // + // Change the SDA GPIO into an input so that the data provided + // by the slave can be read. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_IN); + } + + // + // Otherwise, see if this is the ninth bit of the data phase (in + // other words, the ACK bit). + // + else if(pI2C->ucCurrentBit == 8) + { + // + // Change the SDA GPIO into an output so that the ACK bit can + // be driven to the slave. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_DIR_MODE_OUT); + + // + // See if this byte should be ACKed or NAKed. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_ACK) == 1) + { + // + // Drive SDA low to ACK the data byte. + // + HWREG(pI2C->ulSDAGPIO) = 0; + } + else + { + // + // Allow SDA to get pulled high to NAK the data byte. + // + HWREG(pI2C->ulSDAGPIO) = 255; + } + } + + // + // Advance to the next state. + // + pI2C->ucState = SOFTI2C_STATE_RECV1; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SCL must be driven low. For the first eight bits of + // the data transfer, the data bits are read from the slave. + // + case SOFTI2C_STATE_RECV3: + { + // + // See if this is the ninth bit of the data phase (in other words, + // the ACK bit). + // + if(pI2C->ucCurrentBit == 8) + { + // + // The data phase has completed, so clear the RUN flag. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RUN) = 0; + + // + // See if the STOP flag is set, indicating that a stop + // condition should be generated. + // + if(HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_STOP) == 1) + { + // + // Advance to the stop state. + // + pI2C->ucState = SOFTI2C_STATE_STOP0; + } + + // + // Otherwise, go to the idle state. + // + else + { + // + // Since the requested operations have completed, set the + // SoftI2C ``interrupt''. + // + pI2C->ucIntStatus = 1; + + // + // Advance to the idle state. + // + pI2C->ucState = SOFTI2C_STATE_IDLE; + } + } + + // + // Otherwise, the next bit of the data should be transferred. + // + else + { + // + // Read the next bit of data from the SDA line. + // + pI2C->ucData |= (HWREG(pI2C->ulSDAGPIO) ? + (1 << (7 - pI2C->ucCurrentBit)) : 0); + + // + // Increment the bit count. + // + pI2C->ucCurrentBit++; + + // + // Advance to the data receive state. + // + pI2C->ucState = SOFTI2C_STATE_RECV0; + } + + // + // Set SCL low. + // + HWREG(pI2C->ulSCLGPIO) = 0; + + // + // This state has been handled. + // + break; + } + + // + // In this state, SDA must be driven high to create the stop condition. + // + case SOFTI2C_STATE_STOP4: + { + // + // Set SDA high to create the stop condition. + // + HWREG(pI2C->ulSDAGPIO) = 255; + + // + // The stop condition has been generated, so clear the STOP flag. + // + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_STOP) = 0; + + // + // Since the requested operations have completed, set the SoftI2C + // ``interrupt''. + // + pI2C->ucIntStatus = 1; + + // + // Advance to the idle state. + // + pI2C->ucState = SOFTI2C_STATE_IDLE; + + // + // This state has been handled. + // + break; + } + } + + // + // Call the "interrupt" callback while there are enabled "interrupts" + // asserted. By calling in a loop until the "interrupts" are no longer + // asserted, this mimics the behavior of a real hardware implementation of + // the I2C peripheral. + // + while(((pI2C->ucIntStatus & pI2C->ucIntMask) != 0) && + (pI2C->pfnIntCallback != 0)) + { + // + // Call the callback function. + // + pI2C->pfnIntCallback(); + } +} + +//***************************************************************************** +// +//! Initializes the SoftI2C module. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! +//! This function initializes operation of the SoftI2C module. After +//! successful initialization of the SoftI2C module, the software I2C bus is in +//! the idle state. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CInit(tSoftI2C *pI2C) +{ + // + // Configure the SCL pin. + // + MAP_GPIODirModeSet(pI2C->ulSCLGPIO & 0xfffff000, + (pI2C->ulSCLGPIO & 0x00000fff) >> 2, GPIO_DIR_MODE_OUT); + MAP_GPIOPadConfigSet(pI2C->ulSCLGPIO & 0xfffff000, + (pI2C->ulSCLGPIO & 0x00000fff) >> 2, + GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_OD); + + // + // Set the SCL pin high. + // + HWREG(pI2C->ulSCLGPIO) = 255; + + // + // Configure the SDA pin. + // + MAP_GPIODirModeSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, GPIO_DIR_MODE_OUT); + MAP_GPIOPadConfigSet(pI2C->ulSDAGPIO & 0xfffff000, + (pI2C->ulSDAGPIO & 0x00000fff) >> 2, + GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_OD); + + // + // Set the SDA pin high. + // + HWREG(pI2C->ulSDAGPIO) = 255; + + // + // The ``interrupt'' is not asserted at the start. + // + pI2C->ucIntStatus = 0; + + // + // There are no flags at the start. + // + pI2C->ucFlags = 0; + + // + // Start the SoftI2C state machine in the idle state. + // + pI2C->ucState = SOFTI2C_STATE_IDLE; +} + +//***************************************************************************** +// +//! Sets the callback used by the SoftI2C module. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param pfnCallback is a pointer to the callback function. +//! +//! This function sets the address of the callback function that is called when +//! there is an ``interrupt'' produced by the SoftI2C module. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CCallbackSet(tSoftI2C *pI2C, void (*pfnCallback)(void)) +{ + // + // Save the callback function address. + // + pI2C->pfnIntCallback = pfnCallback; +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftI2C SCL signal. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param ulBase is the base address of the GPIO module. +//! \param ucPin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftI2C SCL signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CSCLGPIOSet(tSoftI2C *pI2C, unsigned long ulBase, unsigned char ucPin) +{ + // + // Save the base address and pin for the SCL signal. + // + pI2C->ulSCLGPIO = ulBase + (ucPin << 2); +} + +//***************************************************************************** +// +//! Sets the GPIO pin to be used as the SoftI2C SDA signal. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param ulBase is the base address of the GPIO module. +//! \param ucPin is the bit-packed representation of the pin to use. +//! +//! This function sets the GPIO pin that is used for the SoftI2C SDA signal. +//! +//! The pin is specified using a bit-packed byte, where bit 0 of the byte +//! represents GPIO port pin 0, bit 1 represents GPIO port pin 1, and so on. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CSDAGPIOSet(tSoftI2C *pI2C, unsigned long ulBase, unsigned char ucPin) +{ + // + // Save the base address and pin for the SDA signal. + // + pI2C->ulSDAGPIO = ulBase + (ucPin << 2); +} + +//***************************************************************************** +// +//! Enables the SoftI2C ``interrupt''. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! +//! Enables the SoftI2C ``interrupt'' source. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CIntEnable(tSoftI2C *pI2C) +{ + // + // Enable the master interrupt. + // + pI2C->ucIntMask = 1; +} + +//***************************************************************************** +// +//! Disables the SoftI2C ``interrupt''. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! +//! Disables the SoftI2C ``interrupt'' source. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CIntDisable(tSoftI2C *pI2C) +{ + // + // Disable the master interrupt. + // + pI2C->ucIntMask = 0; +} + +//***************************************************************************** +// +//! Gets the current SoftI2C ``interrupt'' status. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param bMasked is \b false if the raw ``interrupt'' status is requested and +//! \b true if the masked ``interrupt'' status is requested. +//! +//! This returns the ``interrupt'' status for the SoftI2C module. Either the +//! raw ``interrupt'' status or the status of ``interrupts'' that are allowed +//! to reflect to the processor can be returned. +//! +//! \return The current interrupt status, returned as \b true if active +//! or \b false if not active. +// +//***************************************************************************** +tBoolean +SoftI2CIntStatus(tSoftI2C *pI2C, tBoolean bMasked) +{ + // + // Return either the interrupt status or the raw interrupt status as + // requested. + // + if(bMasked) + { + return((pI2C->ucIntStatus & pI2C->ucIntMask) ? true : false); + } + else + { + return(pI2C->ucIntStatus ? true : false); + } +} + +//***************************************************************************** +// +//! Clears the SoftI2C ``interrupt''. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! +//! The SoftI2C ``interrupt'' source is cleared, so that it no longer asserts. +//! This function must be called in the ``interrupt'' handler to keep it from +//! being called again immediately on exit. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CIntClear(tSoftI2C *pI2C) +{ + // + // Clear the SoftI2C interrupt source. + // + pI2C->ucIntStatus = 0; +} + +//***************************************************************************** +// +//! Sets the address that the SoftI2C module places on the bus. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param ucSlaveAddr 7-bit slave address +//! \param bReceive flag indicating the type of communication with the slave. +//! +//! This function sets the address that the SoftI2C module places on the bus +//! when initiating a transaction. When the \e bReceive parameter is set to +//! \b true, the address indicates that the SoftI2C moudle is initiating a read +//! from the slave; otherwise the address indicates that the SoftI2C module is +//! initiating a write to the slave. +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CSlaveAddrSet(tSoftI2C *pI2C, unsigned char ucSlaveAddr, + tBoolean bReceive) +{ + // + // Check the arguments. + // + ASSERT(!(ucSlaveAddr & 0x80)); + + // + // Set the address of the slave with which the master will communicate. + // + pI2C->ucSlaveAddr = ucSlaveAddr; + + // + // Set a flag to indicate if this is a transmit or receive. + // + if(bReceive) + { + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RECEIVE) = 1; + } + else + { + HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_RECEIVE) = 0; + } +} + +//***************************************************************************** +// +//! Indicates whether or not the SoftI2C module is busy. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! +//! This function returns an indication of whether or not the SoftI2C module is +//! busy transmitting or receiving data. +//! +//! \return Returns \b true if the SoftI2C module is busy; otherwise, returns +//! \b false. +// +//***************************************************************************** +tBoolean +SoftI2CBusy(tSoftI2C *pI2C) +{ + // + // Return the busy status. + // + if(pI2C->ucState != SOFTI2C_STATE_IDLE) + { + return(true); + } + else + { + return(false); + } +} + +//***************************************************************************** +// +//! Controls the state of the SoftI2C module. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param ulCmd command to be issued to the SoftI2C module. +//! +//! This function is used to control the state of the SoftI2C module send and +//! receive operations. The \e ucCmd parameter can be one of the following +//! values: +//! +//! - \b SOFTI2C_CMD_SINGLE_SEND +//! - \b SOFTI2C_CMD_SINGLE_RECEIVE +//! - \b SOFTI2C_CMD_BURST_SEND_START +//! - \b SOFTI2C_CMD_BURST_SEND_CONT +//! - \b SOFTI2C_CMD_BURST_SEND_FINISH +//! - \b SOFTI2C_CMD_BURST_SEND_ERROR_STOP +//! - \b SOFTI2C_CMD_BURST_RECEIVE_START +//! - \b SOFTI2C_CMD_BURST_RECEIVE_CONT +//! - \b SOFTI2C_CMD_BURST_RECEIVE_FINISH +//! - \b SOFTI2C_CMD_BURST_RECEIVE_ERROR_STOP +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CControl(tSoftI2C *pI2C, unsigned long ulCmd) +{ + // + // Check the arguments. + // + ASSERT((ulCmd == SOFTI2C_CMD_SINGLE_SEND) || + (ulCmd == SOFTI2C_CMD_SINGLE_RECEIVE) || + (ulCmd == SOFTI2C_CMD_BURST_SEND_START) || + (ulCmd == SOFTI2C_CMD_BURST_SEND_CONT) || + (ulCmd == SOFTI2C_CMD_BURST_SEND_FINISH) || + (ulCmd == SOFTI2C_CMD_BURST_SEND_ERROR_STOP) || + (ulCmd == SOFTI2C_CMD_BURST_RECEIVE_START) || + (ulCmd == SOFTI2C_CMD_BURST_RECEIVE_CONT) || + (ulCmd == SOFTI2C_CMD_BURST_RECEIVE_FINISH) || + (ulCmd == SOFTI2C_CMD_BURST_RECEIVE_ERROR_STOP)); + + // + // Send the command. + // + pI2C->ucFlags = (pI2C->ucFlags & 0xf0) | ulCmd; +} + +//***************************************************************************** +// +//! Gets the error status of the SoftI2C module. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! +//! This function is used to obtain the error status of the SoftI2C module send +//! and receive operations. +//! +//! \return Returns the error status, as one of \b SOFTI2C_ERR_NONE, +//! \b SOFTI2C_ERR_ADDR_ACK, or \b SOFTI2C_ERR_DATA_ACK. +// +//***************************************************************************** +unsigned long +SoftI2CErr(tSoftI2C *pI2C) +{ + // + // If the SoftI2C is busy, there is no error to report. + // + if(pI2C->ucState != SOFTI2C_STATE_IDLE) + { + return(SOFTI2C_ERR_NONE); + } + + // + // Return any errors that may have occurred. + // + return((HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_ADDR_ACK) ? + SOFTI2C_ERR_ADDR_ACK : 0) | + (HWREGBITB(&(pI2C->ucFlags), SOFTI2C_FLAG_DATA_ACK) ? + SOFTI2C_ERR_DATA_ACK : 0)); +} + +//***************************************************************************** +// +//! Transmits a byte from the SoftI2C module. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! \param ucData data to be transmitted from the SoftI2C module. +//! +//! This function places the supplied data into SoftI2C module in preparation +//! for being transmitted via an appropriate call to SoftI2CControl(). +//! +//! \return None. +// +//***************************************************************************** +void +SoftI2CDataPut(tSoftI2C *pI2C, unsigned char ucData) +{ + // + // Write the byte. + // + pI2C->ucData = ucData; +} + +//***************************************************************************** +// +//! Receives a byte that has been sent to the SoftI2C module. +//! +//! \param pI2C specifies the SoftI2C data structure. +//! +//! This function reads a byte of data from the SoftI2C module that was +//! received as a result of an appropriate call to SoftI2CControl(). +//! +//! \return Returns the byte received by the SoftI2C module, cast as an +//! unsigned long. +// +//***************************************************************************** +unsigned long +SoftI2CDataGet(tSoftI2C *pI2C) +{ + // + // Read a byte. + // + return(pI2C->ucData); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** -- cgit v1.3.1