?lang_form? ?lang_select? ?lang_submit? ?lang_endform?
{HEADER END}
{FILE START}

library

?curdirlinks? - Rev 6

?prevdifflink? - Blame - ?getfile?

/*! \file i2c.c \brief I2C interface using AVR Two-Wire Interface (TWI) hardware. */
//*****************************************************************************
//
// File Name    : 'i2c.c'
// Title                : I2C interface using AVR Two-Wire Interface (TWI) hardware
// Author               : Pascal Stang - Copyright (C) 2002-2003
// Created              : 2002.06.25
// Revised              : 2003.03.02
// Version              : 0.9
// Target MCU   : Atmel AVR series
// Editor Tabs  : 4
//
// This code is distributed under the GNU Public License
//              which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************

#include <avr/io.h>
#include <avr/interrupt.h>

#include "i2c.h"

#include "rprintf.h"    // include printf function library
#include "uart2.h"

// Standard I2C bit rates are:
// 100KHz for slow speed
// 400KHz for high speed

//#define I2C_DEBUG

// I2C state and address variables
static volatile eI2cStateType I2cState;
static u08 I2cDeviceAddrRW;
// send/transmit buffer (outgoing data)
static u08 I2cSendData[I2C_SEND_DATA_BUFFER_SIZE];
static u08 I2cSendDataIndex;
static u08 I2cSendDataLength;
// receive buffer (incoming data)
static u08 I2cReceiveData[I2C_RECEIVE_DATA_BUFFER_SIZE];
static u08 I2cReceiveDataIndex;
static u08 I2cReceiveDataLength;

// function pointer to i2c receive routine
//! I2cSlaveReceive is called when this processor
// is addressed as a slave for writing
static void (*i2cSlaveReceive)(u08 receiveDataLength, u08* recieveData);
//! I2cSlaveTransmit is called when this processor
// is addressed as a slave for reading
static u08 (*i2cSlaveTransmit)(u08 transmitDataLengthMax, u08* transmitData);

// functions
void i2cInit(void)
{
        // set pull-up resistors on I2C bus pins
        // TODO: should #ifdef these
        sbi(PORTC, 0);  // i2c SCL on ATmega163,323,16,32,etc
        sbi(PORTC, 1);  // i2c SDA on ATmega163,323,16,32,etc
        sbi(PORTD, 0);  // i2c SCL on ATmega128,64
        sbi(PORTD, 1);  // i2c SDA on ATmega128,64

        // clear SlaveReceive and SlaveTransmit handler to null
        i2cSlaveReceive = 0;
        i2cSlaveTransmit = 0;
        // set i2c bit rate to 100KHz
        i2cSetBitrate(100);
        // enable TWI (two-wire interface)
        sbi(TWCR, TWEN);
        // set state
        I2cState = I2C_IDLE;
        // enable TWI interrupt and slave address ACK
        sbi(TWCR, TWIE);
        sbi(TWCR, TWEA);
        //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
        // enable interrupts
        sei();
}

void i2cSetBitrate(u16 bitrateKHz)
{
        u08 bitrate_div;
        // set i2c bitrate
        // SCL freq = F_CPU/(16+2*TWBR))
        #ifdef TWPS0
                // for processors with additional bitrate division (mega128)
                // SCL freq = F_CPU/(16+2*TWBR*4^TWPS)
                // set TWPS to zero
                cbi(TWSR, TWPS0);
                cbi(TWSR, TWPS1);
        #endif
        // calculate bitrate division   
        bitrate_div = ((F_CPU/1000l)/bitrateKHz);
        if(bitrate_div >= 16)
                bitrate_div = (bitrate_div-16)/2;
        outb(TWBR, bitrate_div);
}

void i2cSetLocalDeviceAddr(u08 deviceAddr, u08 genCallEn)
{
        // set local device address (used in slave mode only)
        outb(TWAR, ((deviceAddr&0xFE) | (genCallEn?1:0)) );
}

void i2cSetSlaveReceiveHandler(void (*i2cSlaveRx_func)(u08 receiveDataLength, u08* recieveData))
{
        i2cSlaveReceive = i2cSlaveRx_func;
}

void i2cSetSlaveTransmitHandler(u08 (*i2cSlaveTx_func)(u08 transmitDataLengthMax, u08* transmitData))
{
        i2cSlaveTransmit = i2cSlaveTx_func;
}

inline void i2cSendStart(void)
{
        // send start condition
        outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTA));
}

