/* * 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 .\n */ /* Dependencies */ #include #include "PCF8574.h" #ifdef PCF8574_INTERRUPT_SUPPORT #include "PCint.h" #endif PCF8574::PCF8574() : _PORT(0), _PIN(0), _DDR(0), _address(0) #ifdef PCF8574_INTERRUPT_SUPPORT , _oldPIN(0), _isrIgnore(0), _pcintPin(0), _intMode(), _intCallback() #endif { } void PCF8574::begin(uint8_t address) { /* Store the I2C address and init the Wire library */ _address = address; Wire.begin(); readGPIO(); } void PCF8574::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::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::digitalRead(uint8_t pin) { /* Read GPIO */ readGPIO(); #ifdef PCF8574_INTERRUPT_SUPPORT /* Check for interrupt (manual detection) */ //checkForInterrupt(); #endif /* Read and return the pin state */ return (_PIN & (1 << pin)) ? HIGH : LOW; } void PCF8574::write(uint8_t value) { /* Store pins values and apply */ _PORT = value; /* Update GPIO values */ updateGPIO(); } uint8_t PCF8574::read() { /* Read GPIO */ readGPIO(); #ifdef PCF8574_INTERRUPT_SUPPORT /* Check for interrupt (manual detection) */ //checkForInterrupt(); #endif /* Return current pins values */ return _PIN; } void PCF8574::pullUp(uint8_t pin) { /* Same as pinMode(INPUT_PULLUP) */ pinMode(pin, INPUT_PULLUP); // /!\ pinMode form THE LIBRARY } void PCF8574::pullDown(uint8_t pin) { /* Same as pinMode(INPUT) */ pinMode(pin, INPUT); // /!\ pinMode form THE LIBRARY } void PCF8574::clear() { /* User friendly wrapper for write() */ write(0x00); } void PCF8574::set() { /* User friendly wrapper for write() */ write(0xFF); } void PCF8574::toggle(uint8_t pin) { /* Toggle pin state */ _PORT ^= (1 << pin); /* Update GPIO values */ updateGPIO(); } void PCF8574::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_INTERRUPT_SUPPORT void PCF8574::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::disableInterrupt() { /* Detach interrupt handler */ PCdetachInterrupt(_pcintPin); } void PCF8574::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::attachInterrupt(uint8_t pin, void (*userFunc)(void), uint8_t mode) { /* Store interrupt mode and callback */ _intMode[pin] = mode; _intCallback[pin] = userFunc; } void PCF8574::detachInterrupt(uint8_t pin) { /* Void interrupt handler */ _intCallback[pin] = 0; } #endif void PCF8574::readGPIO() { #ifdef PCF8574_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 = I2CREAD(); } void PCF8574::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); I2CWRITE(value); Wire.endTransmission(); }