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

Dependencies:   mbed-rtos

Fork of mbed-RtosI2cDriver by Helmut Schmücker

I2cRtosDriver

Overview

  • Based on RTOS
    • Less busy wait waste of CPU cycles
    • ... but some waste of CPU cycles by context switches
    • Frees up to 80% of CPU resources
  • Fixes the bug described in https://mbed.org/forum/bugs-suggestions/topic/4128/
  • Spends minimal time in interrupt context
  • Supports I2C Master and Slave mode
  • Interface compatible to official I2C lib
  • Supports LPC1768 and LPC11U24.
  • Reuses parts of the official I2C implementation
  • The test and example programs work quite well and the results look promising. But this is by no means a thoroughly regression tested library. There might be some surprises left.
  • If you want to avoid the RTOS overhead MODI2C might be a better choice.

Usage

  • In existing projects simply replace in the I2C interface class declaration the official type by one of the adapters I2CMasterRtos or I2CSlaveRtos described below. The behavior should be the same.
  • You can also use the I2CDriver interface directly.
  • You can create several instances of I2CMasterRtos, I2CSlaveRtos and I2CDriver. The interface classes are lightweight and work in parallel.
  • See also the tests/examples in I2CDriverTest01.h - I2CDriverTest05.h
  • The I2CDriver class is the central interface
    • I2CDriver provides a "fat" API for I2C master and slave access
    • It supports on the fly changes between master and slave mode.
    • All requests are blocking. Other threads might do their work while the calling thread waits for the i2c requests to be completed.
    • It ensures mutual exclusive access to the I2C HW.
      • This is realized by a static RTOS mutex for each I2C channel. The mutex is taken by the calling thread on any call of an I2CDriver-function.
      • Thus accesses are prioritized automatically by the priority of the calling user threads.
      • Once having access to the interface the requests are performed with high priority and cannot be interrupted by other threads.
      • Optionally the interface can be locked manually. Useful if one wants to perform a sequence of commands without interruption.
  • I2CMasterRtos and I2CSlaveRtos provide an interface compatible to the official mbed I2C interface. Additionally
    • the constructors provide parameters for defining the frequency and the slave address
    • I2CMasterRtos provides a function to read data from a given slave register
    • In contrast to the original interface the I2CSlaveRtos::receive() function is blocking, i.e it returns, when the master sends a request to the listening slave. There is no need to poll the receive status in a loop. Optionally a timeout value can be passed to the function.
    • The stop function provides a timeout mechanism and returns the status. Thus if someone on the bus inhibits the creation of a stop condition by keeping the scl or the sda line low the mbed master won't get freezed.
    • The interface adapters are implemented as object adapters, i.e they hold an I2CDriver-instance, to which they forward the user requests by simple inline functions. The overhead is negligible.

Design

The i2c read and write sequences have been realized in an interrupt service routine. The communicaton between the calling thread and the ISR is realized by a simple static transfer struct and a semaphore ... see i2cRtos_api.c
The start and stop functions still use the busy wait approach. They are not entered that frequently and usually they take less than 12µs at 100kHz bus speed. At 400kHz even less time is consumed. Thus there wouldn't be much benefit if one triggers the whole interrupt/task wait/switch sequence for that short period of time.

Performance

The following performance data have been measured with the small test applications in I2CDriverTest01.h and I2CDriverTest04.h . In these applications a high priority thread, triggered at a rate of 1kHz, reads on each trigger a data packet of given size with given I2C bus speed from a SRF08 ultra sonic ranger or a MPU6050 accelerometer/gyro. At the same time the main thread - running at a lower priority - counts in an endless loop adjacent increments of the mbed's µs-ticker API and calculates a duty cycle from this. These duty cycle measurements are shown in the table below together with the time measured for one read sequence (write address+register; write address and read x byte of data). The measurements have been performed with the ISR/RTOS approach used by this driver and with the busy wait approach used by the official mbed I2C implementation. The i2c implementation can be selected via #define PREFIX in I2CDriver.cpp.

  • The time for one read cycle is almost the same for both approaches
  • At full load the duty cycle of the low priority thread drops almost to zero for the busy wait approach, whereas with the RTOS/ISR enabled driver it stays at 80%-90% on the LPC1768 and above 65% on the LPC11U24.
  • => Especially at low bus speeds and/or high data transfer loads the driver is able to free a significant amount of CPU time.