inline void i2cSendStop(void)
{
        // transmit stop condition
        // leave with TWEA on for slave receiving
        outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA)|BV(TWSTO));
}

inline void i2cWaitForComplete(void)
{
        // wait for i2c interface to complete operation
        while( !(inb(TWCR) & BV(TWINT)) );
}

inline void i2cSendByte(u08 data)
{
        // save data to the TWDR
        outb(TWDR, data);
        // begin send
        outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
}

inline void i2cReceiveByte(u08 ackFlag)
{
        // begin receive over i2c
        if( ackFlag )
        {
                // ackFlag = TRUE: ACK the recevied data
                outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
        }
        else
        {
                // ackFlag = FALSE: NACK the recevied data
                outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
        }
}

inline u08 i2cGetReceivedByte(void)
{
        // retieve received data byte from i2c TWDR
        return( inb(TWDR) );
}

inline u08 i2cGetStatus(void)
{
        // retieve current i2c status from i2c TWSR
        return( inb(TWSR) );
}

void i2cMasterSend(u08 deviceAddr, u08 length, u08* data)
{
        u08 i;
        // wait for interface to be ready
        while(I2cState);
        // set state
        I2cState = I2C_MASTER_TX;
        // save data
        I2cDeviceAddrRW = (deviceAddr & 0xFE);  // RW cleared: write operation
        for(i=0; i<length; i++)
                I2cSendData[i] = *data++;
        I2cSendDataIndex = 0;
        I2cSendDataLength = length;
        // send start condition
        i2cSendStart();
}

void i2cMasterReceive(u08 deviceAddr, u08 length, u08* data)
{
        u08 i;
        // wait for interface to be ready
        while(I2cState);
        // set state
        I2cState = I2C_MASTER_RX;
        // save data
        I2cDeviceAddrRW = (deviceAddr|0x01);    // RW set: read operation
        I2cReceiveDataIndex = 0;
        I2cReceiveDataLength = length;
        // send start condition
        i2cSendStart();
        // wait for data
        while(I2cState);
        // return data
        for(i=0; i<length; i++)
                *data++ = I2cReceiveData[i];
}

u08 i2cMasterSendNI(u08 deviceAddr, u08 length, u08* data)
{
        u08 retval = I2C_OK;

        // disable TWI interrupt
        cbi(TWCR, TWIE);

        // send start condition
        i2cSendStart();
        i2cWaitForComplete();

        // send device address with write
        i2cSendByte( deviceAddr & 0xFE );
        i2cWaitForComplete();

        // check if device is present and live
        if( inb(TWSR) == TW_MT_SLA_ACK)
        {
                // send data
                while(length)
                {
                        i2cSendByte( *data++ );
                        i2cWaitForComplete();
                        length--;
                }
        }
        else
        {
                // device did not ACK it's address,
                // data will not be transferred
                // return error
                retval = I2C_ERROR_NODEV;
        }

        // transmit stop condition
        // leave with TWEA on for slave receiving
        i2cSendStop();
        while( !(inb(TWCR) & BV(TWSTO)) );

        // enable TWI interrupt
        sbi(TWCR, TWIE);

        return retval;
}

