diff --git a/.github/workflows/LibraryBuild.yml b/.github/workflows/LibraryBuild.yml new file mode 100644 index 0000000..5ad588c --- /dev/null +++ b/.github/workflows/LibraryBuild.yml @@ -0,0 +1,59 @@ +# LibraryBuild.yml +# Github workflow script to test compile all examples of an Arduino library repository. +# +# Copyright (C) 2021 Armin Joachimsmeyer +# https://github.com/ArminJo/Github-Actions + +# This is the name of the workflow, visible on GitHub UI. +name: LibraryBuild + +on: + workflow_dispatch: # To run it manually + push: # see: https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request + paths: + - '**.ino' + - '**.cpp' + - '**.h' + - '**LibraryBuild.yml' + pull_request: + +jobs: + build: + name: ${{ matrix.arduino-boards-fqbn }} - test compiling examples + + runs-on: ubuntu-latest # I picked Ubuntu to use shell scripts. + + strategy: + matrix: + # The matrix will produce one job for each configuration parameter of type `arduino-boards-fqbn` + # In the Arduino IDE, the fqbn is printed in the first line of the verbose output for compilation as parameter -fqbn=... for the "arduino-builder -dump-prefs" command + # + # Examples: arduino:avr:uno, arduino:avr:leonardo, arduino:avr:nano, arduino:avr:mega + # arduino:sam:arduino_due_x, arduino:samd:arduino_zero_native" + # ATTinyCore:avr:attinyx5:chip=85,clock=1internal, digistump:avr:digispark-tiny, digistump:avr:digispark-pro + # STMicroelectronics:stm32:GenF1:pnum=BLUEPILL_F103C8 + # esp8266:esp8266:huzzah:eesz=4M3M,xtal=80, esp32:esp32:featheresp32:FlashFreq=80 + # You may add a suffix behind the fqbn with "|" to specify one board for e.g. different compile options like arduino:avr:uno|trace + ############################################################################################################# + arduino-boards-fqbn: + - arduino:avr:uno + - arduino:avr:leonardo + + # Choose the right platform for the boards we want to test. (maybe in the future Arduino will automatically do this for you) + # With sketches-exclude you may exclude specific examples for a board. Use a comma separated list. + ############################################################################################################# + # Do not cancel all jobs / architectures if one job fails + fail-fast: false + + steps: + - name: Checkout + uses: actions/checkout@master + + - name: Compile all examples + uses: ArminJo/arduino-test-compile@master + with: + arduino-board-fqbn: ${{ matrix.arduino-boards-fqbn }} + platform-url: ${{ matrix.platform-url }} + build-properties: ${{ toJson(matrix.build-properties) }} + cli-version: 0.33.0 # in order to avoid errors for ATTinyCore + diff --git a/changelog.txt b/changelog.txt index d7fd137..f52bc24 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,20 @@ CHANGELOG for FlexWire -V0.0.1 (30.1.2023) +V1.0.0 (14.12.2023) + - Fix: Set received bytes to zero in call to requestFrom when I2C + device NAKs on the device address + - Changed DELAY to I2C_DELAY + - Added Atomic Blocks for level changes, provided the AVR + architecture is used + - Added new example: multi_i2c + - Added new method: setPins + - removed the requestFrom call with internal registers (not + documented, but present in the Wire lib) + - Added the possibility to instantiate class without pin arguments; + these have to be set later using setPins + - removed i2c_start_wait + +V0.0.1 (30.11.2023) - initial commit, merging SlowSoftI2CMaster and SlowSoftMaster - - contains already Wire.h in the library folder + - contains already Wire.h in the library folder + diff --git a/examples/i2cscanFlexWire/i2cscanFlexWire.ino b/examples/i2cscanFlexWire/i2cscanFlexWire.ino index 4c8edaf..afcd37e 100644 --- a/examples/i2cscanFlexWire/i2cscanFlexWire.ino +++ b/examples/i2cscanFlexWire/i2cscanFlexWire.ino @@ -34,7 +34,7 @@ #include -FlexWire Wire = FlexWire(A4, A5, true); +FlexWire Wire = FlexWire(A4, A5, false); void setup() { diff --git a/examples/multi_i2c/multi_i2c.ino b/examples/multi_i2c/multi_i2c.ino new file mode 100644 index 0000000..207717c --- /dev/null +++ b/examples/multi_i2c/multi_i2c.ino @@ -0,0 +1,66 @@ +/* + * Demonstrate that we can replace Wire and use multiple instances to implement multiple I2C buses. + * We use the Sparkfun library for the HTU21D temp/humidity sensor to demonstrate that +*/ + +#define SHARESCL 0 +#define SWITCHPINS 0 + +#include +#include + +#define MAXSENSOR 2 + +// The pins are we are going to use for the I2C buses +uint8_t sdapin[MAXSENSOR] = { 2, 4 }; +#if SHARESCL +const uint8_t sclpin = 3; +#else +uint8_t sclpin[MAXSENSOR] = { 3, 5 }; +#endif + +#if SWITCHPINS +FlexWire Wire; +HTU21D htu; +#else +// Array of Flexwire instances +#if SHARESCL +FlexWire wire[MAXSENSOR] = { {sdapin[0], sclpin}, {sdapin[1], sclpin} }; +#else +FlexWire wire[MAXSENSOR] = { {sdapin[0], sclpin[0]}, {sdapin[1], sclpin[1]} }; +#endif +// Create array of instances of the HTU21D class +HTU21D htu[MAXSENSOR]; +#endif + +void setup() +{ + Serial.begin(9600); + Serial.println(F("Multi-I2C example with HTU21D")); +#if SWITCHPINS + for (uint8_t i=0; i < MAXSENSOR; i++) { + Wire.setPins(sdapin[i], sclpin); + htu.begin(); + } +#else + for (uint8_t i=0; i < MAXSENSOR; i++) htu[i].begin(wire[i]); +#endif +} + +void loop() +{ + for (uint8_t i=0; i < MAXSENSOR; i++) { + Serial.print(F("Sensor ")); + Serial.print(i+1); + Serial.print(F(": ")); +#if SWITCHPINS + Wire.setPins(sdapin[i], sclpin); + Serial.print(htu.readTemperature(), 1); +#else + Serial.print(htu[i].readTemperature(), 1); +#endif + Serial.println("C"); + } + Serial.println(); + delay(1000); +} diff --git a/library.properties b/library.properties index 05681b4..a57a8cc 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=FlexWire -version=0.0.1 +version=1.0.0 author=Bernhard Nebel maintainer=Bernhard Nebel -sentence=This library implements the master side of the I2C protocol. -paragraph=It can be used a drop-in replacement for the Wire library without even touching the sensor/actuator library, which uses Wire +sentence=This library implements the master side of the I2C protocol in a platform independent way. +paragraph=It can be used a drop-in replacement for the Wire library without even touching the sensor/actuator library, which uses the Wire library. The reason is that the library folder contains the header file Wire.h, which satisfies the dependency for the Wire library. In addition, it supports dynamic changes of the I2C pins. category=Communication url=https://github.com/felias-fogg/FlexWire architectures=* diff --git a/src/FlexWire.cpp b/src/FlexWire.cpp index c3b7aa1..c22be2d 100644 --- a/src/FlexWire.cpp +++ b/src/FlexWire.cpp @@ -13,9 +13,9 @@ #include FlexWire::FlexWire(uint8_t sda, uint8_t scl, bool internal_pullup): - _sda(sda), - _scl(scl), - _pullup(internal_pullup) { } + _pullup(internal_pullup) { + setPins(sda, scl); +} void FlexWire::begin(void) { _rxBufferIndex = 0; @@ -29,6 +29,12 @@ void FlexWire::begin(void) { void FlexWire::setClock(uint32_t _) { } +void FlexWire::setPins(uint8_t sda, uint8_t scl) +{ + _sda = sda; + _scl = scl; +} + void FlexWire::beginTransmission(uint8_t address) { if (_transmitting) { _error = (i2c_rep_start((address<<1)|I2C_WRITE) ? 0 : 2); @@ -79,23 +85,8 @@ size_t FlexWire::write(const uint8_t *data, size_t quantity) { return trans; } -uint8_t FlexWire::requestFrom(uint8_t address, uint8_t quantity, - uint32_t iaddress, uint8_t isize, uint8_t sendStop) { +uint8_t FlexWire::requestFrom(uint8_t address, uint8_t quantity, bool sendStop) { uint8_t localerror = 0; - if (isize > 0) { - // send internal address; this mode allows sending a repeated start to access - // some devices' internal registers. This function is executed by the hardware - // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers) - beginTransmission(address); - // the maximum size of internal address is 3 bytes - if (isize > 3){ - isize = 3; - } - // write internal register address - most significant byte first - while (isize-- > 0) - write((uint8_t)(iaddress >> (isize*8))); - endTransmission(false); - } // clamp to buffer length if(quantity > BUFFER_LENGTH){ quantity = BUFFER_LENGTH; @@ -103,35 +94,26 @@ uint8_t FlexWire::requestFrom(uint8_t address, uint8_t quantity, localerror = !i2c_rep_start((address<<1) | I2C_READ); if (_error == 0 && localerror) _error = 2; // perform blocking read into buffer - for (uint8_t cnt=0; cnt < quantity; cnt++) - _rxBuffer[cnt] = i2c_read(cnt == quantity-1); + if (!localerror) { + for (uint8_t cnt=0; cnt < quantity; cnt++) + _rxBuffer[cnt] = i2c_read(cnt == quantity-1); + } else { + quantity = 0; + } // set rx buffer iterator vars _rxBufferIndex = 0; _rxBufferLength = quantity; - if (sendStop) { + if (sendStop || _error != 0) { _transmitting = 0; i2c_stop(); } return quantity; } -uint8_t FlexWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) { - return requestFrom((uint8_t)address, (uint8_t)quantity, (uint32_t)0, (uint8_t)0, (uint8_t)sendStop); +uint8_t FlexWire::requestFrom(int address, int quantity, bool sendStop) { + return requestFrom((uint8_t)address, (uint8_t)quantity, sendStop); } - -uint8_t FlexWire::requestFrom(int address, int quantity, int sendStop) { - return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop); -} - - -uint8_t FlexWire::requestFrom(uint8_t address, uint8_t quantity) { - return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); -} - -uint8_t FlexWire::requestFrom(int address, int quantity) { - return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); -} - + int FlexWire::available(void) { return _rxBufferLength - _rxBufferIndex; } @@ -159,12 +141,15 @@ void FlexWire::flush(void) { // Init function. Needs to be called once in the beginning. // Returns false if SDA or SCL are low, which probably means -// a I2C bus lockup or that the lines are not pulled up. +// an I2C bus lockup or that the lines are not pulled up. bool FlexWire::i2c_init(void) { digitalWrite(_sda, LOW); digitalWrite(_scl, LOW); - setHigh(_sda); + delayMicroseconds(I2C_DELAY/2); setHigh(_scl); + delayMicroseconds(I2C_DELAY/2); + setHigh(_sda); + delayMicroseconds(I2C_DELAY/2); if (digitalRead(_sda) == LOW || digitalRead(_scl) == LOW) return false; return true; } @@ -172,10 +157,13 @@ bool FlexWire::i2c_init(void) { // Start transfer function: is the 8-bit I2C address (including the R/W // bit). // Return: true if the slave replies with an "acknowledge", false otherwise +// return also false when one of the lines is initially low (which might be a shortcut) bool FlexWire::i2c_start(uint8_t addr) { + if (digitalRead(_sda) == 0 || digitalRead(_scl) == 0) return false; setLow(_sda); - delayMicroseconds(DELAY); + delayMicroseconds(I2C_DELAY); setLow(_scl); + delayMicroseconds(I2C_DELAY/2); return i2c_write(addr); } @@ -185,38 +173,39 @@ 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) { setHigh(_sda); + delayMicroseconds(I2C_DELAY/2); setHigh(_scl); - delayMicroseconds(DELAY); + delayMicroseconds(I2C_DELAY); return i2c_start(addr); } // Issue a stop condition, freeing the bus. void FlexWire::i2c_stop(void) { setLow(_sda); - delayMicroseconds(DELAY); + delayMicroseconds(I2C_DELAY); setHigh(_scl); - delayMicroseconds(DELAY); + delayMicroseconds(I2C_DELAY); setHigh(_sda); - delayMicroseconds(DELAY); + delayMicroseconds(I2C_DELAY); } // Write one byte to the slave chip that had been addressed // by the previous start call. is the byte to be sent. // Return: true if the slave replies with an "acknowledge", false otherwise bool FlexWire::i2c_write(uint8_t value) { - for (uint8_t curr = 0X80; curr != 0; curr >>= 1) { + for (uint8_t curr = 0x80; curr != 0; curr >>= 1) { if (curr & value) setHigh(_sda); else setLow(_sda); setHigh(_scl); - delayMicroseconds(DELAY); + delayMicroseconds(I2C_DELAY); setLow(_scl); } // get Ack or Nak setHigh(_sda); setHigh(_scl); - delayMicroseconds(DELAY/2); + delayMicroseconds(I2C_DELAY/2); uint8_t ack = digitalRead(_sda); setLow(_scl); - delayMicroseconds(DELAY/2); + delayMicroseconds(I2C_DELAY/2); setLow(_sda); return ack == 0; } @@ -228,35 +217,35 @@ uint8_t FlexWire::i2c_read(bool last) { setHigh(_sda); for (uint8_t i = 0; i < 8; i++) { b <<= 1; - delayMicroseconds(DELAY); + delayMicroseconds(I2C_DELAY); setHigh(_scl); if (digitalRead(_sda)) b |= 1; setLow(_scl); } if (last) setHigh(_sda); else setLow(_sda); setHigh(_scl); - delayMicroseconds(DELAY/2); + delayMicroseconds(I2C_DELAY/2); setLow(_scl); - delayMicroseconds(DELAY/2); + delayMicroseconds(I2C_DELAY/2); setLow(_sda); return b; } void FlexWire::setLow(uint8_t pin) { - noInterrupts(); +#ifdef ATOMIC_BLOCK + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) +#endif + { if (_pullup) digitalWrite(pin, LOW); pinMode(pin, OUTPUT); - interrupts(); + } } - void FlexWire::setHigh(uint8_t pin) { - noInterrupts(); if (_pullup) pinMode(pin, INPUT_PULLUP); else pinMode(pin, INPUT); - interrupts(); } diff --git a/src/FlexWire.h b/src/FlexWire.h index 399bf0e..a810c5f 100644 --- a/src/FlexWire.h +++ b/src/FlexWire.h @@ -5,11 +5,14 @@ #include #include +#if defined(ARDUINO_ARCH_AVR) +#include +#endif -#define FLEXWIRE_VERSION 0.0.1 +#define FLEXWIRE_VERSION 1.0.0 #define I2C_READ 1 #define I2C_WRITE 0 -#define DELAY 4 // usec delay +#define I2C_DELAY 4 // usec delay #define BUFFER_LENGTH 32 #define I2C_MAXWAIT 5000 @@ -26,7 +29,6 @@ class FlexWire { bool i2c_init(void); bool i2c_start(uint8_t addr); - bool i2c_start_wait(uint8_t addr); bool i2c_rep_start(uint8_t addr); void i2c_stop(void); bool i2c_write(uint8_t value); @@ -35,23 +37,19 @@ class FlexWire { void setLow(uint8_t pin); public: - FlexWire(uint8_t sda, uint8_t scl); - FlexWire(uint8_t sda, uint8_t scl, bool internal_pullup); + FlexWire(uint8_t sda = 0, uint8_t scl = 0, bool internal_pullup = false); void begin(void); void end(void); void setClock(uint32_t _); + void setPins(uint8_t sda, uint8_t scl); void beginTransmission(uint8_t address); void beginTransmission(int address); uint8_t endTransmission(uint8_t sendStop); uint8_t endTransmission(void); size_t write(uint8_t data); size_t write(const uint8_t *data, size_t quantity); - uint8_t requestFrom(uint8_t address, uint8_t quantity, - uint32_t iaddress, uint8_t isize, uint8_t sendStop); - uint8_t requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop); - uint8_t requestFrom(int address, int quantity, int sendStop); - uint8_t requestFrom(uint8_t address, uint8_t quantity); - uint8_t requestFrom(int address, int quantity); + uint8_t requestFrom(uint8_t address, uint8_t quantity, bool sendStop = true); + uint8_t requestFrom(int address, int quantity, bool sendStop = true); int available(void); int read(void); int peek(void); diff --git a/src/Wire.h b/src/Wire.h index bb31ca7..c8c3ac7 100644 --- a/src/Wire.h +++ b/src/Wire.h @@ -5,6 +5,6 @@ extern FlexWire Wire; -#define TwoWire FlexWire +typedef FlexWire TwoWire; #endif