Ian Hua / I2cRtosDriver

Fork of I2cRtosDriver by Helmut Schmücker

i2cRtos_api.c

Committer:
humlet
Date:
2013-05-04
Revision:
9:65aae53a34de
Child:
10:e3d6c92ff222

File content as of revision 9:65aae53a34de:

#include "i2cRtos_api.h"

#if DEVICE_I2C

#include "cmsis_os.h"
#include "error.h"

#define I2C_CONSET(x)       (x->i2c->I2CONSET)
#define I2C_CONCLR(x)       (x->i2c->I2CONCLR)
#define I2C_STAT(x)         (x->i2c->I2STAT)
#define I2C_DAT(x)          (x->i2c->I2DAT)

enum I2cIsrCmd {
    readMst,
    writeMst,
    readSlv,
    writeSlv,
    waitSI
};
struct I2cIsrTransfer {
    i2c_t* obj;
    enum I2cIsrCmd cmd;
    int adr;
    int len;
    int cnt;
    int stat;
    char* rData;
    const char* wData;
};
volatile static struct I2cIsrTransfer i2c_transfer[2];  // evillive: dare to get rid of volatile?


struct IsrIrqSem {
    IRQn_Type irq;
    uint32_t isr;
    osSemaphoreId sem;
};
static struct IsrIrqSem isrIrqSem[2];


// little helpers cloned from official i2c api
static inline void i2c_conclr(i2c_t *obj, int start, int stop, int interrupt, int acknowledge)
{
    I2C_CONCLR(obj) = (start << 5)
                      | (stop << 4)
                      | (interrupt << 3)
                      | (acknowledge << 2);
}
static inline void i2c_conset(i2c_t *obj, int start, int stop, int interrupt, int acknowledge)
{
    I2C_CONSET(obj) = (start << 5)
                      | (stop << 4)
                      | (interrupt << 3)
                      | (acknowledge << 2);
}
static inline void i2c_clear_SI(i2c_t *obj)
{
    i2c_conclr(obj, 0, 0, 1, 0);
}
static inline int i2c_status(i2c_t *obj)
{
    return I2C_STAT(obj);
}


// ISR stuff
static inline void i2cRtos_isr(uint32_t ch)
{
    volatile struct I2cIsrTransfer* tr=&(i2c_transfer[ch]);

    if(tr->cmd==waitSI) {
        osSemaphoreRelease(isrIrqSem[ch].sem);
        NVIC_DisableIRQ(isrIrqSem[ch].irq);
        return;
    }

    int stat=i2c_status(tr->obj);
    int stay = 0;

    switch(tr->cmd) {
        case readMst: {
            int cnt = (tr->cnt)++;
            if(cnt==-1) {
                if(stat==0x40) {
                    i2c_conset(tr->obj, 0, 0, 0, 1);
                    stay = 1;
                }
            } else if(cnt < tr->len-1) {
                if(stat==0x50) {
                    (tr->rData)[cnt] = (char)(I2C_DAT(tr->obj) & 0xff);
                    if(cnt != tr->len-2) {
                        i2c_conset(tr->obj, 0, 0, 0, 1);
                    } else {
                        i2c_conclr(tr->obj, 0, 0, 0, 1);
                    }
                    stay = 1;
                }
            } else {
                if(stat==0x58) {
                    (tr->rData)[cnt] = (char)(I2C_DAT(tr->obj) & 0xff);
                    stat = 0;
                }
            }
            break;
        }
        case writeMst: {
            int cnt = ++(tr->cnt);
            if(cnt==0) {
                if(stat==0x18) {
                    I2C_DAT(tr->obj) = (tr->wData)[cnt];
                    stay = 1;
                }
            } else if(cnt < tr->len) {
                if(stat==0x28) {
                    I2C_DAT(tr->obj) = (tr->wData)[cnt];
                    stay = 1;
                }
            } else {
                if(stat==0x28) {
                    stat = 0;
                }
            }
            break;
        }
        case readSlv: {
            int cnt = ++(tr->cnt);
            if(stat==0x80 || stat==0x90)
                (tr->rData)[cnt] = I2C_DAT(tr->obj) & 0xFF;
            stay = (stat==0x80 || stat==0x90 || stat==0x060 || stat==0x70) && (cnt < tr->len-1);
            break;
        }
        case writeSlv: {
            int cnt = ++(tr->cnt);
            stay = cnt<tr->len && stat==0xb8;
            if(stay)
                I2C_DAT(tr->obj) = tr->wData[cnt];
            break;
        }
        default:
            error("Dial911 i2cRtos_isr\n");
    }
    if(stay) {
        i2c_clear_SI(tr->obj);
    } else {
        tr->stat = stat;
        osSemaphoreRelease(isrIrqSem[ch].sem);
        NVIC_DisableIRQ(isrIrqSem[ch].irq);
    }
}
static inline void i2cRtos_isr_ch0()
{
    i2cRtos_isr(0);
}
static inline void i2cRtos_isr_ch1()
{
    i2cRtos_isr(1);
}


