I2CRTOS Driver by Helmut Schmücker. Removed included mbed-rtos library to prevent multiple definition. Make sure to include mbed-rtos library in your program!
Fork of I2cRtosDriver by
Revision 14:352609d395c1, committed 2013-05-19
- Comitter:
- humlet
- Date:
- Sun May 19 11:21:16 2013 +0000
- Parent:
- 13:530968937ccb
- Child:
- 15:6292f189a9e9
- Commit message:
- almost beta?; ***refactored (removed mbed-NXP and mbed-src hacks/dependencies) ; *** bugs fixed; *** performance improved (read/write sequence now handled in ISR);
Changed in this revision
--- a/I2CDriver.cpp Fri May 10 20:38:35 2013 +0000 +++ b/I2CDriver.cpp Sun May 19 11:21:16 2013 +0000 @@ -1,11 +1,15 @@ #include "I2CDriver.h" #include "i2cRtos_api.h" +//#include "rt_System.h" #include "error.h" using namespace mbed; using namespace rtos; -#define PREFIX i2c +//DigitalOut I2CDriver::osci2(p7); + +#define PREFIX i2cRtos +//#define PREFIX i2c // fallback to offical busy wait i2c c-api for performance testing #define PASTER(x,y) x ## _ ## y #define EVALUATOR(x,y) PASTER(x,y) #define FUNCTION(fun) EVALUATOR(PREFIX, fun) @@ -17,8 +21,14 @@ I2CDriver::I2CDriver(PinName sda, PinName scl, int hz, int slaveAdr):m_freq(hz),m_slaveAdr(slaveAdr) { - static Mutex mutex; - mutex.lock(); + // ensure exclusive access for initialization + static Mutex mtx; + bool locked = false; + if(osKernelRunning()) { // but don't try to lock if rtos kernel is not running yet. (global/static definition) + mtx.lock(); + locked = true; + } + // check pins and determine i2c channel int channel=0; #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) @@ -27,41 +37,55 @@ #endif if (sda==c_sdas[1] && scl==c_scls[1]) channel=1; //I2C_2 or I2C else error("I2CDriver: Invalid I2C pins selected\n"); - + + // initialize the selected i2c channel if(s_channels[channel]==0) { s_channels[channel] = new I2CDriver::Channel; m_channel = s_channels[channel]; m_channel->freq = 0; m_channel->slaveAdr = 0; m_channel->modeSlave = 0; - FUNCTION(init)(&m_channel->i2c, c_sdas[channel], c_scls[channel]); + m_channel->initialized=false; // defer i2c initialization util we are sure the rtos kernel is running (config() function) } m_channel = s_channels[channel]; - mutex.unlock(); + if(locked) mtx.unlock(); } void I2CDriver::lock() { + //osci2.write(1); // One and the same thread can lock twice, but then it needs also to unlock twice. // exactly what we need here - m_callerID = osThreadGetId(); - m_callerPrio = osThreadGetPriority(m_callerID); m_channel->mutex.lock(osWaitForever); + m_channel->callerID = osThreadGetId(); + m_channel->callerPrio = osThreadGetPriority(m_channel->callerID); // maximize thread prio - osThreadSetPriority(m_callerID, c_drvPrio); // hopefully not interrupted since the lock in the line above + osThreadSetPriority(m_channel->callerID, c_drvPrio); // hopefully not interrupted since the lock in the line above // mutex code looks like that waiting threads are priority ordered // also priority inheritance seems to be provided + //osci2.write(0); } void I2CDriver::unlock() { + //osci2.write(1); // free the mutex and restore original prio - m_channel->mutex.unlock(); - osThreadSetPriority(m_callerID, m_callerPrio); + //rt_tsk_lock(); // just prevent beeing preempted after restoring prio before freeing the mutex + osThreadSetPriority(m_channel->callerID, m_channel->callerPrio); + m_channel->mutex.unlock(); + //rt_tsk_unlock(); + //osci2.write(0); } void I2CDriver::config() { + //osci2.write(1); + // check and initialize driver + if(!m_channel->initialized) { + int channel = m_channel==s_channels[0] ? 0 : 1; // ...ugly + FUNCTION(init)(&m_channel->i2c, c_sdas[channel], c_scls[channel]); + m_channel->initialized=true; + } // check and update frequency if(m_freq != m_channel->freq) { m_channel->freq = m_freq; @@ -77,6 +101,7 @@ m_channel->slaveAdr = m_slaveAdr; i2c_slave_address(&m_channel->i2c, 0, m_slaveAdr, 0); } + //osci2.write(0); } int I2CDriver::readMaster(int address, char *data, int length, bool repeated)
--- a/I2CDriver.h Fri May 10 20:38:35 2013 +0000 +++ b/I2CDriver.h Sun May 19 11:21:16 2013 +0000 @@ -2,10 +2,11 @@ #define I2CDRIVER_H #include "stdint.h" +#include "I2C.h" +#include "Mutex.h" -#include "I2C.h" +#include "DigitalOut.h" -#include "Mutex.h" namespace mbed { @@ -14,6 +15,7 @@ class I2CDriver { public: + //static DigitalOut osci2; /// Status returned by the receiveSlave() function enum SlaveRxStatus { NoData = 0, @@ -154,7 +156,7 @@ * * @returns * 0 on success, - * non-0 otherwise + * non-0 otherwise */ int writeSlave(const char *data, int length); @@ -176,8 +178,8 @@ void stopSlave(void); /// Creates a stop condition on the I2C bus - /// If unsccessful because someone on the bus holds the scl line down it returns "false" after 23µs - /// In normal operation the stop shouldn't take longer than 12µs @ 100kHz and 3-4µs @ 400kHz. + /// If unsccessful because someone on the bus holds the scl line down it returns "false" after 23µs + /// In normal operation the stop shouldn't take longer than 12µs @ 100kHz and 3-4µs @ 400kHz. bool stopMaster(void); /// Wait until the i2c driver becomes available. @@ -204,18 +206,16 @@ int freq; int slaveAdr; bool modeSlave; + bool initialized; + osThreadId callerID; + osPriority callerPrio; }; - // curren i2c configuration of this driver interface + // current i2c configuration of this driver interface int m_freq; int m_slaveAdr; bool m_modeSlave; - // id and prio of current caller thread - osThreadId m_callerID; - osPriority m_callerPrio; - - // i2c driver prio static const osPriority c_drvPrio = osPriorityRealtime; // the pin names fo the i2c channels
--- a/I2CDriverTest01.h Fri May 10 20:38:35 2013 +0000 +++ b/I2CDriverTest01.h Sun May 19 11:21:16 2013 +0000 @@ -1,52 +1,41 @@ +// A high prio thread reads at a rate of 1kHz from a SRF08 ultrasonic ranger +// data packets of different size, whereas the lower prio main thread measures the CPU time left. + #include "mbed.h" #include "rtos.h" #include "I2CMasterRtos.h" #include "stdint.h" -#include "DigitalOut.h" volatile int g_disco=0; -volatile int g_len=0; +volatile int g_len=2; volatile int g_freq=100000; -volatile osThreadId i2cDrvThrdID; -DigitalOut osci(p12); +I2CMasterRtos i2c(p28, p27); +const uint32_t adr = 0x70<<1; void highPrioCallBck(void const *args) { - osSignalSet(i2cDrvThrdID, 1<<5); -} - -void highPrioThreadFun(void const *args) -{ - i2cDrvThrdID = Thread::gettid(); - I2CMasterRtos i2c(p28, p27); - const uint32_t adr = 0x70<<1; - - // trigger on srf08 ranging - const char regNcmd[2]= {0x00,0x51}; - i2c.write(adr, regNcmd, 2); - osDelay(200); - while(true) { - i2c.frequency(g_freq); - Thread::signal_wait(1<<5,osWaitForever); - // read back srf08 echo times (1+16 words) - const char reg= 0x02; - char result[64]; - uint32_t t1=us_ticker_read(); - i2c.read(adr, reg, result, g_len); - uint32_t dt=us_ticker_read()-t1; - uint16_t tm=((static_cast<uint16_t>(result[0])<<8)|static_cast<uint16_t>(result[1])); - - if(--g_disco>0) printf("tm=%4dus dt=%4dus\n",tm,dt); - } + i2c.frequency(g_freq); + // read back srf08 echo times (1+16 words) + const char reg= 0x02; + char result[64]; + uint32_t t1=us_ticker_read(); + i2c.read(adr, reg, result, g_len); + uint32_t dt=us_ticker_read()-t1; + uint16_t dst=((static_cast<uint16_t>(result[0])<<8)|static_cast<uint16_t>(result[1])); + if(--g_disco>0) printf("dst=%4dcm dt=%4dus\n",dst,dt); } int doit() { - Thread highPrioThread(highPrioThreadFun,0,osPriorityAboveNormal); RtosTimer highPrioTicker(highPrioCallBck, osTimerPeriodic, (void *)0); - Thread::wait(100); + Thread::wait(500); + // trigger srf08 ranging + const char regNcmd[2]= {0x00,0x54}; + i2c.write(adr, regNcmd, 2); + osDelay(200); + highPrioTicker.start(1); #if defined(TARGET_LPC1768) @@ -56,7 +45,7 @@ #elif defined(TARGET_LPC11U24) const int nTest=5; const int freq[nTest]= {1e5, 1e5, 4e5, 4e5, 4e5 }; - const int len[nTest]= {1, 4, 1, 6, 16}; + const int len[nTest]= {1, 6, 1, 6, 23}; #endif for(int i=0; i<nTest; ++i) { g_freq = freq[i]; @@ -70,14 +59,16 @@ uint32_t tMe=0; do { tAct=us_ticker_read(); - if(tAct>tLast) { - if(tAct==tLast+1)++tMe; - osci.write(tAct&1); - } + #if defined(TARGET_LPC1768) + if(tAct==tLast+1)++tMe; + #elif defined(TARGET_LPC11U24) + uint32_t delta = tAct-tLast; + if(delta<=2)tMe+=delta; // on the 11U24 this loop takes a bit longer than 1µs (ISR ~3µs, task switch ~8µs) + #endif tLast=tAct; } while(tAct-tStart<dt); printf("dc=%5.2f \n", 100.0*(float)tMe/dt); - g_disco=10; + g_disco=5; while(g_disco>0); } return 0;
--- a/I2CDriverTest02.h Fri May 10 20:38:35 2013 +0000 +++ b/I2CDriverTest02.h Sun May 19 11:21:16 2013 +0000 @@ -1,3 +1,6 @@ +// exchange messages betwen the LPC1768's two i2c ports using high level reead/write commands +// changing master and slave mode on the fly + #include "mbed.h" #include "rtos.h" #include "I2CMasterRtos.h"
--- a/I2CDriverTest03.h Fri May 10 20:38:35 2013 +0000 +++ b/I2CDriverTest03.h Sun May 19 11:21:16 2013 +0000 @@ -1,3 +1,6 @@ +// exchange messages betwen the LPC1768's two i2c ports using low level read/write/start/stop commands +// changing master and slave mode on the fly + #include "mbed.h" #include "rtos.h" #include "I2CMasterRtos.h"
--- a/I2CDriverTest04.h Fri May 10 20:38:35 2013 +0000 +++ b/I2CDriverTest04.h Sun May 19 11:21:16 2013 +0000 @@ -1,65 +1,57 @@ +// A high prio thread reads at a rate of 1kHz from a MPU6050 gyro/acc meter's FIFO +// data packets of different size, whereas the lower prio ain thread the CPU time left. + #include "mbed.h" #include "rtos.h" #include "I2CMasterRtos.h" #include "stdint.h" -//DigitalOut osci(p16); +//#include "DigitalOut.h" +//DigitalOut osci(p8); volatile int g_disco=0; volatile int g_len=0; volatile int g_freq=100000; const uint32_t i2cAdr = 0x68<<1; -volatile osThreadId i2cDrvThrdID; static void config(I2CMasterRtos& i2c); +I2CMasterRtos i2c(p28, p27); + void highPrioCallBck(void const *args) { - osSignalSet(i2cDrvThrdID, 1<<5); -} - -void highPrioThreadFun(void const *args) -{ - //printf("tst01\n"); - i2cDrvThrdID = Thread::gettid(); - I2CMasterRtos i2c(p28, p27); - //printf("tst02\n"); - config(i2c); + //I2CDriver::osci2.write(0); + const char reg= 0x74; + static char result[64]; + //I2CDriver::osci2.write(1); + // read from MPU600's fifo + i2c.frequency(g_freq); + uint32_t t1=us_ticker_read(); + //I2CDriver::osci2.write(0); + int stat = i2c.read(i2cAdr, reg, result, g_len); + uint32_t dt=us_ticker_read()-t1; + if(stat!=0) { + printf("\n%x %d %d %d\n",stat,g_freq,g_len,dt); + exit(0); + } + int16_t val=((static_cast<int16_t>(result[0])<<8)|static_cast<int16_t>(result[1])); - const char reg= 0x3a; - char result[64]; - while(true) { - //osci.write(0); - i2c.frequency(g_freq); - Thread::signal_wait(1<<5,osWaitForever); - // read from MPU600's fifo - - uint32_t t1=us_ticker_read(); - int stat = i2c.read(i2cAdr, reg, result, g_len); - uint32_t dt=us_ticker_read()-t1; - if(stat!=0) { - //osci.write(1); - printf("\n%x %d %d %d\n",stat,g_freq,g_len,dt); - exit(0); - } - uint16_t tm=((static_cast<uint16_t>(result[0])<<8)|static_cast<uint16_t>(result[1])); - - if(--g_disco>0) printf("tm=%8d dt=%4dus\n",tm,dt); - } + if(--g_disco>0)printf("val=%8d dt=%4dus\n",val,dt); } int doit() { - Thread highPrioThread(highPrioThreadFun,0,osPriorityAboveNormal); + config(i2c); + RtosTimer highPrioTicker(highPrioCallBck, osTimerPeriodic, (void *)0); - Thread::wait(2000); + Thread::wait(500); highPrioTicker.start(1); #if defined(TARGET_LPC1768) const int nTest=7; const int freq[nTest]= {1e5, 1e5, 1e5, 4e5, 4e5, 4e5, 4e5}; - const int len[nTest]= {1, 4, 6, 1, 6, 12, 36}; + const int len[nTest]= {1, 4, 7, 1, 6, 12, 36}; #elif defined(TARGET_LPC11U24) const int nTest=5; const int freq[nTest]= {1e5, 1e5, 4e5, 4e5, 4e5 }; @@ -70,21 +62,27 @@ g_len = len[i]; printf("f=%d l=%d\n",g_freq,g_len); Thread::wait(500); + //highPrioTicker.start(1); const uint32_t dt=1e6; uint32_t tStart = us_ticker_read(); uint32_t tLast = tStart; uint32_t tAct = tStart; uint32_t tMe=0; - do { + do { // loop an count consecutive µs ticker edges + //osci.write(!osci.read()); tAct=us_ticker_read(); - if(tAct>tLast) { - if(tAct==tLast+1)++tMe; - //tLast=tAct; - } + #if defined(TARGET_LPC1768) + if(tAct==tLast+1)++tMe; + #elif defined(TARGET_LPC11U24) + uint32_t delta = tAct-tLast; + if(delta<=2)tMe+=delta; // on the 11U24 this loop takes a bit longer than 1µs (ISR ~3µs, task switch ~8µs) + #endif tLast=tAct; } while(tAct-tStart<dt); + //highPrioTicker.stop(); + // and calculate the duty cycle from this measurement printf("dc=%5.2f \n", 100.0*(float)tMe/dt); - g_disco=10; + g_disco=5; while(g_disco>0); } return 0;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/I2CDriverTest05.h Sun May 19 11:21:16 2013 +0000 @@ -0,0 +1,110 @@ +// several threads try to read concurrently from a MPU6050 gyro/acc meter +// via the same globally defined i2c driver interface + +#include "mbed.h" +#include "rtos.h" +#include "I2CMasterRtos.h" +#include "stdint.h" + +const uint32_t i2cAdr = 0x68<<1; +const char reg= 0x3b; // accelerometer x,y,z +volatile osThreadId i2cDrvThrdID[2]; + +I2CMasterRtos g_i2c(p28, p27, 400000); + +static void config(I2CMasterRtos& i2c); + +void highPrioCallBck(void const *args) +{ + osSignalSet(i2cDrvThrdID[1], 1<<5); + osSignalSet(i2cDrvThrdID[0], 1<<5); +} + +void highPrioThreadFun(void const *args) +{ + int thrdID = (int)args; + i2cDrvThrdID[thrdID] = Thread::gettid(); + + char result[64]; + while(true) { + Thread::signal_wait(1<<5,osWaitForever); + g_i2c.lock(); + g_i2c.read(i2cAdr, reg, result, 3*2); + printf("%s prio thread has read from MPU650:", (thrdID==0?"high ":"even higher")); + for(int i=0; i<3; i++) { + int16_t acc=((static_cast<int16_t>(result[i*2])<<8)|static_cast<int16_t>(result[i*2+1])); + printf("%7i",acc); + } + printf("\n"); + g_i2c.unlock(); + } +} + +int doit() +{ + I2CMasterRtos i2c(p28, p27, 100000); + config(i2c); + + Thread highPrioThread(highPrioThreadFun, 0, osPriorityAboveNormal); + Thread evenHigherPrioThread(highPrioThreadFun, (void*)1, osPriorityHigh); + RtosTimer highPrioTicker(highPrioCallBck, osTimerPeriodic, (void *)0); + + Thread::wait(1000); + highPrioTicker.start(503); + + char result[64]; + for(int i=0; i<100; ++i) { + i2c.read(i2cAdr, reg, result, 3*2); + printf("normal prio thread has read from MPU650:"); + for(int i=0; i<3; i++) { + int16_t acc=((static_cast<int16_t>(result[i*2])<<8)|static_cast<int16_t>(result[i*2+1])); + printf("%7i",acc); + } + printf("\n"); + Thread::wait(100); + } + return 0; +} + +void readModWrite(I2CMasterRtos& i2c, uint8_t reg, uint8_t dta) +{ + char rd1; + int rStat1 = i2c.read(i2cAdr, reg, &rd1,1); + char data[2]; + data[0]=(char)reg; + data[1]=(char)dta; + char rd2; + int wStat = i2c.write(i2cAdr, data, 2); + osDelay(100); + int rStat2 = i2c.read(i2cAdr, reg, &rd2,1); + printf("(%3x%3x%3x) %2x <- %2x => %2x -> %2x \n", rStat1, wStat, rStat2, reg, dta, rd1, rd2); +} + +static void config(I2CMasterRtos& i2c) +{ + uint8_t ncfg=32; + uint8_t regs[ncfg]; + uint8_t vals[ncfg]; + int cnt=0; + regs[cnt]=0x6b; + vals[cnt++]=(1<<7); // pwr 1 reg //: device reset + regs[cnt]=0x6b; + vals[cnt++]=1; // pwr 1 reg // clock from x gyro all pwr sav modes off + regs[cnt]=0x19; + vals[cnt++]=0; // sample rate divider reg // sapmle rate = gyro rate / (1+x) + regs[cnt]=0x1a; + vals[cnt++]=0x01;// conf reg // no ext frame sync / no dig low pass set to 1 => 1kHz Sampling + regs[cnt]=0x1b; + vals[cnt++]=0;// gyro conf reg // no test mode and gyro range 250°/s + regs[cnt]=0x1c; + vals[cnt++]=0;// accl conf reg // no test mode and accl range 2g + regs[cnt]=0x23; + //vals[cnt++]=0x1f<<3;// fifo conf reg // accl + all gyro -> fifo + //regs[cnt]=0x6a; + //vals[cnt++]=(1<<2); // pwr 1 reg // fifo reset + //regs[cnt]=0x6a; + //vals[cnt++]=(1<<6); // pwr 1 reg // fifo on + + for(int i=0; i<cnt; i++) + readModWrite(i2c, regs[i], vals[i]); +} \ No newline at end of file
--- a/i2cRtos_api.c Fri May 10 20:38:35 2013 +0000 +++ b/i2cRtos_api.c Sun May 19 11:21:16 2013 +0000 @@ -6,6 +6,7 @@ #include "cmsis_os.h" #include "error.h" +// little helpers cloned from official i2c api #if defined(TARGET_LPC1768) || defined(TARGET_LPC2368) #define I2C_CONSET(x) (x->i2c->I2CONSET) #define I2C_CONCLR(x) (x->i2c->I2CONCLR) @@ -18,9 +19,10 @@ #define I2C_DAT(x) (x->i2c->DAT) #endif -#include "gpio_api.h" -static gpio_t gpio[2]; // evillive +//#include "gpio_api.h" +//static gpio_t gpio[2]; // evillive +// isr/thread data transfer struct enum I2cIsrCmd { readMst, writeMst, @@ -37,14 +39,17 @@ char* rData; const char* wData; }; -static struct I2cIsrTransfer i2c_transfer[2]; // evillive: dare to get rid of volatile? +// one for each channel +// "volatile" has been omitted since ISR and thread do not run simultaneously, hopefully ok +static struct I2cIsrTransfer i2c_transfer[2]; - +// struct holding IRQ and semaphore ID +// needed for thread<->isr communication/activation struct IsrIrqSem { IRQn_Type irq; osSemaphoreId sem; }; -static struct IsrIrqSem isrIrqSem[2]; +static struct IsrIrqSem isrIrqSem[2]; // one for each channel // little helpers cloned from official i2c api @@ -71,11 +76,13 @@ return I2C_STAT(obj); } -// ISR stuff +// ISR routines +// implements the same read/write sequences as the official i2c lib static void i2cRtos_isr(uint32_t ch) { struct I2cIsrTransfer* tr=&(i2c_transfer[ch]); if(tr->cmd==waitSI) { + // just waiting for an interrupt after a byte read/write or slave receive call osSemaphoreRelease(isrIrqSem[ch].sem); NVIC_DisableIRQ(isrIrqSem[ch].irq); return; @@ -85,17 +92,17 @@ switch(tr->cmd) { case readMst: switch(stat) { - case 0x50: + case 0x50: // Data byte has been received; ACK has been returned. (tr->rData)[tr->cnt] = (char)(I2C_DAT(tr->obj) & 0xff); - case 0x40: + case 0x40: // SLA+R has been transmitted; ACK has been received. ++(tr->cnt); if(tr->cnt != tr->len-1) i2c_conset(tr->obj, 0, 0, 0, 1); else - i2c_conclr(tr->obj, 0, 0, 0, 1); + i2c_conclr(tr->obj, 0, 0, 0, 1); // do not ack the last byte read stay = 1; break; - case 0x58: + case 0x58: // Data byte has been received; NOT ACK has been returned. (tr->rData)[tr->cnt] = (char)(I2C_DAT(tr->obj) & 0xff); stat=0; break; @@ -103,9 +110,9 @@ break; case writeMst: switch(stat) { - case 0x18: - case 0x28: - if(++(tr->cnt)!=tr->len) { // evillive + case 0x18: // SLA+W has been transmitted; ACK has been received. + case 0x28: // SLA+W has been transmitted; NOT ACK has been received. + if(++(tr->cnt)!=tr->len) { I2C_DAT(tr->obj) = (tr->wData)[tr->cnt]; stay=1; } else { @@ -115,20 +122,24 @@ break; case readSlv: ++(tr->cnt); - if(stat==0x80 || stat==0x90) + if(stat==0x80 || stat==0x90) // Previously addressed with own SLA address(0x80) or geberal call (0x90); DATA has been received; ACK has been returned. (tr->rData)[tr->cnt] = I2C_DAT(tr->obj) & 0xFF; stay = (stat==0x80 || stat==0x90 || stat==0x060 || stat==0x70) && (tr->cnt < tr->len-1); + // 60: Own SLA+W has been received; ACK has been returned. ... SLV+W??? + // 70: General Call address (0x00) has been received; ACK has been returned. break; case writeSlv: ++(tr->cnt); - stay = tr->cnt<tr->len && stat==0xb8; - if(stay) - I2C_DAT(tr->obj) = tr->wData[tr->cnt]; + stay = tr->cnt<tr->len && stat==0xb8; // Data byte in I2DAT has been transmitted; ACK has been received. + if(stay) I2C_DAT(tr->obj) = tr->wData[tr->cnt]; break; } if(stay) { + // sequence not finished => stay in ISR mode and trigger next i2c by clearing he SI bit i2c_clear_SI(tr->obj); } else { + // sequence finished or unexpected state has been reported + // => bail out of isr mode and return last status received tr->stat = stat; osSemaphoreRelease(isrIrqSem[ch].sem); NVIC_DisableIRQ(isrIrqSem[ch].irq); @@ -167,18 +178,19 @@ } -// wait for ISR finished +// Enable IRQ and wait for ISR sequence to be finished static inline void i2cRtos_wait_and_see(i2c_t *obj, int ch, uint32_t tmOut) //evillive { struct IsrIrqSem* iis = &(isrIrqSem[ch]); - __disable_irq(); + __disable_irq(); // evil, but don't want the next three lines to be interrupted i2c_clear_SI(obj); NVIC_ClearPendingIRQ(iis->irq); NVIC_EnableIRQ(iis->irq); __enable_irq(); - if(osSemaphoreWait(iis->sem, tmOut)!=1) NVIC_DisableIRQ(iis->irq); + if(osSemaphoreWait(iis->sem, tmOut)!=1) NVIC_DisableIRQ(iis->irq); // time out => diable the IRQ } +// just wait for a generic i2c interrupt static inline void i2cRtos_waitSI(i2c_t *obj, uint32_t tmOut) { int ch = i2c_get_channel(obj); @@ -186,42 +198,42 @@ i2cRtos_wait_and_see(obj, ch, tmOut); } - +// master mode read sequence int i2cRtos_read(i2c_t *obj, int address, char *data, int length, int stop) { //gpio_write(&gpio[1], 1); int stat = i2c_start(obj); if ((stat != 0x10) && (stat != 0x08)) { - i2c_stop(obj); + i2cRtos_stop(obj); // use freeze free stop if the start has failed return stat; } //gpio_write(&gpio[1], 0); int ch = i2c_get_channel(obj); - struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem? + struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); tr->obj=obj; tr->cmd=readMst; tr->len=length; tr->cnt=-1; tr->rData=data; - I2C_DAT(obj) = address | 0x01; + I2C_DAT(obj) = address | 0x01; // initiate address+R write and enter isr mode i2cRtos_wait_and_see(obj, ch,2+(length>>2)); // timeout (2+len/4)ms stat = tr->stat; //gpio_write(&gpio[1], 1); - if(stat || stop) i2c_stop(obj); + if(stat || stop) i2cRtos_stop(obj); //gpio_write(&gpio[1], 0); return stat; } +// master mode write sequence int i2cRtos_write(i2c_t *obj, int address, const char *data, int length, int stop) { //gpio_write(&gpio[1], 1); int status = i2c_start(obj); if ((status != 0x10) && (status != 0x08)) { - i2c_stop(obj); + i2cRtos_stop(obj); // use freeze free stop if the start has failed return status; } //gpio_write(&gpio[1], 0); - int ch = i2c_get_channel(obj); struct I2cIsrTransfer* tr = &(i2c_transfer[ch]); // evilive fill it locally and then copy it in one go to (volatile) mem? tr->obj = obj; @@ -229,16 +241,17 @@ tr->len = length; tr->cnt = -1; tr->wData = data; - I2C_DAT(obj) = address & 0xfe; + I2C_DAT(obj) = address & 0xfe; // initiate address+W write and enter isr mode i2cRtos_wait_and_see(obj, ch, 2+(length>>2)); // timeout (2+len/4)ms //i2c_clear_SI(obj); // ... why? Also in official lib ... I guess this is the "write instead of start" bug status = tr->stat; //gpio_write(&gpio[1], 1); - if(status || stop) i2c_stop(obj); + if(status || stop) i2cRtos_stop(obj); //gpio_write(&gpio[1], 0); return status; } +// read single byte from bus (master/slave) int i2cRtos_byte_read(i2c_t *obj, int last) { if(last) { @@ -250,6 +263,7 @@ return (I2C_DAT(obj) & 0xff); } +// write single byte to bus (master/slave) int i2cRtos_byte_write(i2c_t *obj, int data) { I2C_DAT(obj) = (data & 0xff); @@ -258,14 +272,17 @@ return (stat==0x18 || stat==0x28 || stat==0x40 || stat==0xb8); } - -inline int i2cRtos_stop(i2c_t *obj) { +// freeze free i2c stop +// the busy wait without timeout of the official i2c lib +// might freeze the mbed if someone on the bus keeps the clock down +// and prevents LPC's i2c controller to create a stop condition +int i2cRtos_stop(i2c_t *obj) +{ i2c_conset(obj, 0, 1, 0, 0); i2c_clear_SI(obj); - uint32_t t0=us_ticker_read(); uint32_t dt=0; - while((I2C_CONSET(obj) & (1 << 4)) && dt<23){ + while((I2C_CONSET(obj) & (1 << 4)) && dt<23) { dt = us_ticker_read() - t0; } return dt<23; @@ -274,18 +291,19 @@ #if DEVICE_I2CSLAVE - +// determine slave's receive mode. Blocking with timeout 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! + // No request? Wait for it! ... with time out i2cRtos_waitSI(obj, tmOut); // check again for pending requests return i2c_slave_receive(obj); } +// slave mode read sequence int i2cRtos_slave_read(i2c_t *obj, char *data, int length) { int ch = i2c_get_channel(obj); @@ -297,12 +315,13 @@ tr->rData=data; i2cRtos_wait_and_see(obj, ch, 2+(length>>2)); // timeout (1+len/4)ms if(tr->stat != 0xa0) { - i2c_stop(obj); + i2cRtos_stop(obj); } - i2c_clear_SI(obj); // ... why? Also in official lib ... stops keeping scl low + i2c_clear_SI(obj); // stop keeping scl low return tr->cnt; // same weird return as in official lib } +// slave mode write sequence int i2cRtos_slave_write(i2c_t *obj, const char *data, int length) { if(length <= 0) { @@ -319,25 +338,24 @@ i2cRtos_wait_and_see(obj, ch, 2+(length>>2)); // timeout (1+len/4)ms int status = tr->stat; if(status!=0xC0 && status!=0xC8) { - i2c_stop(obj); + i2cRtos_stop(obj); } - i2c_clear_SI(obj); // ... why? Also in official lib ... stops keeping scl low + i2c_clear_SI(obj); // stops keeping scl low return tr->cnt; } - +#endif // setup semaphores and hook in ISRs void i2cRtos_init(i2c_t *obj, PinName sda, PinName scl) { - - static int called=0; + /*static int called=0; if(!called) { gpio_init(&gpio[0], p15, PIN_OUTPUT); gpio_init(&gpio[1], p16, PIN_OUTPUT); } called = 1; gpio_write(&gpio[0], 0); - gpio_write(&gpio[1], 0); + gpio_write(&gpio[1], 0);*/ i2c_init(obj,sda,scl); uint32_t ch = i2c_get_channel(obj); @@ -368,4 +386,3 @@ #endif } #endif -#endif
--- a/i2cRtos_api.h Fri May 10 20:38:35 2013 +0000 +++ b/i2cRtos_api.h Sun May 19 11:21:16 2013 +0000 @@ -1,18 +1,4 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2013 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + #ifndef MBED_I2CRTOS_API_H #define MBED_I2CRTOS_API_H