LPC17681byte/ms4byte/ms6byte/ms1byte/ms6byte/ms12byte/ms25byte/ms
SRF08@ 100kHz@ 100kHz@ 100kHz@ 400kHz@ 400kHz@ 400kHz@ 400kHz
rtos/ISRDC[%]91.791.090.593.391.990.386.8
t[µs]421714910141314518961
busy waitDC[%]57.127.78.185.868.748.23.8
t[µs]415710907128299503949
LPC17681byte/ms4byte/ms7byte/ms1byte/ms6byte/ms12byte/ms36byte/ms
MPU6050@ 100kHz@ 100kHz@ 100kHz@ 400kHz@ 400kHz@ 400kHz@ 400kHz
rtos/ISRDC[%]91.590.789.393.091.690.084.2
t[µs]415687959133254398977
busy waitDC[%]57.730.53.386.574.359.71.2
t[µs]408681953121243392974
LPC11U241byte/ms6byte/ms1byte/ms6byte/ms23byte/ms
SRF08@ 100kHz@ 100kHz@ 400kHz@ 400kHz@ 400kHz
rtos/ISRDC[%]79.277.581.178.771.4
t[µs]474975199374978
busy waitDC[%]51.82.480.5633.3
t[µs]442937156332928
LPC11U241byte/ms6byte/ms1byte/ms6byte/ms32byte/ms
MPU6050@ 100kHz@ 100kHz@ 400kHz@ 400kHz@ 400kHz
rtos/ISRDC[%]79.176.881.078.667.1
t[µs]466922188316985
busy waitDC[%]52.87.281.769.87.4
t[µs]433893143268895
Committer:
humlet
Date:
Sun May 19 11:21:16 2013 +0000
Revision:
14:352609d395c1
Parent:
13:530968937ccb
almost beta?; ***refactored (removed mbed-NXP and mbed-src hacks/dependencies) ; *** bugs fixed; *** performance improved (read/write sequence now handled in ISR);

Who changed what in which revision?