u08 i2cMasterReceiveNI(u08 deviceAddr, u08 length, u08 *data)
{
        u08 retval = I2C_OK;

        // disable TWI interrupt
        cbi(TWCR, TWIE);

        // send start condition
        i2cSendStart();
        i2cWaitForComplete();

        // send device address with read
        i2cSendByte( deviceAddr | 0x01 );
        i2cWaitForComplete();

        // check if device is present and live
        if( inb(TWSR) == TW_MR_SLA_ACK)
        {
                // accept receive data and ack it
                while(length > 1)
                {
                        i2cReceiveByte(TRUE);
                        i2cWaitForComplete();
                        *data++ = i2cGetReceivedByte();
                        // decrement length
                        length--;
                }

                // accept receive data and nack it (last-byte signal)
                i2cReceiveByte(FALSE);
                i2cWaitForComplete();
                *data++ = i2cGetReceivedByte();
        }
        else
        {
                // device did not ACK it's address,
                // data will not be transferred
                // return error
                retval = I2C_ERROR_NODEV;
        }

        // transmit stop condition
        // leave with TWEA on for slave receiving
        i2cSendStop();

        // enable TWI interrupt
        sbi(TWCR, TWIE);

        return retval;
}
/*
void i2cMasterTransferNI(u08 deviceAddr, u08 sendlength, u08* senddata, u08 receivelength, u08* receivedata)
{
        // disable TWI interrupt
        cbi(TWCR, TWIE);

        // send start condition
        i2cSendStart();
        i2cWaitForComplete();

        // if there's data to be sent, do it
        if(sendlength)
        {
                // send device address with write
                i2cSendByte( deviceAddr & 0xFE );
                i2cWaitForComplete();
                
                // send data
                while(sendlength)
                {
                        i2cSendByte( *senddata++ );
                        i2cWaitForComplete();
                        sendlength--;
                }
        }

        // if there's data to be received, do it
        if(receivelength)
        {
                // send repeated start condition
                i2cSendStart();
                i2cWaitForComplete();

                // send device address with read
                i2cSendByte( deviceAddr | 0x01 );
                i2cWaitForComplete();

                // accept receive data and ack it
                while(receivelength > 1)
                {
                        i2cReceiveByte(TRUE);
                        i2cWaitForComplete();
                        *receivedata++ = i2cGetReceivedByte();
                        // decrement length
                        receivelength--;
                }

                // accept receive data and nack it (last-byte signal)
                i2cReceiveByte(TRUE);
                i2cWaitForComplete();
                *receivedata++ = i2cGetReceivedByte();
        }
        
        // transmit stop condition
        // leave with TWEA on for slave receiving
        i2cSendStop();
        while( !(inb(TWCR) & BV(TWSTO)) );

        // enable TWI interrupt
        sbi(TWCR, TWIE);
}
*/

