Files
2024-07-20 22:09:06 +08:00

283 lines
5.7 KiB
C++

/*
* See header file for details
*
* This program is free software: you can redistribute it and/or modify\n
* it under the terms of the GNU General Public License as published by\n
* the Free Software Foundation, either version 3 of the License, or\n
* (at your option) any later version.\n
*
* This program is distributed in the hope that it will be useful,\n
* but WITHOUT ANY WARRANTY; without even the implied warranty of\n
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n
* GNU General Public License for more details.\n
*
* You should have received a copy of the GNU General Public License\n
* along with this program. If not, see <http://www.gnu.org/licenses/>.\n
*/
/* Dependencies */
#include <SoftwareWire.h>
#include "PCF8574_Soft.h"
#ifdef PCF8574_Soft_INTERRUPT_SUPPORT
#include "PCint.h"
#endif
PCF8574_Soft::PCF8574_Soft() :
_PORT(0), _PIN(0), _DDR(0), _address(0)
#ifdef PCF8574_Soft_INTERRUPT_SUPPORT
, _oldPIN(0), _isrIgnore(0), _pcintPin(0), _intMode(), _intCallback()
#endif
{
}
void PCF8574_Soft::begin(uint8_t address, SoftwareWire *theWire) {
/* Store the I2C address and init the Wire library */
_wire = theWire;
_address = address;
_wire->begin();
readGPIO();
}
void PCF8574_Soft::pinMode(uint8_t pin, uint8_t mode) {
/* Switch according mode */
switch (mode) {
case INPUT:
_DDR &= ~(1 << pin);
_PORT &= ~(1 << pin);
break;
case INPUT_PULLUP:
_DDR &= ~(1 << pin);
_PORT |= (1 << pin);
break;
case OUTPUT:
_DDR |= (1 << pin);
_PORT &= ~(1 << pin);
break;
default:
break;
}
/* Update GPIO values */
updateGPIO();
}
void PCF8574_Soft::digitalWrite(uint8_t pin, uint8_t value) {
/* Set PORT bit value */
if (value)
_PORT |= (1 << pin);
else
_PORT &= ~(1 << pin);
/* Update GPIO values */
updateGPIO();
}
uint8_t PCF8574_Soft::digitalRead(uint8_t pin) {
/* Read GPIO */
readGPIO();
#ifdef PCF8574_Soft_INTERRUPT_SUPPORT
/* Check for interrupt (manual detection) */
//checkForInterrupt();
#endif
/* Read and return the pin state */
return (_PIN & (1 << pin)) ? HIGH : LOW;
}
void PCF8574_Soft::write(uint8_t value) {
/* Store pins values and apply */
_PORT = value;
/* Update GPIO values */
updateGPIO();
}
uint8_t PCF8574_Soft::read() {
/* Read GPIO */
readGPIO();
#ifdef PCF8574_Soft_INTERRUPT_SUPPORT
/* Check for interrupt (manual detection) */
//checkForInterrupt();
#endif
/* Return current pins values */
return _PIN;
}
void PCF8574_Soft::pullUp(uint8_t pin) {
/* Same as pinMode(INPUT_PULLUP) */
pinMode(pin, INPUT_PULLUP); // /!\ pinMode form THE LIBRARY
}
void PCF8574_Soft::pullDown(uint8_t pin) {
/* Same as pinMode(INPUT) */
pinMode(pin, INPUT); // /!\ pinMode form THE LIBRARY
}
void PCF8574_Soft::clear() {
/* User friendly wrapper for write() */
write(0x00);
}
void PCF8574_Soft::set() {
/* User friendly wrapper for write() */
write(0xFF);
}
void PCF8574_Soft::toggle(uint8_t pin) {
/* Toggle pin state */
_PORT ^= (1 << pin);
/* Update GPIO values */
updateGPIO();
}
void PCF8574_Soft::blink(uint8_t pin, uint16_t count, uint32_t duration) {
/* Compute steps duration */
duration /= count * 2;
/* Loop n times */
while (count--) {
/* Toggle pin 2 times */
toggle(pin);
delay(duration);
toggle(pin);
delay(duration);
}
}
#ifdef PCF8574_Soft_INTERRUPT_SUPPORT
void PCF8574_Soft::enableInterrupt(uint8_t pin, void (*selfCheckFunction)(void)) {
/* Store interrupt pin number */
_pcintPin = pin;
/* Setup interrupt pin */
#if ARDUINO >= 100
::pinMode(pin, INPUT_PULLUP); // /!\ pinMode form THE ARDUINO CORE
#else
::pinMode(pin, INPUT); // /!\ pinMode form THE ARDUINO CORE
::digitalWrite(pin, HIGH); // /!\ digitalWrite form THE ARDUINO CORE
#endif
/* Attach interrupt handler */
PCattachInterrupt(pin, selfCheckFunction, FALLING);
}
void PCF8574_Soft::disableInterrupt() {
/* Detach interrupt handler */
PCdetachInterrupt(_pcintPin);
}
void PCF8574_Soft::checkForInterrupt() {
/* Avoid nested interrupt triggered by I2C read/write */
if(_isrIgnore)
return;
else
_isrIgnore = 1;
/* Re-enable interrupts to allow Wire library to work */
sei();
/* Read current pins values */
readGPIO();
/* Check all pins */
for (uint8_t i = 0; i < 8; ++i) {
/* Check for interrupt handler */
if (!_intCallback[i])
continue;
/* Check for interrupt event */
switch (_intMode[i]) {
case CHANGE:
if ((1 << i) & (_PIN ^ _oldPIN))
_intCallback[i]();
break;
case LOW:
if (!(_PIN & (1 << i)))
_intCallback[i]();
break;
case FALLING:
if ((_oldPIN & (1 << i)) && !(_PIN & (1 << i)))
_intCallback[i]();
break;
case RISING:
if (!(_oldPIN & (1 << i)) && (_PIN & (1 << i)))
_intCallback[i]();
break;
}
}
/* Turn off ISR ignore flag */
_isrIgnore = 0;
}
void PCF8574_Soft::attachInterrupt(uint8_t pin, void (*userFunc)(void),
uint8_t mode) {
/* Store interrupt mode and callback */
_intMode[pin] = mode;
_intCallback[pin] = userFunc;
}
void PCF8574_Soft::detachInterrupt(uint8_t pin) {
/* Void interrupt handler */
_intCallback[pin] = 0;
}
#endif
void PCF8574_Soft::readGPIO() {
#ifdef PCF8574_Soft_INTERRUPT_SUPPORT
/* Store old _PIN value */
_oldPIN = _PIN;
#endif
/* Start request, wait for data and receive GPIO values as byte */
_wire->requestFrom(_address, (uint8_t) 0x01);
while (_wire->available() < 1)
;
_PIN = _wire->read();
}
void PCF8574_Soft::updateGPIO() {
/* Read current GPIO states */
//readGPIO(); // Experimental
/* Compute new GPIO states */
//uint8_t value = ((_PIN & ~_DDR) & ~(~_DDR & _PORT)) | _PORT; // Experimental
uint8_t value = (_PIN & ~_DDR) | _PORT;
/* Start communication and send GPIO values as byte */
_wire->beginTransmission(_address);
_wire->write(value);
_wire->endTransmission();
}