Prof Greg Egan I2C code, prevents I2C freeze when using interrupt in
Diff: MyI2C.cpp
- Revision:
- 0:1c722325e933
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MyI2C.cpp Tue Aug 27 09:31:56 2013 +0000 @@ -0,0 +1,207 @@ +#include "MyI2C.h" + +const uint8_t mbed1768Pins[32] = { // Maping of mbed pins to LPC 1768 "port" pins + 255,255,255,255,255,9,8,7,6,0, + 1,18,17,15,16,23,24,25,26,62, + 63,69,68,67,66,65,64,11,10,5, + 4,255 +}; + +#define I2C0SDASet (1<<mbed1768Pins[28&0x1f]) // 28 +#define I2C0SDAPort (mbed1768Pins[28&0x0f]>>5) +PortInOut I2C0SDA(Port0, I2C0SDASet ); + +#define I2C0SCLSet (1<<mbed1768Pins[27&0x1f]) // 27 +#define I2C0SCLPort (mbed1768Pins[27&0x0f]>>5) +PortInOut I2C0SCL(Port0, I2C0SCLSet ); + +MyI2C I2C0; + +bool I2C0AddressResponds(uint8_t); + +uint16_t I2CError[256]; + +void SDelay(uint16_t d) +{ // 1.25 + 0.0475 * n uS ~0.05uS per click + volatile int16_t v; + for (v = 0; v < d ; v++ ) {}; +} // SDelay + +#define SCLLowStartT SDelay(10) +#define DataLowPadT SDelay(16) // 10 +#define SCLLowPadT SDelay(6) +#define SCLHighT SDelay(10) + +#define I2CSDALow {I2C0SDA.write(0);I2C0SDA.output();SCLLowPadT;} +#define I2CSDAFloat {I2C0SDA.input();SCLLowPadT;} +#define I2CSCLLow {I2C0SCL.write(0);I2C0SCL.output();} +#define I2CSCLFloat {I2C0SCL.input();SCLHighT;} + +void MyI2C::start(void) +{ + I2CSDAFloat; + bool r = waitclock(); + I2CSDALow; + SCLLowStartT; + I2CSCLLow; +} // start + +void MyI2C::stop(void) +{ + I2CSDALow; + bool r = waitclock(); + I2CSDAFloat; + SCLLowStartT; +} // stop + +bool MyI2C::waitclock(void) +{ + static uint32_t s; + I2CSCLFloat; // set SCL to input, output a high + s = 0; + while ( I2C0SCL.read() == 0 ) + { + if ( ++s > 16000 ) + { // ~1mS + I2CError[0]++; + // possible add SCL cycles here to attempt to force device reset + //Stats[I2CFailS]++; + return (false); + } + } + return( true ); +} // waitclock + +uint8_t MyI2C::read(uint8_t ack) +{ + static uint8_t s, d; + I2CSDAFloat; + d = 0; + s = 8; + do + { + if ( waitclock() ) + { + d <<= 1; + if ( I2C0SDA.read() ) + { + d |= 1; + I2CSCLLow; + DataLowPadT; + } + else + { + I2CSCLLow; + SDelay(10);//DataLowPadT; + } + } + else + { + return( 0 ); + } + } while ( --s ); + if ( ack == I2C_NACK ) + { + I2C0SDA.write(0xffff); // Port write with mask selecting SDA - messy + } + else + { + I2C0SDA.write(0); + } + I2C0SDA.output(); + SCLLowPadT; + if ( waitclock() ) + { + I2CSCLLow; + return( d ); + } + else + { + return( 0 ); + } +} // read + +uint8_t MyI2C::write(uint8_t d) +{ + static uint8_t s, r; + for ( s = 0; s < 8; s++) + { + if ( d & 0x80 ) + { + I2CSDAFloat; + } + else + { + I2CSDALow; + } + + if ( waitclock() ) + { + I2CSCLLow; + d <<= 1; + } + else + { + return(I2C_NACK); + } + } + + I2CSDAFloat; + if ( waitclock() ) { + if ( I2C0SDA.read() ) + r = I2C_NACK; + else + r = I2C_ACK; + I2CSDALow;// kill runt pulses + I2CSCLLow; + return ( r ); + } + else + { +// I2CSCLLow; + return(I2C_NACK); + } + +} // write + +uint8_t MyI2C::blockread(uint8_t a, char* S, uint8_t l) +{ + static uint8_t b; + static bool err; + + I2C0.start(); + err = I2C0.write(a|1) != I2C_ACK; + for (b = 0; b < (l - 1); b++) + S[b] = I2C0.read(I2C_ACK); + S[l-1] = I2C0.read(I2C_NACK); + I2C0.stop(); + + return( err ); +} // blockread + +bool MyI2C::blockwrite(uint8_t a, const char* S, uint8_t l) +{ + static uint8_t b; + + I2C0.start(); + if ( I2C0.write(a) != I2C_ACK ) goto BlockWriteError; // use this? + for ( b = 0; b < l; b++ ) + if ( I2C0.write(S[b]) != I2C_ACK ) goto BlockWriteError; + I2C0.stop(); + + return(false); + +BlockWriteError: + I2C0.stop(); + + return(true); + +} // blockwrite + +bool I2C0AddressResponds(uint8_t s) { + static bool r; + I2C0.start(); + r = I2C0.write(s) == I2C_ACK; + I2C0.stop(); + return (r); +} // I2C0AddressResponds \ No newline at end of file