Commit 466f71bc authored by papp's avatar papp
Browse files

Update

- SdFat aktualisiert
- PCF8575 Librarie hinzugefügt
parent 9112451e
MIT License
Copyright (c) 2020-2021 Rob Tillaart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
//
// FILE: PCF8575.cpp
// AUTHOR: Rob Tillaart
// DATE: 2020-07-20
// VERSION: 0.1.2
// PURPOSE: Arduino library for PCF8575 - 16 channel I2C IO expander
// URL: https://github.com/RobTillaart/PCF8575
//
// HISTORY:
// 0.0.1 2020-07-20 initial version
// 0.0.2 2020-07-21 fix reverse(); refactor;
// 0.0.3 2020-07-29 fix #5 reverse() + refactor.
// 0.1.0 2021-01-03 add Arduino-CI + unit tests
// 0.1.1 2021-04-23 fix for platformIO compatibility
// 0.1.2 2021-07-09 fix #10 add set/getAddress() function
#include "PCF8575.h"
PCF8575::PCF8575(const uint8_t deviceAddress, TwoWire *wire)
{
_address = deviceAddress;
_wire = wire;
_dataIn = 0;
_dataOut = 0xFFFF;
_buttonMask = 0xFFFF;
_error = PCF8575_OK;
}
#if defined (ESP8266) || defined(ESP32)
bool PCF8575::begin(uint8_t dataPin, uint8_t clockPin, uint16_t val)
{
_wire = &Wire;
if ((dataPin < 255) && (clockPin < 255))
{
_wire->begin(dataPin, clockPin);
} else {
_wire->begin();
}
if (! isConnected()) return false;
PCF8575::write16(val);
return true;
}
#endif
bool PCF8575::begin(uint16_t val)
{
_wire->begin();
if (! isConnected()) return false;
PCF8575::write16(val);
return true;
}
bool PCF8575::isConnected()
{
_wire->beginTransmission(_address);
return ( _wire->endTransmission() == 0);
}
bool PCF8575::setAddress(const uint8_t deviceAddress)
{
_address = deviceAddress;
return isConnected();
}
uint8_t PCF8575::getAddress()
{
return _address;
}
uint16_t PCF8575::read16()
{
if (_wire->requestFrom(_address, (uint8_t)2) != 2)
{
_error = PCF8575_I2C_ERROR;
return _dataIn; // last value
}
_dataIn = _wire->read(); // low 8 bits
_dataIn |= (_wire->read() << 8); // high 8 bits
return _dataIn;
}
void PCF8575::write16(const uint16_t value)
{
_dataOut = value;
_wire->beginTransmission(_address);
_wire->write(_dataOut & 0xFF); // low 8 bits
_wire->write(_dataOut >> 8); // high 8 bits
_error = _wire->endTransmission();
}
uint8_t PCF8575::read(const uint8_t pin)
{
if (pin > 15)
{
_error = PCF8575_PIN_ERROR;
return 0;
}
PCF8575::read16();
return (_dataIn & (1 << pin)) > 0;
}
void PCF8575::write(const uint8_t pin, const uint8_t value)
{
if (pin > 15)
{
_error = PCF8575_PIN_ERROR;
return;
}
if (value == LOW)
{
_dataOut &= ~(1 << pin);
}
else
{
_dataOut |= (1 << pin);
}
write16(_dataOut);
}
void PCF8575::toggle(const uint8_t pin)
{
if (pin > 15)
{
_error = PCF8575_PIN_ERROR;
return;
}
toggleMask(1 << pin);
}
void PCF8575::toggleMask(const uint16_t mask)
{
_dataOut ^= mask;
PCF8575::write16(_dataOut);
}
void PCF8575::shiftRight(const uint8_t n)
{
if ((n == 0) || (_dataOut == 0)) return;
if (n > 15) _dataOut = 0; // shift 8++ clears all, valid...
if (_dataOut != 0) _dataOut >>= n; // only shift if there are bits set
PCF8575::write16(_dataOut);
}
void PCF8575::shiftLeft(const uint8_t n)
{
if ((n == 0) || (_dataOut == 0)) return;
if (n > 15) _dataOut = 0; // shift 8++ clears all, valid...
if (_dataOut != 0) _dataOut <<= n; // only shift if there are bits set
PCF8575::write16(_dataOut);
}
int PCF8575::lastError()
{
int e = _error;
_error = PCF8575_OK; // reset error after read, is this wise?
return e;
}
void PCF8575::rotateRight(const uint8_t n)
{
uint8_t r = n & 15;
if (r == 0) return;
_dataOut = (_dataOut >> r) | (_dataOut << (15 - r));
PCF8575::write16(_dataOut);
}
void PCF8575::rotateLeft(const uint8_t n)
{
rotateRight(16 - (n & 15));
}
void PCF8575::reverse() // quite fast
{ // 1 char === 1 bit
uint16_t x = _dataOut; // x = 0123456789ABCDEF
x = (((x & 0xAAAA) >> 1) | ((x & 0x5555) << 1)); // x = 1032547698BADCFE
x = (((x & 0xCCCC) >> 2) | ((x & 0x3333) << 2)); // x = 32107654BA98FEDC
x = (((x & 0xF0F0) >> 4) | ((x & 0x0F0F) << 4)); // x = 76543210FEDCBA98
x = (x >> 8) | ( x << 8); // x = FEDCBA9876543210
PCF8575::write16(x);
}
//added 0.1.07/08 Septillion
uint16_t PCF8575::readButton16(const uint16_t mask)
{
uint16_t temp = _dataOut;
PCF8575::write16(mask | _dataOut); // read only selected lines
PCF8575::read16();
PCF8575::write16(temp); // restore
return _dataIn;
}
//added 0.1.07 Septillion
uint8_t PCF8575::readButton(const uint8_t pin)
{
if (pin > 15)
{
_error = PCF8575_PIN_ERROR;
return 0;
}
uint16_t temp = _dataOut;
PCF8575::write(pin, HIGH);
uint8_t rtn = PCF8575::read(pin);
PCF8575::write16(temp);
return rtn;
}
// -- END OF FILE --
#pragma once
//
// FILE: PCF8575.h
// AUTHOR: Rob Tillaart
// DATE: 2020-07-20
// VERSION: 0.1.2
// PURPOSE: Arduino library for PCF8575 - 16 channel I2C IO expander
// URL: https://github.com/RobTillaart/PCF8575
//
// HISTORY:
// see PCF8575.cpp file
//
#include "Arduino.h"
#include "Wire.h"
#define PCF8575_LIB_VERSION (F("0.1.2"))
#ifndef PCF8575_INITIAL_VALUE
#define PCF8575_INITIAL_VALUE 0xFFFF
#endif
#define PCF8575_OK 0x00
#define PCF8575_PIN_ERROR 0x81
#define PCF8575_I2C_ERROR 0x82
class PCF8575
{
public:
// deviceAddress base = 0x20 + depends on address bits
explicit PCF8575(const uint8_t deviceAddress, TwoWire *wire = &Wire);
#if defined (ESP8266) || defined(ESP32)
bool begin(uint8_t sda, uint8_t scl, uint16_t val = PCF8575_INITIAL_VALUE);
#endif
bool begin(uint16_t val = PCF8575_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();
uint16_t read16();
uint8_t read(uint8_t pin);
uint16_t value() const { return _dataIn; };
void write16(const uint16_t value);
void write(const uint8_t pin, const uint8_t value);
uint16_t valueOut() const { return _dataOut; }
// added 0.1.07/08 Septillion
uint16_t readButton16() { return readButton16(_buttonMask); }
uint16_t readButton16(const uint16_t mask);
uint8_t readButton(const uint8_t pin);
void setButtonMask(uint16_t mask) { _buttonMask = mask; };
// rotate, shift, toggle, reverse expect all lines are output
void toggle(const uint8_t pin);
void toggleMask(const uint16_t mask = 0xFFFF); // default invertAll()
void shiftRight(const uint8_t n = 1);
void shiftLeft(const uint8_t n = 1);
void rotateRight(const uint8_t n = 1);
void rotateLeft(const uint8_t n = 1);
void reverse();
int lastError();
private:
uint8_t _address;
uint16_t _dataIn;
uint16_t _dataOut;
uint16_t _buttonMask;
int _error;
TwoWire* _wire;
};
// -- END OF FILE --
[![Arduino CI](https://github.com/RobTillaart/PCF8575/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/PCF8575/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/RobTillaart/PCF8575.svg?maxAge=3600)](https://github.com/RobTillaart/PCF8575/releases)
# PCF8575
Arduino library for PCF8575 - 16 channel I2C IO expander
## Description
Related to the PCF8574 8 channel IO expander library https://github.com/RobTillaart/PCF8574
The library gives easy control over the 16 pins of the PCF8575 chips.
Base address = 0x20 + 0..7 depending on address pins A0..A2
| TYPE | ADDRESS-RANGE | notes |
|:---------|:-------------:|:------------------------:|
|PCF8575 | 0x20 to 0x27 | same range as PCF8574 !! |
| | | |
So you can connect up to 8 PCF8575 on one I2C bus, giving access
to 8 x 16 = 128 IO lines.
To maximize IO lines combine 8 x PCF8575 + 8 x PCF8574A giving
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 16 pins at once.
Furthermore some additional functions are implemented that are
playful but useful.
## I2C Clock
Testing showed that the PCF8575 still works at 600 KHz and failed at 800 KHz.
These values are outside the specs of the datasheet so they are not recommended.
However when performance is needed you can try to overclock the chip.
## Interface
**PCF8575_INITIAL_VALUE** is a define that can be set compile time or before
the include of "pcf8575.h" to overrule the default value used with the
**begin()** call.
### Constructor
- **PCF8575(deviceAddress, TwoWire \*wire = &Wire)** Constructor with I2C device address,
and optional the Wire interface as parameter.
- **begin(val = PCF8575_INITIAL_VALUE)** set the initial value for the pins and masks.
- **begin(sda, scl, val = PCF8575_INITIAL_VALUE)** idem, for the ESP32 where one can choose the I2C pins
What needs to be added in the future is a parameter to choose another Wire interface
as some processors have multiple hardware Wire interfaces.
- **isConnected()** checks if the address is visible on the I2C bus
- **bool setAddress(const uint8_t deviceAddress)** sets the device address after construction.
Can be used to switch between PCF8575 modules runtime. Note this corrupts internal buffered values,
so one might need to call **read16()** and/or **write16()**. Returns true if address can be found on I2C bus.
- **uint8_t getAddress()** returns the device address.
### Read and Write
- **read16()** reads all 16 pins at once. This one does the actual reading.
- **read(pin)** reads a single pin; pin = 0..15
- **value()** returns the last read inputs again, as this information is buffered
in the class this is faster than reread the pins.
- **write16(value)** writes all 16 pins at once. This one does the actual reading.
- **write(pin, value)** writes a single pin; pin = 0..15; value is HIGH(1) or LOW (0)
- **valueOut()** returns the last written data.
### Button
- **setButtonMask(mask)**
- **readButton16()**
- **readButton16(mask)**
- **readButton(pin)**
### Special
- **toggle(pin)** toggles a single pin
- **toggleMask(mask)** toggles a selection of pins,
if you want to invert all pins use 0xFFFF (default value)
- **shiftRight(n = 1)** shifts output channels n pins (default 1) pins right (e.g. leds ).
Fills the higher lines with zero's.
- **shiftLeft(n = 1)** shifts output channels n pins (default 1) pins left (e.g. leds ).
Fills the lower lines with zero's.
- **rotateRight(n = 1)** rotates output channels to right, moving lowest line to highest line.
- **rotateLeft(n = 1)** rotates output channels to left, moving highest line to lowest line.
- **reverse()** revers the "bit pattern" of the lines, high to low and vice versa.
### Misc
- **lastError()** returns the last error from the lib. (see .h file)
## Error codes
| name | value | description |
|:-------------------|:-----:|:------------------------|
| PCF8574_OK | 0x00 | no error |
| PCF8574_PIN_ERROR | 0x81 | pin number out of range |
| PCF8574_I2C_ERROR | 0x82 | I2C communication error |
## Testing
Testing the initial library is done by Colin Mackay (thanks!).
Platforms used for testing include: Nano, ESP32 and Seeed Xiao
## Operation
See examples
//
// FILE: PCF8575_Wire2.ino
// AUTHOR: Rob Tillaart
// DATE: 2021-01-03
//
// PUPROSE: demo
//
#include "PCF8575.h"
// adjust addresses if needed
PCF8575 PCF(0x39, &Wire2);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("PCF8575_LIB_VERSION:\t");
Serial.println(PCF8575_LIB_VERSION);
if (!PCF.begin())
{
Serial.println("could not initialize...");
}
if (!PCF.isConnected())
{
Serial.println("=> not connected");
while(1);
}
int x = PCF.read16();
Serial.print("Read ");
Serial.println(x, HEX);
delay(1000);
}
void loop()
{
Serial.println("HLT");
while (Serial.available() == 0);
switch (Serial.read())
{
case 'H': doHigh(); break;
case 'L': doLow(); break;
case 'T': doToggle(); break;
}
}
void doHigh()
{
PCF.write(4, HIGH);
int x = PCF.read16();
Serial.print("Read ");
Serial.println(x, HEX);
}
void doLow()
{
PCF.write(4, LOW);
int x = PCF.read16();
Serial.print("Read ");
Serial.println(x, HEX);
}
void doToggle()
{
PCF.toggle(4);
int x = PCF.read16();
Serial.print("Read ");
Serial.println(x, HEX);
}
// -- END OF FILE --
//
// FILE: PCF8575_interrupt.ino
// AUTHOR: Rob Tillaart
// DATE: 2021-01-03
// PUPROSE: test PCF8575 library
//
// TEST SETUP
// Connect INT pin of the PCF8575 to UNO pin 2
//
// (from figure 4 datasheet
// Place a pull up resistor 4K7 between pin and 5V
// Place a capacitor 10-400pF between pin and GND
#include "PCF8575.h"
PCF8575 PCF(0x38);
////////////////////////////////////
//
// INTERRUPT ROUTINE + FLAG
//
const int IRQPIN = 2;
volatile bool flag = false;
void pcf_irq()
{
flag = true;
}
////////////////////////////////////
//
// MAIN CODE
//
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("PCF8575_LIB_VERSION: ");
Serial.println(PCF8575_LIB_VERSION);
PCF.begin();
pinMode(IRQPIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(IRQPIN), pcf_irq, FALLING);
}
void loop()
{
uint32_t now = millis();
if (flag)
{
flag = false;
uint16_t x = PCF.read16();
Serial.print("READ:\t");
Serial.print('\t');
Serial.print(now);
Serial.print('\t');
Serial.println(x, HEX);
}
// do other things here
delay(10);
}
// -- END OF FILE --
//
// FILE: PCF8575_isConnected.ino
// AUTHOR: Rob Tillaart
// DATE: 2021-01-03
//
// PUPROSE: demo
//