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

Dependencies:   FPointer

Dependents:   FreeIMU FreeIMU_external_magnetometer FreeIMU

Fork of MODI2C by Erik -

Files at this revision

API Documentation at this revision

Comitter:
Sissors
Date:
Sat Jun 30 14:55:14 2012 +0000
Child:
1:eed116eb680a
Commit message:
v1.0

Changed in this revision

MODI2C.cpp Show annotated file Show diff for this revision Revisions of this file
MODI2C.h Show annotated file Show diff for this revision Revisions of this file
MODI2C_IRQ.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MODI2C.cpp	Sat Jun 30 14:55:14 2012 +0000
@@ -0,0 +1,214 @@
+#include "MODI2C.h"
+
+MODI2C::I2CBuffer MODI2C::Buffer1 = {0,0};        //Sets the initial buffer empty and count on zero
+MODI2C::I2CBuffer MODI2C::Buffer2 = {0,0};        //Sets the initial buffer empty
+//int MODI2C::status=0;
+int MODI2C::defaultStatus=0;
+
+
+
+
+MODI2C::MODI2C(PinName sda, PinName scl) : led(LED3) {
+    //Check which connection we are using, if not correct, go to error status
+    if ((sda==p9) && (scl==p10))
+        I2CMODULE = LPC_I2C1;
+    else if ((sda==p28) && (scl==p27))
+        I2CMODULE = LPC_I2C2;
+    else
+        error("MODI2C pins not valid");
+
+    //Default settings:
+    frequency(100000);
+
+    writePinState();
+}
+
+int MODI2C::write(int address, char *data, int length, bool repeated, int *status) {
+
+    I2CData Data;
+    //Store relevant information
+    address &= 0xFE;
+    Data.caller = this;
+    Data.address = address;
+    Data.repeated = repeated;
+    Data.data = data;
+    Data.length = length;
+    Data.status = status;
+
+    while(!addBuffer(Data, I2CMODULE));
+
+    return 0;
+}
+
+int MODI2C::write(int address, char *data, int length, int *status) {
+    return write(address, data, length, false, status);
+    }
+
+int MODI2C::read_nb(int address, char *data, int length, bool repeated, int *status) {
+    //Store relevant information
+    address |= 0x01;
+
+    //isIdle check here
+    I2CData Data;
+
+
+    Data.caller = this;
+    Data.address = address;
+    Data.repeated = repeated;
+    Data.data = data;
+    Data.length = length;
+    Data.status = status;
+
+    while(!addBuffer(Data, I2CMODULE));
+
+    return 0;
+}
+
+int MODI2C::read_nb(int address, char *data, int length, int *status) {
+    return read_nb(address, data, length, false, status);
+    }
+
+int MODI2C::read(int address, char *data, int length, bool repeated) {
+    int stat;
+    //Store relevant information
+    address |= 0x01;
+
+    //isIdle check here
+    I2CData Data;
+
+
+    Data.caller = this;
+    Data.address = address;
+    Data.repeated = repeated;
+    Data.data = data;
+    Data.length = length;
+    Data.status = &stat;
+
+    while(!addBuffer(Data, I2CMODULE));
+    
+    I2CBuffer *Buffer;
+    if (I2CMODULE == LPC_I2C1) {
+        Buffer = &Buffer1;
+    } else {
+        Buffer = &Buffer2;
+    }
+    
+    while(Buffer->queue!=0)
+        wait_us(1);
+    
+    if (stat==0x58)         //Return zero if ended correctly, otherwise return return code.
+        return 0;
+    else
+        return stat;
+}
+
+
+void MODI2C::start( void ) {
+    _start(I2CMODULE);
+}
+
+void MODI2C::stop( void ) {
+    _stop(I2CMODULE);
+}
+
+void MODI2C::frequency(int hz) {
+    //The I2C clock by default runs on quarter of system clock, which is 96MHz
+    //So to calculate high/low count times, we do 96MHz/4/2/frequency
+    duty = 96000000/8/hz;
+    if (duty>65535)
+        duty=65535;
+    if (duty<4)
+        duty=4;
+}
+
+int MODI2C::getQueue( void ) {
+    I2CBuffer *Buffer;
+    if (I2CMODULE == LPC_I2C1) {
+        Buffer = &Buffer1;
+    } else {
+        Buffer = &Buffer2;
+    }
+    return Buffer->queue;
+    }
+
+
+
+//*******************************************
+//***********Internal functions**************
+//*******************************************
+
+
+void MODI2C::writeSettings( void ) {
+    I2CMODULE->I2CONSET = 1<<I2C_ENABLE;     //Enable I2C
+    I2CMODULE->I2CONCLR = I2C_STOP;
+    I2CMODULE->MMCTRL = 0;                   //Disable monitor mode
+    I2CMODULE->I2SCLH = duty;
+    I2CMODULE->I2SCLL = duty;
+
+}
+
+void MODI2C::writePinState( void ) {
+    if (I2CMODULE == LPC_I2C1) {
+        LPC_PINCON->PINSEL0 |= 0x0000000F;       //Sets pins as I2C
+        LPC_PINCON->PINMODE0 |= 0x0000000A;      //Neither pull up nor pull down
+        LPC_PINCON->PINMODE_OD0 |= 0x00000003;   //Open drain mode enabled
+    } else if (I2CMODULE == LPC_I2C2) {
+        LPC_PINCON->PINSEL0 |= (1<<21)|(1<<23); //Same story, different register settings
+        LPC_PINCON->PINMODE0 |= (1<<21)|(1<<23);
+        LPC_PINCON->PINMODE_OD0 |= (1<<10)|(1<<11);
+    }
+}
+
+
+
+void MODI2C::attach( void (*function)(void), int operation) {
+    IRQOp = operation;
+    callback.attach(function);
+}
+
+template<typename T>
+void MODI2C::attach(T *object, void (T::*member)(void), int operation) {
+    IRQOp = operation;
+    callback.attach(object, member);
+}
+
+void MODI2C::detach( void ) {
+    callback.attach(NULL);
+}
+
+void MODI2C::_start(LPC_I2C_TypeDef *I2CMODULE) {
+    if (!(I2CMODULE->I2CONSET & 1<<I2C_START))  //If already sent, skip
+        I2CMODULE->I2CONSET = 1<<I2C_START;     //Send start condition
+}
+
+void MODI2C::_stop(LPC_I2C_TypeDef *I2CMODULE) {
+    I2CMODULE->I2CONSET = 1<<I2C_STOP;      //Send stop condition
+    I2CMODULE->I2CONCLR = 1<<I2C_FLAG;
+}
+
+//Set interrupt vector
+void MODI2C::setISR(void) {
+    _setISR(I2CMODULE);
+}
+
+void MODI2C::_setISR(LPC_I2C_TypeDef *I2CMODULE) {
+    if (I2CMODULE == LPC_I2C1) {
+        NVIC_SetVector(I2C1_IRQn, (uint32_t)&IRQ1Handler);
+        NVIC_EnableIRQ(I2C1_IRQn);
+    } else if (I2CMODULE == LPC_I2C2) {
+        NVIC_SetVector(I2C2_IRQn, (uint32_t)&IRQ2Handler);
+        NVIC_EnableIRQ(I2C2_IRQn);
+    }
+}
+
+void MODI2C::clearISR( void ) {
+    _clearISR(I2CMODULE);
+}
+
+void MODI2C::_clearISR( LPC_I2C_TypeDef *I2CMODULE ) {
+    if (I2CMODULE == LPC_I2C1) {
+        NVIC_DisableIRQ(I2C1_IRQn);
+    } else if (I2CMODULE == LPC_I2C2) {
+        NVIC_DisableIRQ(I2C2_IRQn);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MODI2C.h	Sat Jun 30 14:55:14 2012 +0000
@@ -0,0 +1,248 @@
+/* 
+
+                            .       .
+                           / `.   .' \
+                   .---.  <    > <    >  .---.
+                   |    \  \ - ~ ~ - /  /    |
+                    ~-..-~             ~-..-~
+                \~~~\.'                    `./~~~/
+                 \__/                        \__/
+                  /                  .-    .  \
+           _._ _.-    .-~ ~-.       /       }   \/~~~/
+       _.-'q  }~     /       }     {        ;    \__/
+      {'__,  /      (       /      {       /      `. ,~~|   .     .
+       `''''='~~-.__(      /_      |      /- _      `..-'   \\   //
+                   / \   =/  ~~--~~{    ./|    ~-.     `-..__\\_//_.-'
+                  {   \  +\         \  =\ (        ~ - . _ _ _..---~
+                  |  | {   }         \   \_\
+                 '---.o___,'       .o___,'     "Stegosaurus"
+
+
+Face it, dinos much cooler than copyright notices.*/
+
+#include "mbed.h"
+
+
+#ifndef MODI2C_H
+#define MODI2C_H
+
+
+#define I2C_ENABLE          6
+#define I2C_START           5
+#define I2C_STOP            4
+#define I2C_FLAG            3
+#define I2C_ASSERT_ACK      2
+
+#define IRQ_I2C_BOTH        0
+#define IRQ_I2C_READ        1
+#define IRQ_I2C_WRITE       2
+
+
+
+#ifndef I2C_BUFFER
+#define I2C_BUFFER          10
+#endif
+
+
+/** Library that allows interrupt driven communication with I2C devices
+  *
+  * For now this is all in beta, so if you got weird results while the mbed I2C library works, it is probably my fault.
+  * Similar to googles definition of beta, it is unlikely it will ever come out of beta.
+  *
+  * Example:
+  * @code
+  * #include "mbed.h"
+  * #include "MODI2C.h"
+  * #include "MPU6050.h"
+  * 
+  * Serial pc(USBTX, USBRX); // tx, rx
+  * MODI2C mod(p9, p10);
+  * 
+  * int main() {
+  *     char registerAdd = MPU6050_WHO_AM_I_REG;
+  *     char registerResult;
+  *     int status;
+  * 
+  *     while (1) {
+  *         mod.write(MPU6050_ADDRESS*2, &registerAdd, 1, true);
+  *         mod.read_nb(MPU6050_ADDRESS*2, &registerResult, 1, &status);
+  * 
+  *         while (!status) wait_us(1);
+  *         pc.printf("Register holds 0x%02X\n\r", registerResult);
+  *         wait(2);
+  *     }
+  * }
+  * @endcode
+  */
+class MODI2C {
+public:
+    /**
+    * Constructor.
+    *
+    * @param sda - mbed pin to use for the SDA I2C line.
+    * @param scl - mbed pin to use for the SCL I2C line.
+    */
+    MODI2C(PinName sda, PinName scl);
+
+    /**
+    * Write data on the I2C bus.
+    *
+    * This function should generally be a drop-in replacement for the standard mbed write function.
+    * However this function is completely non-blocking, so it barely takes time. This can cause different behavior.
+    * Everything else is similar to read_nb.
+    *
+    * @param address - I2C address of the slave (7 bit address << 1).
+    * @param data - pointer to byte array that holds the data
+    * @param length - amount of bytes that need to be sent
+    * @param repeated - determines if it should end with a stop condition (default false)
+    * @param status - (optional) pointer to integer where the final status code of the I2C transmission is placed. (0x28 is success)
+    * @param return - returns zero
+    */
+    int write(int address, char *data, int length, bool repeated = false, int *status = NULL);
+    int write(int address, char *data, int length, int *status);
+
+    /**
+    * Read data non-blocking from the I2C bus.
+    *
+    * Reads data from the I2C bus, completely non-blocking. Aditionally it will always return zero, since it does
+    * not wait to see how the transfer goes. A pointer to an integer can be added as argument which returns the status.
+    *
+    * @param address - I2C address of the slave (7 bit address << 1).
+    * @param data - pointer to byte array where the data will be stored
+    * @param length - amount of bytes that need to be received
+    * @param repeated - determines if it should end with a stop condition
+    * @param status - (optional) pointer to integer where the final status code of the I2C transmission is placed. (0x58 is success)
+    * @param return - returns zero
+    */
+    int read_nb(int address, char *data, int length, bool repeated = false, int *status=NULL);
+    int read_nb(int address, char *data, int length, int *status);
+    
+    /**
+    * Read data from the I2C bus.
+    *
+    * This function should should be a drop-in replacement for the standard mbed function.
+    *
+    * @param address - I2C address of the slave (7 bit address << 1).
+    * @param data - pointer to byte array where the data will be stored
+    * @param length - amount of bytes that need to be received
+    * @param repeated - determines if it should end with a stop condition
+    * @param return - returns zero on success, LPC status code on failure
+    */
+    int read(int address, char *data, int length, bool repeated = false);
+
+    /**
+    * Sets the I2C bus frequency
+    *
+    * @param hz - the bus frequency in herz
+    */
+    void frequency(int hz);
+
+    /**
+    * Creates a start condition on the I2C bus
+    *
+    * If you use this function you probably break something (but mbed also had it public)
+    */
+    void start ( void );
+
+    /**
+    * Creates a stop condition on the I2C bus
+    *
+    * If you use this function you probably break something (but mbed also had it public)
+    */
+    void stop ( void );
+    
+    /**
+    * Removes attached function
+    */
+    void detach( void );
+    
+    /**
+    * Calls user function when I2C command is finished
+    *
+    * @param function - the function to call.
+    * @param operation - when to call IRQ: IRQ_I2C_BOTH (default) - IRQ_I2C_READ - IRQ_I2C_WRITE
+    */
+    void attach(void (*function)(void), int operation = IRQ_I2C_BOTH);
+    
+    /**
+    * Calls user function when I2C command is finished
+    *
+    * @param object - the object to call the function on.
+    * @param member - the function to call
+    * @param operation - when to call IRQ: IRQ_I2C_BOTH (default) - IRQ_I2C_READ - IRQ_I2C_WRITE
+    */
+    template<typename T>
+        void attach(T *object, void (T::*member)(void), int operation = IRQ_I2C_BOTH);
+        
+    /**
+    * Returns the current number of commands in the queue (including one currently being processed)
+    *
+    * Note that this is the number of commands, not the number of bytes
+    *
+    * @param return - number of commands in queue
+    */
+    int getQueue( void );
+
+
+private:
+
+    struct I2CData {
+        MODI2C *caller;
+        char address;
+        char *data;
+        int  length;
+        bool repeated;
+        int *status;
+    };
+    
+    struct I2CBuffer {
+        int queue;
+        int count;
+        I2CData Data[I2C_BUFFER];
+        };
+        
+    //Settings:
+    int duty;
+    
+    FunctionPointer callback;
+    int IRQOp;   
+    void runUserIRQ( I2CData Data );
+
+    //Remove later:
+    LPC_I2C_TypeDef *I2CMODULE;
+
+    
+
+    //Whole bunch of static stuff, pretty much everything that is ever called from ISR
+    static I2CBuffer Buffer1;
+    static I2CBuffer Buffer2;
+    
+    static void IRQHandler(I2CBuffer *Buffer, LPC_I2C_TypeDef *I2CMODULE);
+    static void IRQ1Handler(void);
+    static void IRQ2Handler(void);
+    
+    static void bufferHandler(LPC_I2C_TypeDef *I2CMODULE);
+    static bool addBuffer(I2CData Data, LPC_I2C_TypeDef *I2CMODULE);
+    static bool removeBuffer(LPC_I2C_TypeDef *I2CMODULE);
+    static void startBuffer(LPC_I2C_TypeDef *I2CMODULE);
+
+    static int defaultStatus;
+    
+    static void _start(LPC_I2C_TypeDef *I2CMODULE);
+    static void _stop(LPC_I2C_TypeDef *I2CMODULE);
+    static void _clearISR( LPC_I2C_TypeDef *I2CMODULE );
+    static void _setISR( LPC_I2C_TypeDef *I2CMODULE );
+
+
+    void writeSettings( void );
+    void writePinState( void );
+    void setISR( void );
+    void clearISR( void );
+
+
+    DigitalOut led;
+
+};
+
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MODI2C_IRQ.cpp	Sat Jun 30 14:55:14 2012 +0000
@@ -0,0 +1,228 @@
+#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);
+
+}
\ No newline at end of file