RTOS enabled i2c-driver based on the official i2c-C-api.

Dependencies:   mbed-rtos

Fork of mbed-RtosI2cDriver by Helmut Schmücker

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers i2cRtos_api.c Source File

i2cRtos_api.c

00001 #include "i2cRtos_api.h"
00002 
00003 #if DEVICE_I2C
00004 
00005 #include "us_ticker_api.h"
00006 #include "cmsis_os.h"
00007 #include "error.h"
00008 
00009 // little helpers cloned from official i2c api
00010 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
00011 #define I2C_CONSET(x)       (x->i2c->I2CONSET)
00012 #define I2C_CONCLR(x)       (x->i2c->I2CONCLR)
00013 #define I2C_STAT(x)         (x->i2c->I2STAT)
00014 #define I2C_DAT(x)          (x->i2c->I2DAT)
00015 #elif defined(TARGET_LPC11U24)
00016 #define I2C_CONSET(x)       (x->i2c->CONSET)
00017 #define I2C_CONCLR(x)       (x->i2c->CONCLR)
00018 #define I2C_STAT(x)         (x->i2c->STAT)
00019 #define I2C_DAT(x)          (x->i2c->DAT)
00020 #endif
00021 
00022 //#include "gpio_api.h"
00023 //static gpio_t gpio[2]; // evillive
00024 
00025 // isr/thread data transfer struct
00026 enum I2cIsrCmd {
00027     readMst,
00028     writeMst,
00029     readSlv,
00030     writeSlv,
00031     waitSI
00032 };
00033 struct I2cIsrTransfer {
00034     i2c_t* obj;
00035     enum I2cIsrCmd cmd;
00036     int len;
00037     int cnt;
00038     int stat;
00039     char* rData;
00040     const char* wData;
00041 };
00042 // one for each channel
00043 // "volatile" has been omitted since ISR and thread do not run simultaneously, hopefully ok
00044 static struct I2cIsrTransfer i2c_transfer[2];
00045 
00046 // struct holding IRQ and semaphore ID
00047 // needed for thread<->isr communication/activation
00048 struct IsrIrqSem {
00049     IRQn_Type irq;
00050     osSemaphoreId sem;
00051 };
00052 static struct IsrIrqSem isrIrqSem[2]; // one for each channel
00053 
00054 
00055 // little helpers cloned from official i2c api
00056 static inline void i2c_conclr(i2c_t *obj, int start, int stop, int interrupt, int acknowledge)
00057 {
00058     I2C_CONCLR(obj) = (start << 5)
00059                       | (stop << 4)
00060                       | (interrupt << 3)
00061                       | (acknowledge << 2);
00062 }
00063 static inline void i2c_conset(i2c_t *obj, int start, int stop, int interrupt, int acknowledge)
00064 {
00065     I2C_CONSET(obj) = (start << 5)
00066                       | (stop << 4)
00067                       | (interrupt << 3)
00068                       | (acknowledge << 2);
00069 }
00070 static inline void i2c_clear_SI(i2c_t *obj)
00071 {
00072     i2c_conclr(obj, 0, 0, 1, 0);
00073 }
00074 static inline int i2c_status(i2c_t *obj)
00075 {
00076     return I2C_STAT(obj);
00077 }
00078 
00079 // ISR routines
00080 // implements the same read/write sequences as the official i2c lib
00081 static void i2cRtos_isr(uint32_t ch)
00082 {
00083     struct I2cIsrTransfer* tr=&(i2c_transfer[ch]);
00084     if(tr->cmd==waitSI) {
00085         // just waiting for an interrupt after a byte read/write or slave receive call
00086         osSemaphoreRelease(isrIrqSem[ch].sem);
00087         NVIC_DisableIRQ(isrIrqSem[ch].irq);
00088         return;
00089     }
00090     int stat=i2c_status(tr->obj);
00091     int stay = 0;
00092     switch(tr->cmd) {
00093         case readMst:
00094             switch(stat) {
00095                 case 0x50: // Data byte has been received; ACK has been returned.
00096                     (tr->rData)[tr->cnt] = (char)(I2C_DAT(tr->obj) & 0xff);
00097                 case 0x40: // SLA+R has been transmitted; ACK has been received.
00098                     ++(tr->cnt);
00099                     if(tr->cnt != tr->len-1)
00100                         i2c_conset(tr->obj, 0, 0, 0, 1);
00101                     else
00102                         i2c_conclr(tr->obj, 0, 0, 0, 1); // do not ack the last byte read
00103                     stay = 1;
00104                     break;
00105                 case 0x58: // Data byte has been received; NOT ACK has been returned.
00106                     (tr->rData)[tr->cnt] = (char)(I2C_DAT(tr->obj) & 0xff);
00107                     stat=0;
00108                     break;
00109             }
00110             break;
00111         case writeMst:
00112             switch(stat) {
00113                 case 0x18: // SLA+W has been transmitted; ACK has been received.
00114                 case 0x28: // SLA+W has been transmitted; NOT ACK has been received.
00115                     if(++(tr->cnt)!=tr->len) {
00116                         I2C_DAT(tr->obj) = (tr->wData)[tr->cnt];
00117                         stay=1;
00118                     } else {
00119                         stat=0;
00120                     }
00121             }
00122             break;
00123         case readSlv:
00124             ++(tr->cnt);
00125             if(stat==0x80 || stat==0x90) // Previously addressed with own SLA address(0x80) or geberal call (0x90); DATA has been received; ACK has been returned.
00126                 (tr->rData)[tr->cnt] = I2C_DAT(tr->obj) & 0xFF;
00127             stay = (stat==0x80 || stat==0x90 || stat==0x060 || stat==0x70) && (tr->cnt < tr->len-1);
00128             // 60: Own SLA+W has been received; ACK has been returned. ... SLV+W???
00129             // 70: General Call address (0x00) has been received; ACK has been returned.
00130             break;
00131         case writeSlv:
00132             ++(tr->cnt);
00133             stay = tr->cnt<tr->len && stat==0xb8; // Data byte in I2DAT has been transmitted; ACK has been received.
00134             if(stay) I2C_DAT(tr->obj) = tr->wData[tr->cnt];
00135             break;
00136     }
00137     if(stay) {
00138         // sequence not finished => stay in ISR mode and trigger next i2c by clearing he SI bit
00139         i2c_clear_SI(tr->obj);
00140     } else {
00141         // sequence finished or unexpected state has been reported
00142         // => bail out of isr mode and return last status received
00143         tr->stat = stat;
00144         osSemaphoreRelease(isrIrqSem[ch].sem);
00145         NVIC_DisableIRQ(isrIrqSem[ch].irq);
00146     }
00147 }
00148 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
00149 static void i2cRtos_isr_ch0()
00150 {
00151     //gpio_write(&gpio[0], 1);
00152     i2cRtos_isr(0);
00153     //gpio_write(&gpio[0], 0);
00154 }
00155 #endif
00156 static void i2cRtos_isr_ch1()
00157 {
00158     //gpio_write(&gpio[1], 1);
00159     i2cRtos_isr(1);
00160     //gpio_write(&gpio[1], 0);
00161 }
00162 
00163 
00164 // determine channel
00165 static inline uint32_t i2c_get_channel(const i2c_t *obj)
00166 {
00167 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
00168     switch((I2CName)(obj->i2c)) {
00169         case I2C_1:
00170             return 0;
00171         case I2C_2:
00172             return 1;
00173         default:
00174             error("Dial911 i2c_get_channel: Invaid I2CName \n");
00175     }
00176 #endif
00177     return 1;
00178 }
00179 
00180 
00181 // Enable IRQ and wait for ISR sequence to be finished
00182 static inline void i2cRtos_wait_and_see(i2c_t *obj, int ch, uint32_t tmOut)  //evillive
00183 {
00184     struct IsrIrqSem* iis = &(isrIrqSem[ch]);
00185     __disable_irq(); // evil, but don't want the next three lines to be interrupted
00186     i2c_clear_SI(obj);
00187     NVIC_ClearPendingIRQ(iis->irq);
00188     NVIC_EnableIRQ(iis->irq);
00189     __enable_irq();
00190     if(osSemaphoreWait(iis->sem, tmOut)!=1) NVIC_DisableIRQ(iis->irq); // time out => diable the IRQ
00191 }
00192 
00193 // just wait for a generic i2c interrupt
00194 static inline void i2cRtos_waitSI(i2c_t *obj, uint32_t tmOut)
00195 {
00196     int ch = i2c_get_channel(obj);
00197     i2c_transfer[ch].cmd = waitSI;
00198     i2cRtos_wait_and_see(obj, ch, tmOut);
00199 }
00200 
00201 // master mode read sequence
00202 int i2cRtos_read(i2c_t *obj, int address, char *data, int length, int stop)
00203 {
00204     //gpio_write(&gpio[1], 1);
00205     int stat = i2c_start(obj);
00206     if ((stat != 0x10) && (stat != 0x08)) {
00207         i2cRtos_stop(obj); // use freeze free stop if the start has failed
00208         return stat;
00209     }
00210     //gpio_write(&gpio[1], 0);
00211     int ch = i2c_get_channel(obj);
00212     struct I2cIsrTransfer* tr = &(i2c_transfer[ch]);
00213     tr->obj=obj;
00214     tr->cmd=readMst;
00215     tr->len=length;
00216     tr->cnt=-1;
00217     tr->rData=data;
00218     I2C_DAT(obj) = address | 0x01;  // initiate address+R write and enter isr mode
00219     i2cRtos_wait_and_see(obj, ch,2+(length>>2));  // timeout (2+len/4)ms
00220     stat = tr->stat;
00221     //gpio_write(&gpio[1], 1);
00222     if(stat || stop) i2cRtos_stop(obj);
00223     //gpio_write(&gpio[1], 0);
00224     return stat;
00225 }
00226 
00227 // master mode write sequence
00228 int i2cRtos_write(i2c_t *obj, int address, const char *data, int length, int stop)
00229 {
00230     //gpio_write(&gpio[1], 1);
00231     int status = i2c_start(obj);
00232     if ((status != 0x10) && (status != 0x08)) {
00233         i2cRtos_stop(obj); // use freeze free stop if the start has failed
00234         return status;
00235     }
00236     //gpio_write(&gpio[1], 0);
00237     int ch = i2c_get_channel(obj);
00238     struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
00239     tr->obj = obj;
00240     tr->cmd = writeMst;
00241     tr->len = length;
00242     tr->cnt = -1;
00243     tr->wData = data;
00244     I2C_DAT(obj) = address & 0xfe; // initiate address+W write and enter isr mode
00245     i2cRtos_wait_and_see(obj, ch, 2+(length>>2));  // timeout (2+len/4)ms
00246     //i2c_clear_SI(obj); // ... why? Also in official lib ... I guess this is the "write instead of start" bug
00247     status = tr->stat;
00248     //gpio_write(&gpio[1], 1);
00249     if(status || stop) i2cRtos_stop(obj);
00250     //gpio_write(&gpio[1], 0);
00251     return status;
00252 }
00253 
00254 // read single byte from bus (master/slave)
00255 int i2cRtos_byte_read(i2c_t *obj, int last)
00256 {
00257     if(last) {
00258         i2c_conclr(obj, 0, 0, 0, 1); // send a NOT ACK
00259     } else {
00260         i2c_conset(obj, 0, 0, 0, 1); // send a ACK
00261     }
00262     i2cRtos_waitSI(obj, 2);
00263     return (I2C_DAT(obj) & 0xff);
00264 }
00265 
00266 // write single byte to bus (master/slave)
00267 int i2cRtos_byte_write(i2c_t *obj, int data)
00268 {
00269     I2C_DAT(obj) = (data & 0xff);
00270     i2cRtos_waitSI(obj, 2);
00271     int stat=i2c_status(obj);
00272     return (stat==0x18 || stat==0x28 || stat==0x40 || stat==0xb8);
00273 }
00274 
00275 // freeze free i2c stop
00276 // the busy wait without timeout of the official i2c lib
00277 // might freeze the mbed if someone on the bus keeps the clock down
00278 // and prevents LPC's i2c controller to create a stop condition
00279 int i2cRtos_stop(i2c_t *obj)
00280 {
00281     i2c_conset(obj, 0, 1, 0, 0);
00282     i2c_clear_SI(obj);
00283     uint32_t t0=us_ticker_read();
00284     uint32_t dt=0;
00285     while((I2C_CONSET(obj) & (1 << 4)) && dt<23) {
00286         dt = us_ticker_read() - t0;
00287     }
00288     return dt<23;
00289 }
00290 
00291 
00292 #if DEVICE_I2CSLAVE
00293 
00294 // determine slave's receive mode. Blocking with timeout
00295 int i2cRtos_slave_receive(i2c_t *obj, uint32_t tmOut)
00296 {
00297     int retval = i2c_slave_receive(obj);
00298     //check for pending requests
00299     if(retval)return retval; // there is one => bail out
00300     // No request? Wait for it! ... with time out
00301     i2cRtos_waitSI(obj, tmOut);
00302     // check again for pending requests
00303     return i2c_slave_receive(obj);
00304 }
00305 
00306 // slave mode read sequence
00307 int i2cRtos_slave_read(i2c_t *obj, char *data, int length)
00308 {
00309     int ch = i2c_get_channel(obj);
00310     struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
00311     tr->obj=obj;
00312     tr->cmd=readSlv;
00313     tr->len=length;
00314     tr->cnt=-1;
00315     tr->rData=data;
00316     i2cRtos_wait_and_see(obj, ch, 2+(length>>2));  // timeout (1+len/4)ms
00317     if(tr->stat != 0xa0) {
00318         i2cRtos_stop(obj);
00319     }
00320     i2c_clear_SI(obj); // stop keeping scl low
00321     return tr->cnt;    // same weird return as in official lib
00322 }
00323 
00324 // slave mode write sequence
00325 int i2cRtos_slave_write(i2c_t *obj, const char *data, int length)
00326 {
00327     if(length <= 0) {
00328         return(0);
00329     }
00330     int ch = i2c_get_channel(obj);
00331     struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
00332     tr->obj=obj;
00333     tr->cmd=writeSlv;
00334     tr->len=length;
00335     tr->cnt=0;
00336     tr->wData=data;
00337     I2C_DAT(obj) = data[0];
00338     i2cRtos_wait_and_see(obj, ch, 2+(length>>2));  // timeout (1+len/4)ms
00339     int status = tr->stat;
00340     if(status!=0xC0 && status!=0xC8) {
00341         i2cRtos_stop(obj);
00342     }
00343     i2c_clear_SI(obj); // stops keeping scl low
00344     return tr->cnt;
00345 }
00346 #endif
00347 
00348 // setup semaphores and hook in ISRs
00349 void i2cRtos_init(i2c_t *obj, PinName sda, PinName scl)
00350 {
00351     /*static int called=0;
00352     if(!called) {
00353         gpio_init(&gpio[0], p15, PIN_OUTPUT);
00354         gpio_init(&gpio[1], p16, PIN_OUTPUT);
00355     }
00356     called = 1;
00357     gpio_write(&gpio[0], 0);
00358     gpio_write(&gpio[1], 0);*/
00359     
00360     i2c_init(obj,sda,scl);
00361     uint32_t ch = i2c_get_channel(obj);
00362 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
00363     static osSemaphoreDef(i2cIsrDrvSem_ch0);
00364     static osSemaphoreDef(i2cIsrDrvSem_ch1);
00365     switch(ch) {
00366         case 0:
00367             isrIrqSem[ch].irq = I2C1_IRQn;
00368             NVIC_SetVector(I2C1_IRQn, (uint32_t)i2cRtos_isr_ch0);
00369             isrIrqSem[ch].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch0), 1);
00370             break;
00371         case 1:
00372             isrIrqSem[ch].irq = I2C2_IRQn;
00373             NVIC_SetVector(I2C2_IRQn, (uint32_t)i2cRtos_isr_ch1);
00374             isrIrqSem[ch].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch1), 1);
00375             break;
00376     }
00377     osSemaphoreWait(isrIrqSem[ch].sem, osWaitForever);
00378 #elif defined(TARGET_LPC11U24)
00379     static osSemaphoreDef(i2cIsrDrvSem_ch1);
00380     isrIrqSem[ch].irq = I2C_IRQn;
00381     NVIC_SetVector(I2C_IRQn, (uint32_t)i2cRtos_isr_ch1);
00382     isrIrqSem[ch].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch1), 1);
00383     osSemaphoreWait(isrIrqSem[ch].sem, osWaitForever);
00384 #else
00385 #error "Dial911 i2cRtos_init: Unsupported HW"
00386 #endif
00387 }
00388 #endif