Improvements to Olieman's MODI2C library. Supports calls from IRQ.

Dependencies:   FPointer

Dependents:   FreeIMU FreeIMU_external_magnetometer FreeIMU

Fork of MODI2C by Erik -

MODI2C_IRQ.cpp

Committer:
Sissors
Date:
2012-06-30
Revision:
0:ff579e7e8efa
Child:
1:eed116eb680a

File content as of revision 0:ff579e7e8efa:

#include "MODI2C.h"

//**********************************************************
//*********************IRQ FUNCTIONS************************
//**********************************************************


void MODI2C::runUserIRQ(I2CData Data) {
     
    if (IRQOp==IRQ_I2C_BOTH)        //Always call if both
        callback.call();
    if ((IRQOp==IRQ_I2C_READ)&&(Data.address&0x01)) //Call if read and byte was read
        callback.call();
    if ((IRQOp==IRQ_I2C_WRITE)&&(!(Data.address&0x01))) //Call if write and byte was written
        callback.call();
}

void MODI2C::IRQ1Handler( void ) {
    IRQHandler(&Buffer1, LPC_I2C1);
}

void MODI2C::IRQ2Handler( void ) {
    IRQHandler(&Buffer2, LPC_I2C2);
}

void MODI2C::IRQHandler( I2CBuffer *Buffer, LPC_I2C_TypeDef *I2CMODULE) {
    I2CData *Data = &Buffer->Data[0];

    //Depending on the status register it determines next action, see datasheet
    //This is also pretty much copy pasting the datasheet
    //General options
    switch (I2CMODULE->I2STAT) {
        case(0x08):
        case(0x10):
            //Send Address
            I2CMODULE->I2DAT = Data->address;
            I2CMODULE->I2CONCLR = 1<<I2C_FLAG | 1<<I2C_START;
            break;

            //All master TX options

            //Address + W has been sent, ACK received
            //Data has been sent, ACK received
        case(0x18):
        case(0x28):
            if (Buffer->count==Data->length) {
                *Data->status=I2CMODULE->I2STAT;
                if (!Data->repeated)
                    _stop(I2CMODULE);
                else {
                    I2CMODULE->I2CONSET = 1<<I2C_START;
                    I2CMODULE->I2CONCLR = 1<<I2C_FLAG;
                }
                bufferHandler(I2CMODULE);
            } else {
                I2CMODULE->I2DAT = Data->data[Buffer->count];
                I2CMODULE->I2CONSET = 1<<I2C_ASSERT_ACK;        //I dont see why I have to enable that bit, but datasheet says so
                I2CMODULE->I2CONCLR = 1<<I2C_FLAG;
                Buffer->count++;
            }
            break;

            //Address + W has been sent, NACK received
            //Data has been sent, NACK received
        case(0x20):
        case(0x30):
            *Data->status=I2CMODULE->I2STAT;
            _stop(I2CMODULE);
            bufferHandler(I2CMODULE);
            break;

            //Arbitration lost (situation looks pretty hopeless to me if you arrive here)
        case(0x38):
            _start(I2CMODULE);
            break;


            //All master RX options

            //Address + R has been sent, ACK received
        case(0x40):
            //If next byte is last one, NACK, otherwise ACK
            if (Data->length <= Buffer->count + 1)
                I2CMODULE->I2CONCLR = 1<<I2C_ASSERT_ACK;
            else
                I2CMODULE->I2CONSET = 1<<I2C_ASSERT_ACK;
            I2CMODULE->I2CONCLR = 1<<I2C_FLAG;
            break;

            //Address + R has been sent, NACK received
        case(0x48):
            *Data->status=I2CMODULE->I2STAT;
            _stop(I2CMODULE);
            bufferHandler(I2CMODULE);
            break;

            //Data was received, ACK returned
        case(0x50):
            //Read data
            Data->data[Buffer->count]=I2CMODULE->I2DAT;
            Buffer->count++;

            //If next byte is last one, NACK, otherwise ACK
            if (Data->length == Buffer->count + 1)
                I2CMODULE->I2CONCLR = 1<<I2C_ASSERT_ACK;
            else
                I2CMODULE->I2CONSET = 1<<I2C_ASSERT_ACK;

            I2CMODULE->I2CONCLR = 1<<I2C_FLAG;
            break;

            //Data was received, NACK returned (last byte)
        case(0x58):
            //Read data
            *Data->status=I2CMODULE->I2STAT;
            Data->data[Buffer->count]=I2CMODULE->I2DAT;
            if (!Data->repeated)
                _stop(I2CMODULE);
            else {
                I2CMODULE->I2CONSET = 1<<I2C_START;
                I2CMODULE->I2CONCLR = 1<<I2C_FLAG;
            }
            bufferHandler(I2CMODULE);
            break;

        default:
            *Data->status=I2CMODULE->I2STAT;
            bufferHandler(I2CMODULE);
            break;
    }
}


//**********************************************************
//*********************COMMAND BUFFER***********************
//**********************************************************

void MODI2C::bufferHandler(LPC_I2C_TypeDef *I2CMODULE) {
    I2CBuffer *Buffer;
    if (I2CMODULE == LPC_I2C1) {
        Buffer = &Buffer1;
    } else {
        Buffer = &Buffer2;
    }

    //Start user interrupt
    Buffer->Data[0].caller->runUserIRQ(Buffer->Data[0]);

    removeBuffer(I2CMODULE);



    if (Buffer->queue!=0)
        startBuffer(I2CMODULE);
    else
        _clearISR(I2CMODULE);
}

//Returns true if succeeded, false if buffer is full
bool MODI2C::addBuffer(I2CData Data, LPC_I2C_TypeDef *I2CMODULE) {
    I2CBuffer *Buffer;
    if (I2CMODULE == LPC_I2C1) {
        Buffer = &Buffer1;
    } else {
        Buffer = &Buffer2;
    }
    if (Buffer->queue<I2C_BUFFER) {
        
        if(Data.status == NULL) {
            Data.status = &defaultStatus;
            wait_us(1);     //I blame the compiler that this is needed
            }
        *Data.status = 0;
            
        Buffer->Data[Buffer->queue]=Data;
        Buffer->queue++;

        //If queue was empty, set I2C settings, start conversion
        if (Buffer->queue==1) {
            startBuffer(I2CMODULE);
        }

        return true;
    } else
        return false;

}

//Returns true if buffer still has data, false if empty
bool MODI2C::removeBuffer(LPC_I2C_TypeDef *I2CMODULE) {
    I2CBuffer *Buffer;
    if (I2CMODULE == LPC_I2C1) {
        Buffer = &Buffer1;
    } else {
        Buffer= &Buffer2;
    }

    if (Buffer->queue>0) {
        for (int i =0; i<Buffer->queue-1; i++)
            Buffer->Data[i]=Buffer->Data[i+1];
        Buffer->queue--;
    }
    if (Buffer->queue>0)
        return true;
    else
        return false;
}

//Starts new conversion
void MODI2C::startBuffer(LPC_I2C_TypeDef *I2CMODULE) {
    I2CBuffer *Buffer;
    if (I2CMODULE == LPC_I2C1) {
        Buffer = &Buffer1;
    } else {
        Buffer = &Buffer2;
    }

    //Write settings
    Buffer->Data[0].caller->writeSettings();
    Buffer->count=0;

    //Send Start
    _start(I2CMODULE);

    //Start ISR (when buffer wasnt empty this wont do anything)
    _setISR(I2CMODULE);

}