Testato / SoftwareWire

Creates a software I2C/TWI bus on every pins
GNU General Public License v3.0
149 stars 33 forks source link

Expand your library in order for it to could be used with esp8266 #26

Open elC0mpa opened 4 years ago

elC0mpa commented 4 years ago

In this issue https://github.com/Testato/SoftwareWire/issues/10 you said that the esp8266 Wire library is already a software library. This is true, but it isn't able to create multiple i2c buses. You referenced another library, which works fine but it is not compatible with Wire original library, so if you use a device library which receives a pointer to a Wire object, you must modify the library (something i already did). Because of this, I think that if you improve your library in order for it to be used with esp8266, it will be the BEST SoftwareWire library. I hope you will think about it. I will be waiting for your answer

Testato commented 4 years ago

One of the idea about our library is to write a second working modality that will use digitalwrite() instead of registry manipulation. This will slow down the speed but will work on all mcu with an Arduino core.

Expecially on 32bit world, that normally have a big cpu freq, the speed problem will non be a problem, we can contunue to use the lib like now on 8 bit and with the new modality on 32bit cpu.

elC0mpa commented 4 years ago

Thanks for your answer, let me know if i can Help you in something

Koepel commented 4 years ago

My idea was to use the low-level functions of the OneWire library for other processors. However, I did some tests with the 48MHz SAMD21 processor (Arduino Zero, M0, and many MKR boards) and the digitalWrite() and digitalRead() seems fast enough for those. Using the normal Arduino functions as a fallback might be good enough for most faster boards.

It could be something like this:

#ifdef __AVR__

// Sets SDA low and drives output.
// The SDA may not be HIGH output, so first the output register is cleared 
// (clearing internal pullup resistor), after that the SDA is set as output.
#define i2c_sda_lo()              \
  *_sdaPortReg &= ~_sdaBitMask;   \
  *_sdaDirReg  |=  _sdaBitMask;

// sets SCL low and drives output.
// The SCL may not be HIGH output, so first the output register is cleared 
// (clearing internal pullup resistor), after that the SCL is set as output.
#define i2c_scl_lo()              \
  *_sclPortReg &= ~_sclBitMask;   \
  *_sclDirReg  |=  _sclBitMask;

// Set SDA high and to input (releases pin) (i.e. change to input,turnon pullup).
// The SDA may not become HIGH output. Therefor the pin is first set to input,
// after that, a pullup resistor is switched on if needed.
#define i2c_sda_hi()              \
  *_sdaDirReg &= ~_sdaBitMask;    \
  if(_pullups) { *_sdaPortReg |= _sdaBitMask; }

// set SCL high and to input (releases pin) (i.e. change to input,turnon pullup)
// The SCL may not become HIGH output. Therefor the pin is first set to input,
// after that, a pullup resistor is switched on if needed.
#define i2c_scl_hi()              \
  *_sclDirReg &= ~_sclBitMask;    \
  if(_pullups) { *_sclPortReg |= _sclBitMask; }

// Read the bit value of the pin
// Note that is the pin can also be read when it is an output.
#define i2c_sda_read()   ((uint8_t) (*_sdaPinReg & _sdaBitMask) ? 1 : 0)
#define i2c_scl_read()   ((uint8_t) (*_sclPinReg & _sclBitMask) ? 1 : 0)

#else

// Fall back to default functions.

#define i2c_sda_lo()      digitalWrite(_sdaPin, LOW); pinMode(_sdaPin, OUTPUT)
#define i2c_scl_lo()      digitalWrite(_sclPin, LOW); pinMode(_sclPin, OUTPUT)
#define i2c_sda_hi()      pinMode(_sdaPin, _pullups ? INPUT_PULLUP : INPUT)
#define i2c_scl_hi()      pinMode(_sclPin, _pullups ? INPUT_PULLUP : INPUT)
#define i2c_sda_read()    digitalRead(_sdaPin)
#define i2c_scl_read()    digitalRead(_sclPin)

#endif

The ::setClock() needs a better formula, I did some tests with this:

#ifdef __AVR__
  // The _i2cdelay is an uint16_t
  _i2cdelay = ( (F_CPU / 40L) / clock );               // The delay in microseconds, '40' is for this code.
  unsigned int delayByCode = (F_CPU / 5000000L);       // Add some delay for the code, just a guess

  if( _i2cdelay > delayByCode)
    _i2cdelay -= delayByCode;
  else
    _i2cdelay = 0;
#else
  _i2cdelay = (F_CPU / 10L) / clock;         // set to a safe number
#endif
elC0mpa commented 4 years ago

Your idea looks nice @Koepel, now i am busy but maybe in one or two weeks i could Help you. If i do this, I will make you a pull request. Thanks for sharing