diff --git a/PCF8574.cpp b/PCF8574.cpp index 0a6db6a..533f21d 100644 --- a/PCF8574.cpp +++ b/PCF8574.cpp @@ -8,9 +8,10 @@ // http://forum.arduino.cc/index.php?topic=184800 // // HISTORY: +// // 0.3.2 2021-07-04 fix #25 add setAddress() // 0.3.1 2021-04-23 Fix for platformIO compatibility -// 0.3.0 2021-01-03 multiWire support - inspirated by mattbue - issue #14 +// 0.3.0 2021-01-03 multiWire support - inspired by mattbue - issue #14 // 0.2.4 2020-12-17 fix #6 tag problem 0.2.3 // 0.2.3 2020-12-14 fix #6 readButton8 ambiguity // 0.2.2 2020-12-07 add Arduino-ci + start unit test + _wire->h in PCF8574.h @@ -36,7 +37,7 @@ // 0.1.05 2016-04-30 refactor, +toggleMask, +rotLeft, +rotRight // 0.1.04 2015-05-09 removed ambiguity in read8() // 0.1.03 2015-03-02 address int -> uint8_t -// 0.1.02 replaced ints with uint8_t to reduce footprint; +// 0.1.02 replaced integers with uint8_t to reduce footprint; // added default value for shiftLeft() and shiftRight() // renamed status() to lastError(); // 0.1.01 added value(); returns last read 8 bit value (cached); diff --git a/PCF8574.h b/PCF8574.h index 1d5e950..ca91154 100644 --- a/PCF8574.h +++ b/PCF8574.h @@ -39,24 +39,30 @@ class PCF8574 bool begin(uint8_t val = PCF8574_INITIAL_VALUE); bool isConnected(); + // note: setting the address corrupt internal buffer values // a read8() / write8() call updates them. bool setAddress(const uint8_t deviceAddress); uint8_t getAddress(); + uint8_t read8(); uint8_t read(const uint8_t pin); uint8_t value() const { return _dataIn; }; + void write8(const uint8_t value); void write(const uint8_t pin, const uint8_t value); uint8_t valueOut() const { return _dataOut; } + //added 0.1.07/08 Septillion inline uint8_t readButton8() { return PCF8574::readButton8(_buttonMask); } uint8_t readButton8(const uint8_t mask); uint8_t readButton(const uint8_t pin); inline void setButtonMask(const uint8_t mask) { _buttonMask = mask; }; + uint8_t getButtonMask() { return _buttonMask; }; + // rotate, shift, toggle, reverse expect all lines are output void toggle(const uint8_t pin); @@ -67,8 +73,10 @@ class PCF8574 void rotateLeft(const uint8_t n = 1); void reverse(); + int lastError(); + private: uint8_t _address; uint8_t _dataIn; diff --git a/README.md b/README.md index c694889..2c4a93e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Arduino library for PCF8574 - 8 channel I2C IO expander Related to the PCF8575 16 channel IO expander library https://github.com/RobTillaart/PCF8575 This library gives easy control over the 8 pins of a PCF8574 and PCF8574A chip. -These chips are identical in behavior although there are two distinct address ranges. +These chips are identical in behaviour although there are two distinct address ranges. | TYPE | ADDRESS-RANGE | notes | |:---------|:-------------:|:------------------------:| @@ -28,15 +28,13 @@ to 16 x 8 = 128 IO lines. To maximize IO lines combine 8 x PCF8575 + 8 x PCF8574 128 + 64 = 192 IO lines. Be sure to have a well dimensioned power supply. The library allows to read and write both single pins or 8 pins at once. -Furthermore some additional functions are implemented that are -playful but useful. +Furthermore some additional functions are implemented that are playful but useful. ## Interface **PCF8574_INITIAL_VALUE** is a define that can be set compile time or before -the include of "pcf8574.h" to overrule the default value used with the -**begin()** call. +the include of "pcf8574.h" to overrule the default value used with the **begin()** call. ### Constructor @@ -46,7 +44,9 @@ and optional the Wire interface as parameter. - **bool begin(uint8_t val = PCF8574_INITIAL_VALUE)** set the initial value for the pins and masks. - **bool begin(uint8_t sda, uint8_t scl, uint8_t val = PCF8574_INITIAL_VALUE)** idem, for the ESP32 where one can choose the I2C pins. - **bool isConnected()** checks if the address set in the constructor or by **setAddress()** is visible on the I2C bus. -- **bool setAddress(const uint8_t deviceAddress)** sets the device address after construction. Can be used to switch between PCF8574 modules runtime. Note this corrupts internal buffered values, so one might need to call **read8()** and/or **write8()**. Returns true if address can be found on I2C bus. +- **bool setAddress(const uint8_t deviceAddress)** sets the device address after construction. +Can be used to switch between PCF8574 modules runtime. Note this corrupts internal buffered values, +so one might need to call **read8()** and/or **write8()**. Returns true if address can be found on I2C bus. - **uint8_t getAddress()** returns the device address. @@ -56,17 +56,27 @@ and optional the Wire interface as parameter. - **uint8_t read(uint8_t pin)** reads a single pin; pin = 0..7 - **uint8_t value()** returns the last read inputs again, as this information is buffered in the class this is faster than reread the pins. -- **void write8(const uint8_t value)** writes all 8 pins at once. This one does the actual reading. -- **uint8_t write(const uint8_t pin, const uint8_t value)** writes a single pin; pin = 0..7; value is HIGH(1) or LOW (0) +- **void write8(const uint8_t value)** writes all 8 pins at once. This one does the actual writing. +- **uint8_t write(const uint8_t pin, const uint8_t value)** writes a single pin; pin = 0..7; +value is HIGH(1) or LOW (0) - **valueOut()** returns the last written data. ### Button -- **void setButtonMask(const uint8_t mask)** -- **uint8_t readButton8()** -- **uint8_t readButton8(const uint8_t mask)** -- **uint8_t readButton(const uint8_t pin)** +The **"button"** functions are to be used when you mix input and output on one IC. +It does not change / affect the pins used for output by masking these. +Typical usage is to call **setButtonMask()** once in setup as pins do not (often) change +during program execution. + +- **void setButtonMask(const uint8_t mask)** sets the (bit) mask which lines are input. +- **uint8_t getButtonMask()** returns the set buttonMask. +- **uint8_t readButton8()** use the mask set by setButtonMask to select specific input pins. +- **uint8_t readButton8(const uint8_t mask)** use a specific mask to select specific input pins. +Note this can be a subset of the pins set with **setButtonMask()** if one wants to process not all. +- **uint8_t readButton(const uint8_t pin)** read a singe input pin. + +Background - https://github.com/RobTillaart/Arduino/issues/38 ### Special @@ -74,13 +84,13 @@ in the class this is faster than reread the pins. - **void toggle(const uint8_t pin)** toggles a single pin - **void toggleMask(const uint8_t mask = 0xFF)** toggles a selection of pins, if you want to invert all pins use 0xFF (default value). -- **void shiftRight(const uint8_t n = 1)** shifts output channels n pins (default 1) pins right (e.g. leds ). +- **void shiftRight(const uint8_t n = 1)** shifts output channels n pins (default 1) pins right (e.g. LEDs ). Fills the higher lines with zero's. -- **void shiftLeft(const uint8_t n = 1)** shifts output channels n pins (default 1) pins left (e.g. leds ). +- **void shiftLeft(const uint8_t n = 1)** shifts output channels n pins (default 1) pins left (e.g. LEDs ). Fills the lower lines with zero's. - **void rotateRight(const uint8_t n = 1)** rotates output channels to right, moving lowest line to highest line. - **void rotateLeft(const uint8_t n = 1)** rotates output channels to left, moving highest line to lowest line. -- **void reverse()** reverse the "bit pattern" of the lines, high to low and vice versa. +- **void reverse()** reverse the "bit pattern" of the lines, swapping pin 7 with 0, 6 with 1, 5 with 2 and 4 with 3. ### Misc @@ -101,7 +111,11 @@ Fills the lower lines with zero's. See examples +It is advised to use pull-up or pull-down resistors so the lines have a defined state at startup. + ## Future -- \ No newline at end of file +- update documentation +- link to related libraries. + diff --git a/examples/PCF8574_Wire2/PCF8574_Wire2.ino b/examples/PCF8574_Wire2/PCF8574_Wire2.ino index b21b9ec..9ab3a5b 100644 --- a/examples/PCF8574_Wire2/PCF8574_Wire2.ino +++ b/examples/PCF8574_Wire2/PCF8574_Wire2.ino @@ -2,15 +2,16 @@ // FILE: PCF8574_Wire2.ino // AUTHOR: Rob Tillaart // DATE: 2016-04-30 -// // PUPROSE: demo // + #include "PCF8574.h" // adjust addresses if needed PCF8574 PCF(0x39, &Wire2); + void setup() { Serial.begin(115200); @@ -34,6 +35,7 @@ void setup() delay(1000); } + void loop() { Serial.println("HLT"); @@ -46,6 +48,7 @@ void loop() } } + void doHigh() { PCF.write(4, HIGH); @@ -54,6 +57,7 @@ void doHigh() Serial.println(x, HEX); } + void doLow() { PCF.write(4, LOW); @@ -62,6 +66,7 @@ void doLow() Serial.println(x, HEX); } + void doToggle() { PCF.toggle(4); @@ -72,3 +77,4 @@ void doToggle() // -- END OF FILE -- + diff --git a/examples/PCF8574_interrupt/PCF8574_interrupt.ino b/examples/PCF8574_interrupt/PCF8574_interrupt.ino index ea2ede4..8b7bb44 100644 --- a/examples/PCF8574_interrupt/PCF8574_interrupt.ino +++ b/examples/PCF8574_interrupt/PCF8574_interrupt.ino @@ -12,10 +12,12 @@ // Place a pull up resistor 4K7 between pin and 5V // Place a capacitor 10-400pF between pin and GND + #include "PCF8574.h" PCF8574 PCF(0x38); + //////////////////////////////////// // // INTERRUPT ROUTINE + FLAG @@ -47,6 +49,7 @@ void setup() attachInterrupt(digitalPinToInterrupt(IRQPIN), pcf_irq, FALLING); } + void loop() { uint32_t now = millis(); @@ -60,8 +63,9 @@ void loop() Serial.print('\t'); Serial.println(x, HEX); } - // do other things here + // do other things here delay(10); } + // -- END OF FILE -- diff --git a/examples/PCF8574_isConnected/PCF8574_isConnected.ino b/examples/PCF8574_isConnected/PCF8574_isConnected.ino index 194510d..37de345 100644 --- a/examples/PCF8574_isConnected/PCF8574_isConnected.ino +++ b/examples/PCF8574_isConnected/PCF8574_isConnected.ino @@ -2,15 +2,16 @@ // FILE: PCF8574_isConnected.ino // AUTHOR: Rob Tillaart // DATE: 2021-01-03 +// PUPROSE: demo isConnected function // -// PUPROSE: demo -// + #include "PCF8574.h" // adjust addresses if needed PCF8574 PCF_39(0x39); + void setup() { Serial.begin(115200); @@ -32,8 +33,10 @@ void setup() } } + void loop() { } + // -- END OF FILE -- diff --git a/examples/PCF8574_performance/PCF8574_performance.ino b/examples/PCF8574_performance/PCF8574_performance.ino index 4bb0a6f..4aaa26a 100644 --- a/examples/PCF8574_performance/PCF8574_performance.ino +++ b/examples/PCF8574_performance/PCF8574_performance.ino @@ -2,8 +2,8 @@ // FILE: PCF8574_performance.ino // AUTHOR: Rob Tillaart // DATE: 2021-01-24 -// -// PUPROSE: test PCF8574 library +// PUPROSE: test PCF8574 library at different I2C speeds. + #include "PCF8574.h" @@ -11,6 +11,7 @@ PCF8574 PCF(0x38); uint32_t start, stop; + void setup() { Serial.begin(115200); @@ -39,10 +40,13 @@ void setup() Serial.println(stop - start); delay(1000); } - } + void loop() { - } + + +// -- END OF FILE -- + diff --git a/examples/PCF8574_rotaryEncoder/PCF8574_rotaryEncoder.ino b/examples/PCF8574_rotaryEncoder/PCF8574_rotaryEncoder.ino index 5c0b630..cd204f5 100644 --- a/examples/PCF8574_rotaryEncoder/PCF8574_rotaryEncoder.ino +++ b/examples/PCF8574_rotaryEncoder/PCF8574_rotaryEncoder.ino @@ -30,6 +30,8 @@ uint8_t lastpos[4] = {0, 0, 0, 0}; int32_t encoder[4] = {0, 0, 0, 0}; volatile bool flag = false; + +// IRQ routine void moved() { flag = true; @@ -50,7 +52,8 @@ void setup() Wire.begin(); if (decoder.begin() == false) { - Serial.println("\nERROR: cannot communicate to keypad.\nPlease reboot / adjust address.\n"); + Serial.println("\nERROR: cannot communicate to PCF8574."); + Serial.println("Please reboot / adjust address.\n"); while (1); } Wire.setClock(600000); diff --git a/examples/PCF8574_test/PCF8574_test.ino b/examples/PCF8574_test/PCF8574_test.ino index 0c35ac1..0f13e17 100644 --- a/examples/PCF8574_test/PCF8574_test.ino +++ b/examples/PCF8574_test/PCF8574_test.ino @@ -3,14 +3,15 @@ // FILE: PCF8574_test.ino // AUTHOR: Rob Tillaart // DATE: 7-febr-2013 -// // PUPROSE: test PCF8574 library // + #include "PCF8574.h" PCF8574 PCF_01(0x38); + void setup() { Serial.begin(115200); @@ -26,6 +27,7 @@ void setup() delay(1000); } + void loop() { Serial.println("HLT"); @@ -38,6 +40,7 @@ void loop() } } + void doHigh() { PCF_01.write(4, HIGH); @@ -46,6 +49,7 @@ void doHigh() Serial.println(x, HEX); } + void doLow() { PCF_01.write(4, LOW); @@ -54,6 +58,7 @@ void doLow() Serial.println(x, HEX); } + void doToggle() { PCF_01.toggle(4); @@ -61,3 +66,7 @@ void doToggle() Serial.print("Read "); Serial.println(x, HEX); } + + +// -- END OF FILE -- + diff --git a/examples/PCF8574_test1/PCF8574_test1.ino b/examples/PCF8574_test1/PCF8574_test1.ino index e284407..f092f7c 100644 --- a/examples/PCF8574_test1/PCF8574_test1.ino +++ b/examples/PCF8574_test1/PCF8574_test1.ino @@ -2,7 +2,6 @@ // FILE: pcf8574_test.ino // AUTHOR: Rob Tillaart // DATE: 27-08-2013 -// // PUPROSE: demo // @@ -10,7 +9,8 @@ // adjust addresses if needed PCF8574 PCF_38(0x38); // add switches to lines (used as input) -PCF8574 PCF_39(0x39); // add leds to lines (used as output) +PCF8574 PCF_39(0x39); // add LEDs to lines (used as output) + void setup() { @@ -62,12 +62,15 @@ void setup() } } + void loop() { - // echos the lines + // echo the state of the lines on the other PCF uint8_t value = PCF_38.read8(); PCF_39.write8(value); delay(100); } + // END OF FILE + diff --git a/examples/PCF8574_test2/PCF8574_test2.ino b/examples/PCF8574_test2/PCF8574_test2.ino index 90dcb3a..783d4f7 100644 --- a/examples/PCF8574_test2/PCF8574_test2.ino +++ b/examples/PCF8574_test2/PCF8574_test2.ino @@ -2,14 +2,15 @@ // FILE: pcf8574_test2.ino // AUTHOR: Rob Tillaart // DATE: 2016-04-30 -// // PUPROSE: demo rotateLeft, -Right and toggleMask // + #include "PCF8574.h" // adjust addresses if needed -PCF8574 PCF_39(0x39); // add leds to lines (used as output) +PCF8574 PCF_39(0x39); // add LEDs to lines (used as output) + void setup() { @@ -61,8 +62,11 @@ void setup() } } + void loop() { } + // -- END OF FILE -- + diff --git a/examples/buttonRead/buttonRead.ino b/examples/buttonRead/buttonRead.ino index e07e0e4..62c5811 100644 --- a/examples/buttonRead/buttonRead.ino +++ b/examples/buttonRead/buttonRead.ino @@ -24,9 +24,11 @@ * no matter the set output state when you press the button. */ + #include #include + PCF8574 pcf20(0x20); const byte onboardLed = 13; @@ -35,6 +37,7 @@ const byte PcfButtonLedPin = 0; unsigned int blinkMillis; unsigned int buttonMillis; + void setup() { Serial.begin(115200); pcf20.begin(); @@ -42,6 +45,7 @@ void setup() { pinMode(onboardLed, OUTPUT); } + void loop() { static bool state; unsigned int currentMillis = millis(); @@ -71,3 +75,7 @@ void loop() { Serial.println(pcf20.read8(), BIN); } } + + +// -- END OF FILE -- + diff --git a/examples/buttonRead8/buttonRead8.ino b/examples/buttonRead8/buttonRead8.ino index 0ac80d4..10d5502 100644 --- a/examples/buttonRead8/buttonRead8.ino +++ b/examples/buttonRead8/buttonRead8.ino @@ -24,9 +24,11 @@ * no matter the set output state when you press the button. */ + #include #include + PCF8574 pcf20(0x20); const byte onboardLed = 13; @@ -36,50 +38,52 @@ const byte PcfLedPin = 1; unsigned int blinkMillis; unsigned int buttonMillis; + void setup() { Serial.begin(115200); pcf20.begin(); pinMode(onboardLed, OUTPUT); - //As alternative to adding the mask to buttonRead8() every time - //you can set it once. - //Without setting a mask buttonRead8() will effect ALL pins. - //Not a problem when using things like LEDs. - //pcf20.setButtonMask(_BV(PcfButtonLedPin)); + // As alternative to adding the mask to buttonRead8() every time + // you can set it once. + // Without setting a mask buttonRead8() will effect ALL pins. + // Not a problem when using things like LEDs. + // pcf20.setButtonMask(_BV(PcfButtonLedPin)); } + void loop() { static bool state; unsigned int currentMillis = millis(); - //Limit button read to 20 times a second - //Fast enough for most buttons - //but this way you don't have a dimmer output because it's blanked during button read - //a read takes 460us t 16Mhz Arduino and normal I2C speed. + // Limit button read to 20 times a second + // Fast enough for most buttons + // but this way you don't have a dimmer output because it's blanked during button read + // a read takes 460us t 16Mhz Arduino and normal I2C speed. if(currentMillis - buttonMillis >= 50){ buttonMillis = currentMillis; - //read all states but only force PcfButtonLedPin HIGH during the - //buttonRead8() - //Alternativly the mask could have been set with setButtonMask(). - //Then the mask can be omitted here. See setup() - // byte inputStates = pcf20.readButton8(_BV(PcfButtonLedPin)); + // read all states but only force PcfButtonLedPin HIGH during the + // buttonRead8() + // Alternatively the mask could have been set with setButtonMask(). + // Then the mask can be omitted here. See setup() + // byte inputStates = pcf20.readButton8(_BV(PcfButtonLedPin)); byte inputStates = pcf20.readButton8(1 << PcfButtonLedPin); // Keep Arduino-CI happy - //check the bit of PcfButtonLedPin + // check the bit of PcfButtonLedPin if(state != bitRead(inputStates, PcfButtonLedPin)){ if(state){ - //toggle the LED + // toggle the LED digitalWrite(onboardLed, !digitalRead(onboardLed)); } state = !state; } } - //Lets blink the same output + // Lets blink the same output if(currentMillis - blinkMillis >= 500){ - //Update time + // Update time blinkMillis = currentMillis; pcf20.toggle(PcfButtonLedPin); @@ -87,3 +91,7 @@ void loop() { Serial.println(pcf20.read8(), BIN); } } + + +// -- END OF FILE -- +