// determine channel
static inline uint32_t i2c_get_channel(i2c_t *obj)
{
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
    switch((I2CName)(obj->i2c)) {
        case I2C_1:
            return 0;
        case I2C_2:
            return 1;
        default:
            error("Dial911 i2c_get_channel: Invaid I2CName \n");
    }
#elif defined(TARGET_LPC11U24)
    return 1;
#else
    #error "Dial911 i2c_get_channel: Unsupported HW"
#endif
return 1;
}


// wait for ISR finished
static inline void i2cRtos_wait_and_see(i2c_t *obj, int channel, uint32_t tmOut)
{
    struct IsrIrqSem* iis = &(isrIrqSem[channel]);
    //NVIC_ClearPendingIRQ(iis->irq);  // evillive
    i2c_clear_SI(obj);
    NVIC_EnableIRQ(iis->irq);
    if(osSemaphoreWait(iis->sem, tmOut)!=1) NVIC_DisableIRQ(iis->irq);
}


// setup semaphores and hook in ISRs
void i2cRtos_init(i2c_t *obj, PinName sda, PinName scl)
{
    i2c_init(obj,sda,scl);
    uint32_t ch = i2c_get_channel(obj);
#if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
    static osSemaphoreDef(i2cIsrDrvSem_ch0);
    static osSemaphoreDef(i2cIsrDrvSem_ch1);
    switch(ch) {
        case 0:
            isrIrqSem[ch].irq = I2C1_IRQn;
            isrIrqSem[ch].isr = (uint32_t)i2cRtos_isr_ch0;
            isrIrqSem[ch].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch0), 1);
            osSemaphoreWait(isrIrqSem[ch].sem, osWaitForever);
            break;
        case 1:
            isrIrqSem[ch].irq = I2C2_IRQn;
            isrIrqSem[ch].isr = (uint32_t)i2cRtos_isr_ch1;
            isrIrqSem[ch].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch1), 1);
            osSemaphoreWait(isrIrqSem[ch].sem, osWaitForever);
            break;
    }
#elif defined(TARGET_LPC11U24)
    static osSemaphoreDef(i2cIsrDrvSem_ch1);
    isrIrqSem[ch].irq = I2C_IRQn;
    isrIrqSem[ch].isr = (uint32_t)i2cRtos_isr_ch1;
    isrIrqSem[1].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch1), 1);
    osSemaphoreWait(isrIrqSem[0].sem, osWaitForever);
#else
    #error "Dial911 i2cRtos_init: Unsupported HW"
#endif
}

