crashtest-r0ket/firmware/core/i2c/i2c.c

354 lines
9.7 KiB
C
Raw Permalink Normal View History

2011-05-13 13:14:21 +00:00
/*****************************************************************************
* i2c.c: I2C C file for NXP LPC11xx/13xx Family Microprocessors
*
* Copyright(C) 2008, NXP Semiconductor
* Parts of this code are (C) 2010, MyVoice CAD/CAM Services
* All rights reserved.
*
* History
* 2009.12.07 ver 1.00 Preliminary version, first Release
* 2010.07.19 ver 1.10 Rob Jansen - MyVoice CAD/CAM Services:
* Major cleaning and a rewrite of some functions
* - adding ACK/NACK handling to the state machine
* - adding a return result to the I2CEngine()
*
*****************************************************************************/
#include "i2c.h"
volatile uint32_t I2CMasterState = I2CSTATE_IDLE;
volatile uint32_t I2CSlaveState = I2CSTATE_IDLE;
volatile uint8_t I2CMasterBuffer[I2C_BUFSIZE];
volatile uint8_t I2CSlaveBuffer[I2C_BUFSIZE];
volatile uint32_t I2CReadLength;
volatile uint32_t I2CWriteLength;
volatile uint32_t RdIndex = 0;
volatile uint32_t WrIndex = 0;
/*****************************************************************************
** Function name: I2C_IRQHandler
**
** Descriptions: I2C interrupt handler, deal with master mode only.
**
** parameters: None
** Returned value: None
**
*****************************************************************************/
void I2C_IRQHandler(void)
{
uint8_t StatValue;
/* this handler deals with master read and master write only */
StatValue = I2C_I2CSTAT;
switch ( StatValue )
{
case 0x08:
/*
* A START condition has been transmitted.
* We now send the slave address and initialize
* the write buffer
* (we always start with a write after START+SLA)
*/
WrIndex = 0;
I2C_I2CDAT = I2CMasterBuffer[WrIndex++];
I2C_I2CCONCLR = (I2CONCLR_SIC | I2CONCLR_STAC);
I2CMasterState = I2CSTATE_PENDING;
break;
case 0x10:
/*
* A repeated START condition has been transmitted.
* Now a second, read, transaction follows so we
* initialize the read buffer.
*/
RdIndex = 0;
/* Send SLA with R bit set, */
I2C_I2CDAT = I2CMasterBuffer[WrIndex++];
I2C_I2CCONCLR = (I2CONCLR_SIC | I2CONCLR_STAC);
break;
case 0x18:
/*
* SLA+W has been transmitted; ACK has been received.
* We now start writing bytes.
*/
I2C_I2CDAT = I2CMasterBuffer[WrIndex++];
I2C_I2CCONCLR = I2CONCLR_SIC;
break;
case 0x20:
/*
* SLA+W has been transmitted; NOT ACK has been received.
* Send a stop condition to terminate the transaction
* and signal I2CEngine the transaction is aborted.
*/
I2C_I2CCONSET = I2CONSET_STO;
I2C_I2CCONCLR = I2CONCLR_SIC;
I2CMasterState = I2CSTATE_SLA_NACK;
break;
case 0x28:
/*
* Data in I2DAT has been transmitted; ACK has been received.
* Continue sending more bytes as long as there are bytes to send
* and after this check if a read transaction should follow.
*/
if ( WrIndex < I2CWriteLength )
{
/* Keep writing as long as bytes avail */
I2C_I2CDAT = I2CMasterBuffer[WrIndex++];
}
else
{
if ( I2CReadLength != 0 )
{
/* Send a Repeated START to initialize a read transaction */
/* (handled in state 0x10) */
I2C_I2CCONSET = I2CONSET_STA; /* Set Repeated-start flag */
}
else
{
I2CMasterState = I2CSTATE_ACK;
I2C_I2CCONSET = I2CONSET_STO; /* Set Stop flag */
}
}
I2C_I2CCONCLR = I2CONCLR_SIC;
break;
case 0x30:
/*
* Data byte in I2DAT has been transmitted; NOT ACK has been received
* Send a STOP condition to terminate the transaction and inform the
* I2CEngine that the transaction failed.
*/
I2C_I2CCONSET = I2CONSET_STO;
I2C_I2CCONCLR = I2CONCLR_SIC;
I2CMasterState = I2CSTATE_NACK;
break;
case 0x38:
/*
* Arbitration loss in SLA+R/W or Data bytes.
* This is a fatal condition, the transaction did not complete due
* to external reasons (e.g. hardware system failure).
* Inform the I2CEngine of this and cancel the transaction
* (this is automatically done by the I2C hardware)
*/
I2CMasterState = I2CSTATE_ARB_LOSS;
I2C_I2CCONCLR = I2CONCLR_SIC;
break;
case 0x40:
/*
* SLA+R has been transmitted; ACK has been received.
* Initialize a read.
* Since a NOT ACK is sent after reading the last byte,
* we need to prepare a NOT ACK in case we only read 1 byte.
*/
if ( I2CReadLength == 1 )
{
/* last (and only) byte: send a NACK after data is received */
I2C_I2CCONCLR = I2CONCLR_AAC;
}
else
{
/* more bytes to follow: send an ACK after data is received */
I2C_I2CCONSET = I2CONSET_AA;
}
I2C_I2CCONCLR = I2CONCLR_SIC;
break;
case 0x48:
/*
* SLA+R has been transmitted; NOT ACK has been received.
* Send a stop condition to terminate the transaction
* and signal I2CEngine the transaction is aborted.
*/
I2C_I2CCONSET = I2CONSET_STO;
I2C_I2CCONCLR = I2CONCLR_SIC;
I2CMasterState = I2CSTATE_SLA_NACK;
break;
case 0x50:
/*
* Data byte has been received; ACK has been returned.
* Read the byte and check for more bytes to read.
* Send a NOT ACK after the last byte is received
*/
I2CSlaveBuffer[RdIndex++] = I2C_I2CDAT;
if ( RdIndex < (I2CReadLength-1) )
{
/* lmore bytes to follow: send an ACK after data is received */
I2C_I2CCONSET = I2CONSET_AA;
}
else
{
/* last byte: send a NACK after data is received */
I2C_I2CCONCLR = I2CONCLR_AAC;
}
I2C_I2CCONCLR = I2CONCLR_SIC;
break;
case 0x58:
/*
* Data byte has been received; NOT ACK has been returned.
* This is the last byte to read.
* Generate a STOP condition and flag the I2CEngine that the
* transaction is finished.
*/
I2CSlaveBuffer[RdIndex++] = I2C_I2CDAT;
I2CMasterState = I2CSTATE_ACK;
I2C_I2CCONSET = I2CONSET_STO; /* Set Stop flag */
I2C_I2CCONCLR = I2CONCLR_SIC; /* Clear SI flag */
break;
default:
I2C_I2CCONCLR = I2CONCLR_SIC;
break;
}
return;
}
/*****************************************************************************
** Function name: I2CStart
**
** Descriptions: Create I2C start condition, a timeout
** value is set if the I2C never gets started,
** and timed out. It's a fatal error.
**
** parameters: None
** Returned value: true or false, return false if timed out
**
*****************************************************************************/
static uint32_t I2CStart( void )
{
uint32_t timeout = 0;
/*--- Issue a start condition ---*/
I2C_I2CCONSET = I2CONSET_STA; /* Set Start flag */
while((I2CMasterState != I2CSTATE_PENDING) && (timeout < MAX_TIMEOUT))
{
timeout++;
}
return (timeout < MAX_TIMEOUT);
}
/*****************************************************************************
** Function name: I2CStop
**
** Descriptions: Set the I2C stop condition
**
** parameters: None
** Returned value: true or never return
**
*****************************************************************************/
static uint32_t I2CStop( void )
{
uint32_t timeout = 0;
I2C_I2CCONSET = I2CONSET_STO; /* Set Stop flag */
I2C_I2CCONCLR = I2CONCLR_SIC; /* Clear SI flag */
/*--- Wait for STOP detected ---*/
while((I2C_I2CCONSET & I2CONSET_STO) && (timeout < MAX_TIMEOUT))
{
timeout++;
}
return (timeout >= MAX_TIMEOUT);
}
/*****************************************************************************
** Function name: I2CInit
**
** Descriptions: Initialize I2C controller
**
** parameters: I2c mode is either MASTER or SLAVE
** Returned value: true or false, return false if the I2C
** interrupt handler was not installed correctly
**
*****************************************************************************/
uint32_t i2cInit( uint32_t I2cMode )
{
SCB_PRESETCTRL |= (0x1<<1);
// Enable I2C clock
SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_I2C);
// Configure pin 0.4 for SCL
IOCON_PIO0_4 &= ~(IOCON_PIO0_4_FUNC_MASK | IOCON_PIO0_4_I2CMODE_MASK);
IOCON_PIO0_4 |= (IOCON_PIO0_4_FUNC_I2CSCL);
// Configure pin 0.5 for SDA
IOCON_PIO0_5 &= ~(IOCON_PIO0_5_FUNC_MASK | IOCON_PIO0_5_I2CMODE_MASK);
IOCON_PIO0_5 |= IOCON_PIO0_5_FUNC_I2CSDA;
// Clear flags
I2C_I2CCONCLR = I2C_I2CCONCLR_AAC |
I2C_I2CCONCLR_SIC |
I2C_I2CCONCLR_STAC |
I2C_I2CCONCLR_I2ENC;
// See p.128 for appropriate values for SCLL and SCLH
#if I2C_FAST_MODE_PLUS
IOCON_PIO0_4 |= (IOCON_PIO0_4_I2CMODE_FASTPLUSI2C);
IOCON_PIO0_5 |= (IOCON_PIO0_5_I2CMODE_FASTPLUSI2C);
I2C_I2CSCLL = I2C_SCLL_HS_SCLL;
I2C_I2CSCLH = I2C_SCLH_HS_SCLH;
#else
I2C_I2CSCLL = I2SCLL_SCLL;
I2C_I2CSCLH = I2SCLH_SCLH;
#endif
if ( I2cMode == I2CSLAVE )
{
I2C_I2CADR0 = SLAVE_ADDR;
}
/* Enable the I2C Interrupt */
NVIC_EnableIRQ(I2C_IRQn);
I2C_I2CCONSET = I2C_I2CCONSET_I2EN;
return( TRUE );
}
/*****************************************************************************
** Function name: I2CEngine
**
** Descriptions: The routine to complete a I2C transaction
** from start to stop. All the intermitten
** steps are handled in the interrupt handler.
** Before this routine is called, the read
** length, write length and I2C master buffer
** need to be filled.
**
** parameters: None
** Returned value: Any of the I2CSTATE_... values. See i2c.h
**
*****************************************************************************/
uint32_t i2cEngine( void )
{
I2CMasterState = I2CSTATE_IDLE;
RdIndex = 0;
WrIndex = 0;
if ( I2CStart() != TRUE )
{
I2CStop();
return ( FALSE );
}
/* wait until the state is a terminal state */
while (I2CMasterState < 0x100);
return ( I2CMasterState );
}
/******************************************************************************
** End Of File
******************************************************************************/