Skip to content

Commit

Permalink
V1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
felias-fogg committed Dec 15, 2023
1 parent 9b7fc00 commit cee265b
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 17 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@

Why would you like to use this library? Well, it is a true drop-in replacement for the Wire library (master part). When you include it in your sketch file before any other library, then it will take over the part of the Wire library. In other words, you can simply use any sensor/actuator library that makes use of the Wire library, and it will work ([magically!](https://arduino-craft-corner.de/index.php/2023/11/29/replacing-the-wire-library-sometimes/)). Further, since you can dynamically change the pins for SDA and SCL, you can have as many I2C buses as you want. This means you can implement a sort of software I2C multiplexer, communicating with several I2C devices that have the same I2C address ([see my blog post on that](https://arduino-craft-corner.de/index.php/2023/12/14/software-i2c-multiplexer/)). Furthermore, since it uses only high-level Arduino built-in functions, it should work on all architectures supported by Arduino.

The only disadvantage is that it is somewhat slow. On an AVR MCU with 16 MHz, you get by default roughly 50 kHz. On other platforms that support higher MCU clock frequencies, it should be faster. However, since V1.1.0, the bit-banging on AVR MCUs is implemented using port manipulation, which means that on an AVR MCU running at 16 MHz, you can get up to 250 kHz bus frequency.
The only disadvantage is that it is somewhat slow. Since V1.1.0, on AVR MCUs bit-banging is implemented using port manipulation, which means that on an AVR MCU running at 16 MHz, you can get up to 140 kHz bus frequency. The default is 90 kHz. For other architectures, I have not measured the speed (yet).
34 changes: 21 additions & 13 deletions src/FlexWire.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,19 @@ void FlexWire::begin(void) {
_rxBufferLength = 0;
_error = 0;
_transmitting = false;
_i2cDelay = I2C_DEFAULT_DELAY;
i2c_init();
}

void FlexWire::setClock(uint32_t Hz) {
#if defined(ARDUINO_ARCH_AVR)
uint16_t codeDelay;
_i2cDelay = 1000000UL / Hz ;
#if AVR_OPTIMIZATION
if (Hz <= 10000UL) _i2cDelay = 90;
else if (Hz <= 30000UL) _i2cDelay = 30;
else if (Hz <= 200000UL) _i2cDelay = 5;
codeDelay = 112000000UL / F_CPU; // us delay by code
#else
codeDelay = 288000000UL / F_CPU; // us delay by code
#endif
if (codedelay < _i2cDelay) _i2Delay = (_i2cDelay - codeDelay)/2;
else _i2cDelay = 0;
#endif
}
Expand Down Expand Up @@ -165,13 +169,15 @@ void FlexWire::flush(void) {
// Returns false if SDA or SCL are low, which probably means
// an I2C bus lockup or that the lines are not pulled up.
bool FlexWire::i2c_init(void) {
pinMode(_sda, INPUT);
digitalWrite(_sda, LOW);
pinMode(_scl, INPUT);
digitalWrite(_scl, LOW);
delayMicroseconds(_i2cDelay/2);
delayMicroseconds(_i2cDelay);
setSclHigh();
delayMicroseconds(_i2cDelay/2);
delayMicroseconds(_i2cDelay);
setSdaHigh();
delayMicroseconds(_i2cDelay/2);
delayMicroseconds((_i2cDelay+1)*4);
if (getSda() == 0 || getScl() == 0) return false;
return true;
}
Expand All @@ -185,7 +191,7 @@ bool FlexWire::i2c_start(uint8_t addr) {
setSdaLow();
delayMicroseconds(_i2cDelay);
setSclLow();
delayMicroseconds(_i2cDelay/2);
delayMicroseconds(_i2cDelay);
return i2c_write(addr);
}

Expand All @@ -195,7 +201,7 @@ bool FlexWire::i2c_start(uint8_t addr) {
// Return: true if the slave replies with an "acknowledge", false otherwise
bool FlexWire::i2c_rep_start(uint8_t addr) {
setSdaHigh();
delayMicroseconds(_i2cDelay/2);
delayMicroseconds(_i2cDelay);
setSclHigh();
delayMicroseconds(_i2cDelay);
return i2c_start(addr);
Expand All @@ -220,14 +226,15 @@ bool FlexWire::i2c_write(uint8_t value) {
setSclHigh();
delayMicroseconds(_i2cDelay);
setSclLow();
delayMicroseconds(_i2cDelay);
}
// get Ack or Nak
setSdaHigh();
setSclHigh();
delayMicroseconds(_i2cDelay/2);
delayMicroseconds(_i2cDelay);
uint8_t ack = getSda();
setSclLow();
delayMicroseconds(_i2cDelay/2);
delayMicroseconds(_i2cDelay);
setSdaLow();
return ack == 0;
}
Expand All @@ -242,13 +249,14 @@ uint8_t FlexWire::i2c_read(bool last) {
delayMicroseconds(_i2cDelay);
setSclHigh();
if (getSda()) b |= 1;
delayMicroseconds(_i2cDelay);
setSclLow();
}
if (last) setSdaHigh(); else setSdaLow();
setSclHigh();
delayMicroseconds(_i2cDelay/2);
delayMicroseconds(_i2cDelay);
setSclLow();
delayMicroseconds(_i2cDelay/2);
delayMicroseconds(_i2cDelay);
setSdaLow();
return b;
}
Expand Down
6 changes: 3 additions & 3 deletions src/FlexWire.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#define FLEXWIRE_h
#define FLEXWIRE_VERSION 1.1.0

// #define AVR_OPTIMIZATION 0 // without optimizations, less code, but much slower (<60 kHz)
// #define AVR_OPTIMIZATION 0 // without optimizations, less code, but much slower (55 kHz)

#include <Arduino.h>
#include <inttypes.h>
Expand All @@ -18,7 +18,7 @@
#define I2C_READ 1
#define I2C_WRITE 0
#if AVR_OPTIMIZATION
#define I2C_DEFAULT_DELAY 5 // usec delay
#define I2C_DEFAULT_DELAY 3 // usec delay
#else
#define I2C_DEFAULT_DELAY 0 // usec delay
#endif
Expand All @@ -35,7 +35,7 @@ class FlexWire {
uint8_t _sda;
uint8_t _scl;
bool _pullup;
uint8_t _i2cDelay;
uint16_t _i2cDelay;
#if AVR_OPTIMIZATION
uint8_t _sdaBitMask;
uint8_t _sclBitMask;
Expand Down

0 comments on commit cee265b

Please sign in to comment.