int i2cRtos_read(i2c_t *obj, int address, char *data, int length, int stop)
{
    int status = i2c_start(obj);
    if ((status != 0x10) && (status != 0x08)) {
        i2c_stop(obj);
        return status;
    }
    int ch = i2c_get_channel(obj);
    volatile struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
    tr->obj=obj;
    tr->cmd=readMst;
    tr->len=length;
    tr->cnt=-1;
    tr->rData=data;
    I2C_DAT(obj) = address | 0x01;
    i2cRtos_wait_and_see(obj, ch, 1+(length>>2));  // timeout (1+len/4)ms
    status = tr->stat;
    if(status || stop) i2c_stop(obj);
    return status;
}

int i2cRtos_write(i2c_t *obj, int address, const char *data, int length, int stop)
{
    int status = i2c_start(obj);
    if ((status != 0x10) && (status != 0x08)) {
        i2c_stop(obj);
        return status;
    }
    int ch = i2c_get_channel(obj);
    volatile struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
    tr->obj=obj;
    tr->cmd=writeMst;
    tr->len=length;
    tr->cnt=-1;
    tr->wData=data;
    I2C_DAT(obj) = address & 0xfe;
    i2cRtos_wait_and_see(obj, ch, 1+(length>>2));  // timeout (1+len/4)ms
    i2c_clear_SI(obj); // ... why? Also in official lib
    status = tr->stat;
    if(status || stop) i2c_stop(obj);
    return status;
}

int i2cRtos_byte_read(i2c_t *obj, int last)
{
    if(last) {
        i2c_conclr(obj, 0, 0, 0, 1); // send a NOT ACK
    } else {
        i2c_conset(obj, 0, 0, 0, 1); // send a ACK
    }
    int ch = i2c_get_channel(obj);
    i2c_transfer[ch].cmd = waitSI;
    i2cRtos_wait_and_see(obj, ch, 1);
    return (I2C_DAT(obj) & 0xff);
    
}

int i2cRtos_byte_write(i2c_t *obj, int data)
{
    I2C_DAT(obj) = (data & 0xff);
    int ch = i2c_get_channel(obj);
    i2c_transfer[ch].cmd = waitSI;
    i2cRtos_wait_and_see(obj, ch, 1);
    int stat=i2c_status(obj);
    return (stat==0x18 || stat==0x28 || stat==0x40 || stat==0xb8);
}


#if DEVICE_I2CSLAVE


int i2cRtos_slave_receive(i2c_t *obj, uint32_t tmOut)
{
    int retval = i2c_slave_receive(obj);
    //check for pending requests
    if(retval)return retval; // there is one => bail out

    // No request? Wait for it!
    int ch = i2c_get_channel(obj);
    i2c_transfer[ch].cmd = waitSI;
    i2cRtos_wait_and_see(obj, ch, tmOut);
    // check again for pending requests
    return i2c_slave_receive(obj);
}

int i2cRtos_slave_read(i2c_t *obj, char *data, int length)
{
    int ch = i2c_get_channel(obj);
    volatile struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
    tr->obj=obj;
    tr->cmd=readSlv;
    tr->len=length;
    tr->cnt=-1;
    tr->rData=data;
    i2cRtos_wait_and_see(obj, ch, 1+(length>>2));  // timeout (1+len/4)ms
    if(tr->stat != 0xa0) {
        i2c_stop(obj);
    }
    i2c_clear_SI(obj); // ... why? Also in official lib
    return tr->cnt;    // same weird return as in official lib
}

int i2cRtos_slave_write(i2c_t *obj, const char *data, int length)
{
    if(length <= 0) {
        return(0);
    }
    int ch = i2c_get_channel(obj);
    volatile struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
    tr->obj=obj;
    tr->cmd=writeSlv;
    tr->len=length;
    tr->cnt=0;
    tr->wData=data;
    I2C_DAT(obj) = data[0];
    i2cRtos_wait_and_see(obj, ch, 1+(length>>2));  // timeout (1+len/4)ms
    int status = tr->stat;
    if(status!=0xC0 && status!=0xC8) {
        i2c_stop(obj);
    }
    i2c_clear_SI(obj); // ... why? Also in official lib
    return tr->cnt;
}

#endif
#endif