UserRevisionLine numberNew contents of line
humlet 9:65aae53a34de 1 #include "i2cRtos_api.h"
humlet 9:65aae53a34de 2
humlet 9:65aae53a34de 3 #if DEVICE_I2C
humlet 9:65aae53a34de 4
humlet 13:530968937ccb 5 #include "us_ticker_api.h"
humlet 9:65aae53a34de 6 #include "cmsis_os.h"
humlet 9:65aae53a34de 7 #include "error.h"
humlet 9:65aae53a34de 8
humlet 14:352609d395c1 9 // little helpers cloned from official i2c api
humlet 13:530968937ccb 10 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
humlet 9:65aae53a34de 11 #define I2C_CONSET(x) (x->i2c->I2CONSET)
humlet 9:65aae53a34de 12 #define I2C_CONCLR(x) (x->i2c->I2CONCLR)
humlet 9:65aae53a34de 13 #define I2C_STAT(x) (x->i2c->I2STAT)
humlet 9:65aae53a34de 14 #define I2C_DAT(x) (x->i2c->I2DAT)
humlet 13:530968937ccb 15 #elif defined(TARGET_LPC11U24)
humlet 13:530968937ccb 16 #define I2C_CONSET(x) (x->i2c->CONSET)
humlet 13:530968937ccb 17 #define I2C_CONCLR(x) (x->i2c->CONCLR)
humlet 13:530968937ccb 18 #define I2C_STAT(x) (x->i2c->STAT)
humlet 13:530968937ccb 19 #define I2C_DAT(x) (x->i2c->DAT)
humlet 13:530968937ccb 20 #endif
humlet 9:65aae53a34de 21
humlet 14:352609d395c1 22 //#include "gpio_api.h"
humlet 14:352609d395c1 23 //static gpio_t gpio[2]; // evillive
humlet 10:e3d6c92ff222 24
humlet 14:352609d395c1 25 // isr/thread data transfer struct
humlet 9:65aae53a34de 26 enum I2cIsrCmd {
humlet 9:65aae53a34de 27 readMst,
humlet 9:65aae53a34de 28 writeMst,
humlet 9:65aae53a34de 29 readSlv,
humlet 9:65aae53a34de 30 writeSlv,
humlet 9:65aae53a34de 31 waitSI
humlet 9:65aae53a34de 32 };
humlet 9:65aae53a34de 33 struct I2cIsrTransfer {
humlet 9:65aae53a34de 34 i2c_t* obj;
humlet 9:65aae53a34de 35 enum I2cIsrCmd cmd;
humlet 9:65aae53a34de 36 int len;
humlet 9:65aae53a34de 37 int cnt;
humlet 9:65aae53a34de 38 int stat;
humlet 9:65aae53a34de 39 char* rData;
humlet 9:65aae53a34de 40 const char* wData;
humlet 9:65aae53a34de 41 };
humlet 14:352609d395c1 42 // one for each channel
humlet 14:352609d395c1 43 // "volatile" has been omitted since ISR and thread do not run simultaneously, hopefully ok
humlet 14:352609d395c1 44 static struct I2cIsrTransfer i2c_transfer[2];
humlet 9:65aae53a34de 45
humlet 14:352609d395c1 46 // struct holding IRQ and semaphore ID
humlet 14:352609d395c1 47 // needed for thread<->isr communication/activation
humlet 9:65aae53a34de 48 struct IsrIrqSem {
humlet 9:65aae53a34de 49 IRQn_Type irq;
humlet 9:65aae53a34de 50 osSemaphoreId sem;
humlet 9:65aae53a34de 51 };
humlet 14:352609d395c1 52 static struct IsrIrqSem isrIrqSem[2]; // one for each channel
humlet 9:65aae53a34de 53
humlet 9:65aae53a34de 54
humlet 9:65aae53a34de 55 // little helpers cloned from official i2c api
humlet 9:65aae53a34de 56 static inline void i2c_conclr(i2c_t *obj, int start, int stop, int interrupt, int acknowledge)
humlet 9:65aae53a34de 57 {
humlet 9:65aae53a34de 58 I2C_CONCLR(obj) = (start << 5)
humlet 9:65aae53a34de 59 | (stop << 4)
humlet 9:65aae53a34de 60 | (interrupt << 3)
humlet 9:65aae53a34de 61 | (acknowledge << 2);
humlet 9:65aae53a34de 62 }
humlet 9:65aae53a34de 63 static inline void i2c_conset(i2c_t *obj, int start, int stop, int interrupt, int acknowledge)
humlet 9:65aae53a34de 64 {
humlet 9:65aae53a34de 65 I2C_CONSET(obj) = (start << 5)
humlet 9:65aae53a34de 66 | (stop << 4)
humlet 9:65aae53a34de 67 | (interrupt << 3)
humlet 9:65aae53a34de 68 | (acknowledge << 2);
humlet 9:65aae53a34de 69 }
humlet 9:65aae53a34de 70 static inline void i2c_clear_SI(i2c_t *obj)
humlet 9:65aae53a34de 71 {
humlet 9:65aae53a34de 72 i2c_conclr(obj, 0, 0, 1, 0);
humlet 9:65aae53a34de 73 }
humlet 9:65aae53a34de 74 static inline int i2c_status(i2c_t *obj)
humlet 9:65aae53a34de 75 {
humlet 9:65aae53a34de 76 return I2C_STAT(obj);
humlet 9:65aae53a34de 77 }
humlet 9:65aae53a34de 78
humlet 14:352609d395c1 79 // ISR routines
humlet 14:352609d395c1 80 // implements the same read/write sequences as the official i2c lib
humlet 10:e3d6c92ff222 81 static void i2cRtos_isr(uint32_t ch)
humlet 9:65aae53a34de 82 {
humlet 11:8c1d44595620 83 struct I2cIsrTransfer* tr=&(i2c_transfer[ch]);
humlet 12:6ddadcbbdca2 84 if(tr->cmd==waitSI) {
humlet 14:352609d395c1 85 // just waiting for an interrupt after a byte read/write or slave receive call
humlet 9:65aae53a34de 86 osSemaphoreRelease(isrIrqSem[ch].sem);
humlet 9:65aae53a34de 87 NVIC_DisableIRQ(isrIrqSem[ch].irq);
humlet 9:65aae53a34de 88 return;
humlet 9:65aae53a34de 89 }
humlet 9:65aae53a34de 90 int stat=i2c_status(tr->obj);
humlet 9:65aae53a34de 91 int stay = 0;
humlet 9:65aae53a34de 92 switch(tr->cmd) {
humlet 11:8c1d44595620 93 case readMst:
humlet 11:8c1d44595620 94 switch(stat) {
humlet 14:352609d395c1 95 case 0x50: // Data byte has been received; ACK has been returned.
humlet 11:8c1d44595620 96 (tr->rData)[tr->cnt] = (char)(I2C_DAT(tr->obj) & 0xff);
humlet 14:352609d395c1 97 case 0x40: // SLA+R has been transmitted; ACK has been received.
humlet 11:8c1d44595620 98 ++(tr->cnt);
humlet 11:8c1d44595620 99 if(tr->cnt != tr->len-1)
humlet 10:e3d6c92ff222 100 i2c_conset(tr->obj, 0, 0, 0, 1);
humlet 11:8c1d44595620 101 else
humlet 14:352609d395c1 102 i2c_conclr(tr->obj, 0, 0, 0, 1); // do not ack the last byte read
humlet 9:65aae53a34de 103 stay = 1;
humlet 11:8c1d44595620 104 break;
humlet 14:352609d395c1 105 case 0x58: // Data byte has been received; NOT ACK has been returned.
humlet 11:8c1d44595620 106 (tr->rData)[tr->cnt] = (char)(I2C_DAT(tr->obj) & 0xff);
humlet 11:8c1d44595620 107 stat=0;
humlet 11:8c1d44595620 108 break;
humlet 9:65aae53a34de 109 }
humlet 9:65aae53a34de 110 break;
humlet 11:8c1d44595620 111 case writeMst:
humlet 11:8c1d44595620 112 switch(stat) {
humlet 14:352609d395c1 113 case 0x18: // SLA+W has been transmitted; ACK has been received.
humlet 14:352609d395c1 114 case 0x28: // SLA+W has been transmitted; NOT ACK has been received.
humlet 14:352609d395c1 115 if(++(tr->cnt)!=tr->len) {
humlet 11:8c1d44595620 116 I2C_DAT(tr->obj) = (tr->wData)[tr->cnt];
humlet 11:8c1d44595620 117 stay=1;
humlet 11:8c1d44595620 118 } else {
humlet 11:8c1d44595620 119 stat=0;
humlet 11:8c1d44595620 120 }
humlet 9:65aae53a34de 121 }
humlet 9:65aae53a34de 122 break;
humlet 11:8c1d44595620 123 case readSlv:
humlet 11:8c1d44595620 124 ++(tr->cnt);
humlet 14:352609d395c1 125 if(stat==0x80 || stat==0x90) // Previously addressed with own SLA address(0x80) or geberal call (0x90); DATA has been received; ACK has been returned.
humlet 11:8c1d44595620 126 (tr->rData)[tr->cnt] = I2C_DAT(tr->obj) & 0xFF;
humlet 11:8c1d44595620 127 stay = (stat==0x80 || stat==0x90 || stat==0x060 || stat==0x70) && (tr->cnt < tr->len-1);
humlet 14:352609d395c1 128 // 60: Own SLA+W has been received; ACK has been returned. ... SLV+W???
humlet 14:352609d395c1 129 // 70: General Call address (0x00) has been received; ACK has been returned.
humlet 9:65aae53a34de 130 break;
humlet 11:8c1d44595620 131 case writeSlv:
humlet 11:8c1d44595620 132 ++(tr->cnt);
humlet 14:352609d395c1 133 stay = tr->cnt<tr->len && stat==0xb8; // Data byte in I2DAT has been transmitted; ACK has been received.
humlet 14:352609d395c1 134 if(stay) I2C_DAT(tr->obj) = tr->wData[tr->cnt];
humlet 9:65aae53a34de 135 break;
humlet 9:65aae53a34de 136 }
humlet 9:65aae53a34de 137 if(stay) {
humlet 14:352609d395c1 138 // sequence not finished => stay in ISR mode and trigger next i2c by clearing he SI bit
humlet 9:65aae53a34de 139 i2c_clear_SI(tr->obj);
humlet 9:65aae53a34de 140 } else {
humlet 14:352609d395c1 141 // sequence finished or unexpected state has been reported
humlet 14:352609d395c1 142 // => bail out of isr mode and return last status received
humlet 9:65aae53a34de 143 tr->stat = stat;
humlet 9:65aae53a34de 144 osSemaphoreRelease(isrIrqSem[ch].sem);
humlet 9:65aae53a34de 145 NVIC_DisableIRQ(isrIrqSem[ch].irq);
humlet 9:65aae53a34de 146 }
humlet 9:65aae53a34de 147 }
humlet 13:530968937ccb 148 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
humlet 11:8c1d44595620 149 static void i2cRtos_isr_ch0()
humlet 9:65aae53a34de 150 {
humlet 11:8c1d44595620 151 //gpio_write(&gpio[0], 1);
humlet 9:65aae53a34de 152 i2cRtos_isr(0);
humlet 11:8c1d44595620 153 //gpio_write(&gpio[0], 0);
humlet 9:65aae53a34de 154 }
humlet 13:530968937ccb 155 #endif
humlet 11:8c1d44595620 156 static void i2cRtos_isr_ch1()
humlet 9:65aae53a34de 157 {
humlet 11:8c1d44595620 158 //gpio_write(&gpio[1], 1);
humlet 9:65aae53a34de 159 i2cRtos_isr(1);
humlet 11:8c1d44595620 160 //gpio_write(&gpio[1], 0);
humlet 9:65aae53a34de 161 }
humlet 9:65aae53a34de 162
humlet 9:65aae53a34de 163
humlet 9:65aae53a34de 164 // determine channel
humlet 11:8c1d44595620 165 static inline uint32_t i2c_get_channel(const i2c_t *obj)
humlet 9:65aae53a34de 166 {
humlet 9:65aae53a34de 167 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
humlet 9:65aae53a34de 168 switch((I2CName)(obj->i2c)) {
humlet 9:65aae53a34de 169 case I2C_1:
humlet 9:65aae53a34de 170 return 0;
humlet 9:65aae53a34de 171 case I2C_2:
humlet 9:65aae53a34de 172 return 1;
humlet 9:65aae53a34de 173 default:
humlet 9:65aae53a34de 174 error("Dial911 i2c_get_channel: Invaid I2CName \n");
humlet 9:65aae53a34de 175 }
humlet 9:65aae53a34de 176 #endif
humlet 10:e3d6c92ff222 177 return 1;
humlet 9:65aae53a34de 178 }
humlet 9:65aae53a34de 179
humlet 9:65aae53a34de 180
humlet 14:352609d395c1 181 // Enable IRQ and wait for ISR sequence to be finished
humlet 12:6ddadcbbdca2 182 static inline void i2cRtos_wait_and_see(i2c_t *obj, int ch, uint32_t tmOut) //evillive
humlet 9:65aae53a34de 183 {
humlet 11:8c1d44595620 184 struct IsrIrqSem* iis = &(isrIrqSem[ch]);
humlet 14:352609d395c1 185 __disable_irq(); // evil, but don't want the next three lines to be interrupted
humlet 9:65aae53a34de 186 i2c_clear_SI(obj);
humlet 11:8c1d44595620 187 NVIC_ClearPendingIRQ(iis->irq);
humlet 9:65aae53a34de 188 NVIC_EnableIRQ(iis->irq);
humlet 10:e3d6c92ff222 189 __enable_irq();
humlet 14:352609d395c1 190 if(osSemaphoreWait(iis->sem, tmOut)!=1) NVIC_DisableIRQ(iis->irq); // time out => diable the IRQ
humlet 11:8c1d44595620 191 }
humlet 11:8c1d44595620 192
humlet 14:352609d395c1 193 // just wait for a generic i2c interrupt
humlet 12:6ddadcbbdca2 194 static inline void i2cRtos_waitSI(i2c_t *obj, uint32_t tmOut)
humlet 12:6ddadcbbdca2 195 {
humlet 11:8c1d44595620 196 int ch = i2c_get_channel(obj);
humlet 11:8c1d44595620 197 i2c_transfer[ch].cmd = waitSI;
humlet 11:8c1d44595620 198 i2cRtos_wait_and_see(obj, ch, tmOut);
humlet 9:65aae53a34de 199 }
humlet 9:65aae53a34de 200
humlet 14:352609d395c1 201 // master mode read sequence
humlet 9:65aae53a34de 202 int i2cRtos_read(i2c_t *obj, int address, char *data, int length, int stop)
humlet 9:65aae53a34de 203 {
humlet 12:6ddadcbbdca2 204 //gpio_write(&gpio[1], 1);
humlet 11:8c1d44595620 205 int stat = i2c_start(obj);
humlet 11:8c1d44595620 206 if ((stat != 0x10) && (stat != 0x08)) {
humlet 14:352609d395c1 207 i2cRtos_stop(obj); // use freeze free stop if the start has failed
humlet 11:8c1d44595620 208 return stat;
humlet 9:65aae53a34de 209 }
humlet 12:6ddadcbbdca2 210 //gpio_write(&gpio[1], 0);
humlet 9:65aae53a34de 211 int ch = i2c_get_channel(obj);
humlet 14:352609d395c1 212 struct I2cIsrTransfer* tr = &(i2c_transfer[ch]);
humlet 9:65aae53a34de 213 tr->obj=obj;
humlet 9:65aae53a34de 214 tr->cmd=readMst;
humlet 9:65aae53a34de 215 tr->len=length;
humlet 9:65aae53a34de 216 tr->cnt=-1;
humlet 9:65aae53a34de 217 tr->rData=data;
humlet 14:352609d395c1 218 I2C_DAT(obj) = address | 0x01; // initiate address+R write and enter isr mode
humlet 11:8c1d44595620 219 i2cRtos_wait_and_see(obj, ch,2+(length>>2)); // timeout (2+len/4)ms
humlet 11:8c1d44595620 220 stat = tr->stat;
humlet 12:6ddadcbbdca2 221 //gpio_write(&gpio[1], 1);
humlet 14:352609d395c1 222 if(stat || stop) i2cRtos_stop(obj);
humlet 12:6ddadcbbdca2 223 //gpio_write(&gpio[1], 0);
humlet 11:8c1d44595620 224 return stat;
humlet 9:65aae53a34de 225 }
humlet 9:65aae53a34de 226
humlet 14:352609d395c1 227 // master mode write sequence
humlet 9:65aae53a34de 228 int i2cRtos_write(i2c_t *obj, int address, const char *data, int length, int stop)
humlet 9:65aae53a34de 229 {
humlet 12:6ddadcbbdca2 230 //gpio_write(&gpio[1], 1);
humlet 9:65aae53a34de 231 int status = i2c_start(obj);
humlet 9:65aae53a34de 232 if ((status != 0x10) && (status != 0x08)) {
humlet 14:352609d395c1 233 i2cRtos_stop(obj); // use freeze free stop if the start has failed
humlet 9:65aae53a34de 234 return status;
humlet 9:65aae53a34de 235 }
humlet 12:6ddadcbbdca2 236 //gpio_write(&gpio[1], 0);
humlet 9:65aae53a34de 237 int ch = i2c_get_channel(obj);
humlet 12:6ddadcbbdca2 238 struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
humlet 12:6ddadcbbdca2 239 tr->obj = obj;
humlet 12:6ddadcbbdca2 240 tr->cmd = writeMst;
humlet 12:6ddadcbbdca2 241 tr->len = length;
humlet 12:6ddadcbbdca2 242 tr->cnt = -1;
humlet 12:6ddadcbbdca2 243 tr->wData = data;
humlet 14:352609d395c1 244 I2C_DAT(obj) = address & 0xfe; // initiate address+W write and enter isr mode
humlet 11:8c1d44595620 245 i2cRtos_wait_and_see(obj, ch, 2+(length>>2)); // timeout (2+len/4)ms
humlet 12:6ddadcbbdca2 246 //i2c_clear_SI(obj); // ... why? Also in official lib ... I guess this is the "write instead of start" bug
humlet 9:65aae53a34de 247 status = tr->stat;
humlet 12:6ddadcbbdca2 248 //gpio_write(&gpio[1], 1);
humlet 14:352609d395c1 249 if(status || stop) i2cRtos_stop(obj);
humlet 12:6ddadcbbdca2 250 //gpio_write(&gpio[1], 0);
humlet 9:65aae53a34de 251 return status;
humlet 9:65aae53a34de 252 }
humlet 9:65aae53a34de 253
humlet 14:352609d395c1 254 // read single byte from bus (master/slave)
humlet 9:65aae53a34de 255 int i2cRtos_byte_read(i2c_t *obj, int last)
humlet 9:65aae53a34de 256 {
humlet 9:65aae53a34de 257 if(last) {
humlet 9:65aae53a34de 258 i2c_conclr(obj, 0, 0, 0, 1); // send a NOT ACK
humlet 9:65aae53a34de 259 } else {
humlet 9:65aae53a34de 260 i2c_conset(obj, 0, 0, 0, 1); // send a ACK
humlet 9:65aae53a34de 261 }
humlet 11:8c1d44595620 262 i2cRtos_waitSI(obj, 2);
humlet 9:65aae53a34de 263 return (I2C_DAT(obj) & 0xff);
humlet 9:65aae53a34de 264 }
humlet 9:65aae53a34de 265
humlet 14:352609d395c1 266 // write single byte to bus (master/slave)
humlet 9:65aae53a34de 267 int i2cRtos_byte_write(i2c_t *obj, int data)
humlet 9:65aae53a34de 268 {
humlet 9:65aae53a34de 269 I2C_DAT(obj) = (data & 0xff);
humlet 11:8c1d44595620 270 i2cRtos_waitSI(obj, 2);
humlet 9:65aae53a34de 271 int stat=i2c_status(obj);
humlet 9:65aae53a34de 272 return (stat==0x18 || stat==0x28 || stat==0x40 || stat==0xb8);
humlet 9:65aae53a34de 273 }
humlet 9:65aae53a34de 274
humlet 14:352609d395c1 275 // freeze free i2c stop
humlet 14:352609d395c1 276 // the busy wait without timeout of the official i2c lib
humlet 14:352609d395c1 277 // might freeze the mbed if someone on the bus keeps the clock down
humlet 14:352609d395c1 278 // and prevents LPC's i2c controller to create a stop condition
humlet 14:352609d395c1 279 int i2cRtos_stop(i2c_t *obj)
humlet 14:352609d395c1 280 {
humlet 13:530968937ccb 281 i2c_conset(obj, 0, 1, 0, 0);
humlet 13:530968937ccb 282 i2c_clear_SI(obj);
humlet 13:530968937ccb 283 uint32_t t0=us_ticker_read();
humlet 13:530968937ccb 284 uint32_t dt=0;
humlet 14:352609d395c1 285 while((I2C_CONSET(obj) & (1 << 4)) && dt<23) {
humlet 13:530968937ccb 286 dt = us_ticker_read() - t0;
humlet 13:530968937ccb 287 }
humlet 13:530968937ccb 288 return dt<23;
humlet 13:530968937ccb 289 }
humlet 13:530968937ccb 290
humlet 13:530968937ccb 291
humlet 9:65aae53a34de 292 #if DEVICE_I2CSLAVE
humlet 9:65aae53a34de 293
humlet 14:352609d395c1 294 // determine slave's receive mode. Blocking with timeout
humlet 9:65aae53a34de 295 int i2cRtos_slave_receive(i2c_t *obj, uint32_t tmOut)
humlet 9:65aae53a34de 296 {
humlet 9:65aae53a34de 297 int retval = i2c_slave_receive(obj);
humlet 9:65aae53a34de 298 //check for pending requests
humlet 9:65aae53a34de 299 if(retval)return retval; // there is one => bail out
humlet 14:352609d395c1 300 // No request? Wait for it! ... with time out
humlet 11:8c1d44595620 301 i2cRtos_waitSI(obj, tmOut);
humlet 9:65aae53a34de 302 // check again for pending requests
humlet 9:65aae53a34de 303 return i2c_slave_receive(obj);
humlet 9:65aae53a34de 304 }
humlet 9:65aae53a34de 305
humlet 14:352609d395c1 306 // slave mode read sequence
humlet 9:65aae53a34de 307 int i2cRtos_slave_read(i2c_t *obj, char *data, int length)
humlet 9:65aae53a34de 308 {
humlet 9:65aae53a34de 309 int ch = i2c_get_channel(obj);
humlet 12:6ddadcbbdca2 310 struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
humlet 9:65aae53a34de 311 tr->obj=obj;
humlet 9:65aae53a34de 312 tr->cmd=readSlv;
humlet 9:65aae53a34de 313 tr->len=length;
humlet 9:65aae53a34de 314 tr->cnt=-1;
humlet 9:65aae53a34de 315 tr->rData=data;
humlet 10:e3d6c92ff222 316 i2cRtos_wait_and_see(obj, ch, 2+(length>>2)); // timeout (1+len/4)ms
humlet 9:65aae53a34de 317 if(tr->stat != 0xa0) {
humlet 14:352609d395c1 318 i2cRtos_stop(obj);
humlet 9:65aae53a34de 319 }
humlet 14:352609d395c1 320 i2c_clear_SI(obj); // stop keeping scl low
humlet 9:65aae53a34de 321 return tr->cnt; // same weird return as in official lib
humlet 9:65aae53a34de 322 }
humlet 9:65aae53a34de 323
humlet 14:352609d395c1 324 // slave mode write sequence
humlet 9:65aae53a34de 325 int i2cRtos_slave_write(i2c_t *obj, const char *data, int length)
humlet 9:65aae53a34de 326 {
humlet 9:65aae53a34de 327 if(length <= 0) {
humlet 9:65aae53a34de 328 return(0);
humlet 9:65aae53a34de 329 }
humlet 9:65aae53a34de 330 int ch = i2c_get_channel(obj);
humlet 12:6ddadcbbdca2 331 struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem?
humlet 9:65aae53a34de 332 tr->obj=obj;
humlet 9:65aae53a34de 333 tr->cmd=writeSlv;
humlet 9:65aae53a34de 334 tr->len=length;
humlet 9:65aae53a34de 335 tr->cnt=0;
humlet 9:65aae53a34de 336 tr->wData=data;
humlet 9:65aae53a34de 337 I2C_DAT(obj) = data[0];
humlet 11:8c1d44595620 338 i2cRtos_wait_and_see(obj, ch, 2+(length>>2)); // timeout (1+len/4)ms
humlet 9:65aae53a34de 339 int status = tr->stat;
humlet 9:65aae53a34de 340 if(status!=0xC0 && status!=0xC8) {
humlet 14:352609d395c1 341 i2cRtos_stop(obj);
humlet 9:65aae53a34de 342 }
humlet 14:352609d395c1 343 i2c_clear_SI(obj); // stops keeping scl low
humlet 9:65aae53a34de 344 return tr->cnt;
humlet 9:65aae53a34de 345 }
humlet 14:352609d395c1 346 #endif
humlet 11:8c1d44595620 347
humlet 11:8c1d44595620 348 // setup semaphores and hook in ISRs
humlet 11:8c1d44595620 349 void i2cRtos_init(i2c_t *obj, PinName sda, PinName scl)
humlet 11:8c1d44595620 350 {
humlet 14:352609d395c1 351 /*static int called=0;
humlet 11:8c1d44595620 352 if(!called) {
humlet 11:8c1d44595620 353 gpio_init(&gpio[0], p15, PIN_OUTPUT);
humlet 11:8c1d44595620 354 gpio_init(&gpio[1], p16, PIN_OUTPUT);
humlet 11:8c1d44595620 355 }
humlet 11:8c1d44595620 356 called = 1;
humlet 11:8c1d44595620 357 gpio_write(&gpio[0], 0);
humlet 14:352609d395c1 358 gpio_write(&gpio[1], 0);*/
humlet 13:530968937ccb 359
humlet 11:8c1d44595620 360 i2c_init(obj,sda,scl);
humlet 11:8c1d44595620 361 uint32_t ch = i2c_get_channel(obj);
humlet 11:8c1d44595620 362 #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368)
humlet 11:8c1d44595620 363 static osSemaphoreDef(i2cIsrDrvSem_ch0);
humlet 11:8c1d44595620 364 static osSemaphoreDef(i2cIsrDrvSem_ch1);
humlet 11:8c1d44595620 365 switch(ch) {
humlet 11:8c1d44595620 366 case 0:
humlet 11:8c1d44595620 367 isrIrqSem[ch].irq = I2C1_IRQn;
humlet 11:8c1d44595620 368 NVIC_SetVector(I2C1_IRQn, (uint32_t)i2cRtos_isr_ch0);
humlet 11:8c1d44595620 369 isrIrqSem[ch].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch0), 1);
humlet 11:8c1d44595620 370 break;
humlet 11:8c1d44595620 371 case 1:
humlet 11:8c1d44595620 372 isrIrqSem[ch].irq = I2C2_IRQn;
humlet 11:8c1d44595620 373 NVIC_SetVector(I2C2_IRQn, (uint32_t)i2cRtos_isr_ch1);
humlet 11:8c1d44595620 374 isrIrqSem[ch].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch1), 1);
humlet 11:8c1d44595620 375 break;
humlet 11:8c1d44595620 376 }
humlet 11:8c1d44595620 377 osSemaphoreWait(isrIrqSem[ch].sem, osWaitForever);
humlet 11:8c1d44595620 378 #elif defined(TARGET_LPC11U24)
humlet 11:8c1d44595620 379 static osSemaphoreDef(i2cIsrDrvSem_ch1);
humlet 11:8c1d44595620 380 isrIrqSem[ch].irq = I2C_IRQn;
humlet 11:8c1d44595620 381 NVIC_SetVector(I2C_IRQn, (uint32_t)i2cRtos_isr_ch1);
humlet 11:8c1d44595620 382 isrIrqSem[ch].sem = osSemaphoreCreate(osSemaphore(i2cIsrDrvSem_ch1), 1);
humlet 11:8c1d44595620 383 osSemaphoreWait(isrIrqSem[ch].sem, osWaitForever);
humlet 11:8c1d44595620 384 #else
humlet 11:8c1d44595620 385 #error "Dial911 i2cRtos_init: Unsupported HW"
humlet 11:8c1d44595620 386 #endif
humlet 11:8c1d44595620 387 }
humlet 9:65aae53a34de 388 #endif