Mirror with some correction
Dependencies: mbed FastIO FastPWM USBDevice
BitBangI2C/BitBangI2C.cpp
- Committer:
- arnoz
- Date:
- 2021-10-01
- Revision:
- 116:7a67265d7c19
- Parent:
- 87:8d35c74403af
File content as of revision 116:7a67265d7c19:
// Bit Bang BitBangI2C implementation for KL25Z // #include "mbed.h" #include "BitBangI2C.h" // -------------------------------------------------------------------------- // // Debugging: // // 0 -> no debugging // 1 -> print (on console) error messages only // 2 -> print full diagnostics // // dprintf() = general debug diagnostics (printed only in case 2) // eprintf() = error diagnostics (printed in case 1 and above) // #define BBI2C_DEBUG 0 #if BBI2C_DEBUG # define eprintf(...) printf(__VA_ARGS__) # if BBI2C_DEBUG >= 2 # define dprintf(...) printf(__VA_ARGS__) # else # define dprintf(...) # endif static const char *dbgbytes(const uint8_t *bytes, size_t len) { static char buf[128]; char *p = buf; for (int i = 0 ; i < len && p + 4 < buf + sizeof(buf) ; ++i) { if (i > 0) *p++ = ','; sprintf(p, "%02x", bytes[i]); p += 2; } *p = '\0'; return buf; } #else # define dprintf(...) # define eprintf(...) #endif // -------------------------------------------------------------------------- // // Bit-bang I2C implementation // BitBangI2C::BitBangI2C(PinName sda, PinName scl, bool internalPullup) : sdaPin(sda, internalPullup), sclPin(scl, internalPullup) { // set the default frequency to 100kHz frequency(100000); // we're initially in a stop inStop = true; } void BitBangI2C::frequency(uint32_t freq) { // figure the clock time per cycle clkPeriod_us = 1000000/freq; // Figure wait times according to frequency if (freq <= 100000) { // standard mode I2C bus - up to 100kHz // nanosecond parameters tLow = calcHiResWaitTime(4700); tHigh = calcHiResWaitTime(4000); tHdSta = calcHiResWaitTime(4000); tSuSta = calcHiResWaitTime(4700); tSuSto = calcHiResWaitTime(4000); tAck = calcHiResWaitTime(300); tSuDat = calcHiResWaitTime(250); tBuf = calcHiResWaitTime(4700); } else if (freq <= 400000) { // fast mode I2C - up to 400kHz // nanosecond parameters tLow = calcHiResWaitTime(1300); tHigh = calcHiResWaitTime(600); tHdSta = calcHiResWaitTime(600); tSuSta = calcHiResWaitTime(600); tSuSto = calcHiResWaitTime(600); tAck = calcHiResWaitTime(100); tSuDat = calcHiResWaitTime(100); tBuf = calcHiResWaitTime(1300); } else { // fast mode plus - up to 1MHz // nanosecond parameters tLow = calcHiResWaitTime(500); tHigh = calcHiResWaitTime(260); tHdSta = calcHiResWaitTime(260); tSuSta = calcHiResWaitTime(260); tSuSto = calcHiResWaitTime(260); tAck = calcHiResWaitTime(50); tSuDat = calcHiResWaitTime(50); tBuf = calcHiResWaitTime(500); } } void BitBangI2C::start() { // check to see if we're starting after a stop, or if this is a // repeated start if (inStop) { // in a stop - make sure we waited for the minimum hold time hiResWait(tBuf); } else { // repeated start - take data high sdaHi(); hiResWait(tSuDat); // take clock high sclHi(); // wait for the minimum setup period hiResWait(tSuSta); } // take data low sdaLo(); // wait for the setup period and take clock low hiResWait(tHdSta); sclLo(); // wait for the low period hiResWait(tLow); // no longer in a stop inStop = false; } void BitBangI2C::stop() { // if we're not in a stop, enter one if (!inStop) { // take SDA low sdaLo(); // take SCL high sclHi(); hiResWait(tSuSto); // take SDA high sdaHi(); // we're in a stop inStop = true; } } bool BitBangI2C::wait(uint32_t timeout_us) { // set up a timer to monitor the timeout period Timer t; t.start(); // wait for an ACK for (;;) { // if SDA is low, it's an ACK if (!sdaPin.read()) return true; // if we've reached the timeout, abort if (t.read_us() > timeout_us) return false; } } void BitBangI2C::reset() { // write out 9 '1' bits for (int i = 0 ; i < 9 ; ++i) writeBit(1); // issue a start sequence start(); // take the clock high sclHi(); // wait for a few clock cycles wait_us(4*clkPeriod_us); } int BitBangI2C::write(uint8_t addr, const uint8_t *data, size_t len, bool repeated) { dprintf("i2c.write, addr=%02x [%s] %srepeat\r\n", addr, dbgbytes(data, len), repeated ? "" : "no "); // send the start signal start(); // send the address with the R/W bit set to WRITE (0) if (write(addr)) { eprintf(". i2c.write, address write failed, addr=%02x [%s] %srepeat\r\n", addr, dbgbytes(data, len), repeated ? "": "no "); return -1; } // send the data bytes for (int i = 0 ; i < len ; ++i) { if (write(data[i])) { eprintf(". i2c.write, write failed at byte %d, addr=%02x [%s] %srepeat\r\n", i, addr, dbgbytes(data, len), repeated ? "" : "no "); return -2; } } // send the stop, unless the start is to be repeated if (!repeated) stop(); // success return 0; } int BitBangI2C::read(uint8_t addr, uint8_t *data, size_t len, bool repeated) { dprintf("i2c.read, addr=%02x\r\n", addr); // send the start signal start(); // send the address with the R/W bit set to READ (1) if (write(addr | 0x01)) { eprintf(". i2c.read, read addr write failed, addr=%02x [%s] %srepeat\r\n", addr, dbgbytes(data, len), repeated ? "" : "no "); return -1; } // Read the data. Send an ACK after each byte except the last, // where we send a NAK. for ( ; len != 0 ; --len, ++data) *data = read(len > 1); // send the stop signal, unless a repeated start is indicated if (!repeated) stop(); // success return 0; } int BitBangI2C::write(uint8_t data) { // write the bits, most significant first for (int i = 0 ; i < 8 ; ++i, data <<= 1) writeBit(data & 0x80); // release SDA so the device can control it sdaHi(); // read the ACK bit int ack = readBit(); // take SDA low again sdaLo(); // return success if ACK was 0 return ack; } int BitBangI2C::read(bool ack) { // take SDA high before reading sdaHi(); // read 8 bits, most significant first uint8_t data = 0; for (int i = 0 ; i < 8 ; ++i) data = (data << 1) | readBit(); // switch to output mode and send the ACK bit writeBit(!ack); // release SDA sdaHi(); // return the data byte we read return data; } int BitBangI2C::readBit() { // take the clock high (actually, release it to the pull-up) sclHi(); // Wait (within reason) for it to actually read as high. The device // can intentionally pull the clock line low to tell us to wait while // it's working on preparing the data for us. int t = 0; do { // if the clock is high, we're ready to go if (sclPin.read()) { // wait for the data setup time hiResWait(tSuDat); // read the bit bool bit = sdaPin.read(); // take the clock low again sclLo(); hiResWait(tLow); // return the bit return bit; } } while (t++ < 100000); // we timed out eprintf("i2c.readBit, clock stretching timeout\r\n"); return 0; }