//! I2C (TWI) interrupt service routine
SIGNAL(SIG_2WIRE_SERIAL)
{
        // read status bits
        u08 status = inb(TWSR) & TWSR_STATUS_MASK;

        switch(status)
        {
        // Master General
        case TW_START:                                          // 0x08: Sent start condition
        case TW_REP_START:                                      // 0x10: Sent repeated start condition
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: M->START\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // send device address
                i2cSendByte(I2cDeviceAddrRW);
                break;
        
        // Master Transmitter & Receiver status codes
        case TW_MT_SLA_ACK:                                     // 0x18: Slave address acknowledged
        case TW_MT_DATA_ACK:                            // 0x28: Data acknowledged
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: MT->SLA_ACK or DATA_ACK\r\n");
                rprintfInit(uart1SendByte);
                #endif
                if(I2cSendDataIndex < I2cSendDataLength)
                {
                        // send data
                        i2cSendByte( I2cSendData[I2cSendDataIndex++] );
                }
                else
                {
                        // transmit stop condition, enable SLA ACK
                        i2cSendStop();
                        // set state
                        I2cState = I2C_IDLE;
                }
                break;
        case TW_MR_DATA_NACK:                           // 0x58: Data received, NACK reply issued
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: MR->DATA_NACK\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // store final received data byte
                I2cReceiveData[I2cReceiveDataIndex++] = inb(TWDR);
                // continue to transmit STOP condition
        case TW_MR_SLA_NACK:                            // 0x48: Slave address not acknowledged
        case TW_MT_SLA_NACK:                            // 0x20: Slave address not acknowledged
        case TW_MT_DATA_NACK:                           // 0x30: Data not acknowledged
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: MTR->SLA_NACK or MT->DATA_NACK\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // transmit stop condition, enable SLA ACK
                i2cSendStop();
                // set state
                I2cState = I2C_IDLE;
                break;
        case TW_MT_ARB_LOST:                            // 0x38: Bus arbitration lost
        //case TW_MR_ARB_LOST:                          // 0x38: Bus arbitration lost
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: MT->ARB_LOST\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // release bus
                outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
                // set state
                I2cState = I2C_IDLE;
                // release bus and transmit start when bus is free
                //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTA));
                break;
        case TW_MR_DATA_ACK:                            // 0x50: Data acknowledged
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: MR->DATA_ACK\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // store received data byte
                I2cReceiveData[I2cReceiveDataIndex++] = inb(TWDR);
                // fall-through to see if more bytes will be received
        case TW_MR_SLA_ACK:                                     // 0x40: Slave address acknowledged
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: MR->SLA_ACK\r\n");
                rprintfInit(uart1SendByte);
                #endif
                if(I2cReceiveDataIndex < (I2cReceiveDataLength-1))
                        // data byte will be received, reply with ACK (more bytes in transfer)
                        i2cReceiveByte(TRUE);
                else
                        // data byte will be received, reply with NACK (final byte in transfer)
                        i2cReceiveByte(FALSE);
                break;

        // Slave Receiver status codes
        case TW_SR_SLA_ACK:                                     // 0x60: own SLA+W has been received, ACK has been returned
        case TW_SR_ARB_LOST_SLA_ACK:            // 0x68: own SLA+W has been received, ACK has been returned
        case TW_SR_GCALL_ACK:                           // 0x70:     GCA+W has been received, ACK has been returned
        case TW_SR_ARB_LOST_GCALL_ACK:          // 0x78:     GCA+W has been received, ACK has been returned
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: SR->SLA_ACK\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // we are being addressed as slave for writing (data will be received from master)
                // set state
                I2cState = I2C_SLAVE_RX;
                // prepare buffer
                I2cReceiveDataIndex = 0;
                // receive data byte and return ACK
                outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
                break;
        case TW_SR_DATA_ACK:                            // 0x80: data byte has been received, ACK has been returned
        case TW_SR_GCALL_DATA_ACK:                      // 0x90: data byte has been received, ACK has been returned
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: SR->DATA_ACK\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // get previously received data byte
                I2cReceiveData[I2cReceiveDataIndex++] = inb(TWDR);
                // check receive buffer status
                if(I2cReceiveDataIndex < I2C_RECEIVE_DATA_BUFFER_SIZE)
                {
                        // receive data byte and return ACK
                        i2cReceiveByte(TRUE);
                        //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
                }
                else
                {
                        // receive data byte and return NACK
                        i2cReceiveByte(FALSE);
                        //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
                }
                break;
        case TW_SR_DATA_NACK:                           // 0x88: data byte has been received, NACK has been returned
        case TW_SR_GCALL_DATA_NACK:                     // 0x98: data byte has been received, NACK has been returned
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: SR->DATA_NACK\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // receive data byte and return NACK
                i2cReceiveByte(FALSE);
                //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
                break;
        case TW_SR_STOP:                                        // 0xA0: STOP or REPEATED START has been received while addressed as slave
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: SR->SR_STOP\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // switch to SR mode with SLA ACK
                outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
                // i2c receive is complete, call i2cSlaveReceive
                if(i2cSlaveReceive) i2cSlaveReceive(I2cReceiveDataIndex, I2cReceiveData);
                // set state
                I2cState = I2C_IDLE;
                break;

        // Slave Transmitter
        case TW_ST_SLA_ACK:                                     // 0xA8: own SLA+R has been received, ACK has been returned
        case TW_ST_ARB_LOST_SLA_ACK:            // 0xB0:     GCA+R has been received, ACK has been returned
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: ST->SLA_ACK\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // we are being addressed as slave for reading (data must be transmitted back to master)
                // set state
                I2cState = I2C_SLAVE_TX;
                // request data from application
                if(i2cSlaveTransmit) I2cSendDataLength = i2cSlaveTransmit(I2C_SEND_DATA_BUFFER_SIZE, I2cSendData);
                // reset data index
                I2cSendDataIndex = 0;
                // fall-through to transmit first data byte
        case TW_ST_DATA_ACK:                            // 0xB8: data byte has been transmitted, ACK has been received
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: ST->DATA_ACK\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // transmit data byte
                outb(TWDR, I2cSendData[I2cSendDataIndex++]);
                if(I2cSendDataIndex < I2cSendDataLength)
                        // expect ACK to data byte
                        outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
                else
                        // expect NACK to data byte
                        outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
                break;
        case TW_ST_DATA_NACK:                           // 0xC0: data byte has been transmitted, NACK has been received
        case TW_ST_LAST_DATA:                           // 0xC8:
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: ST->DATA_NACK or LAST_DATA\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // all done
                // switch to open slave
                outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
                // set state
                I2cState = I2C_IDLE;
                break;

        // Misc
        case TW_NO_INFO:                                        // 0xF8: No relevant state information
                // do nothing
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: NO_INFO\r\n");
                rprintfInit(uart1SendByte);
                #endif
                break;
        case TW_BUS_ERROR:                                      // 0x00: Bus error due to illegal start or stop condition
                #ifdef I2C_DEBUG
                rprintfInit(uart1AddToTxBuffer);
                rprintf("I2C: BUS_ERROR\r\n");
                rprintfInit(uart1SendByte);
                #endif
                // reset internal hardware and release bus
                outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTO)|BV(TWEA));
                // set state
                I2cState = I2C_IDLE;
                break;
        }
}

eI2cStateType i2cGetState(void)
{
        return I2cState;
}
{FILE END}
{FOOTER START}

Powered by WebSVN v2.8.3