Complete PCF8574 and PCF74A library with interrupt handling
Dependents: Nucleo_PCF8574_led_iic Dawson_Controller
Revision 0:6d75da25c179, committed 2012-07-14
- Comitter:
- bborredon
- Date:
- Sat Jul 14 08:20:07 2012 +0000
- Commit message:
- [mbed] converted /I2c/PCF8574
Changed in this revision
PCF8574.cpp | Show annotated file Show diff for this revision Revisions of this file |
PCF8574.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r 6d75da25c179 PCF8574.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PCF8574.cpp Sat Jul 14 08:20:07 2012 +0000 @@ -0,0 +1,210 @@ +/*********************************************************** +Author: Bernard Borredon +Date: 27 december 2011 +Version: 1.0 +************************************************************/ +#include "PCF8574.h" + +// Local object address +static PCF8574 *pt_pcf8574; + +// User callback function address +static void (*_ptf)(uint8_t data,PCF8574 *o)= NULL; + +// Callback +static void _InterruptCB(void) +{ + uint8_t data = 0; + bitset<8> data_p,val; + bitset<8> changed; + uint8_t i; + + // Read PCF8574 port (to acknowledge interrupt) + data = pt_pcf8574->read(); + + // Set bits that have changed + pt_pcf8574->getIntrData(data_p,changed); + changed = 0; + val = data; + for(i = 0;i < 8;i++) { + if(val[i] != data_p[i]) + changed[i] = 1; + } + pt_pcf8574->setIntrData(val,changed); + + // Call user callback, if any with port value + if(_ptf != NULL) + _ptf(data,pt_pcf8574); +} + +// Constructor +PCF8574::PCF8574(PinName sda,PinName scl,uint8_t address, bool typeA) : _i2c(sda, scl) +{ + _errnum = PCF8574_NoError; + _intr = NULL; + pt_pcf8574 = NULL; + _intr_data = 0xFF; + _intr_bits_changed = 0; + _intr_flag = 0; + + // Set the address + if(typeA) { // PCF8574A + _PCF8574_address = 0x70; + } + else { // PCF8574 + _PCF8574_address = 0x40; + } + + _address = address; + + // Check address validity (between 0 and 7) + if(address > 7) { + _errnum = PCF8574_BadAddress; + } + + // Shift address + _address = _address << 1; + + // Set I2C frequency (100 MHz) + _i2c.frequency(100000); +} + +// Read IO port +uint8_t PCF8574::read(void) +{ + uint8_t data; + int ack; + + // Check error + if(_errnum) + return(0); + + // No error + _errnum = PCF8574_NoError; + + // Read port + ack = _i2c.read(_PCF8574_address | _address,(char *)&data,sizeof(data)); + + // Check error + if(ack != 0) { + _errnum = PCF8574_I2cError; + } + + return(data); +} + +// Write IO port +void PCF8574::write(uint8_t data) +{ + int ack; + + // Check error + if(_errnum) + return; + + // No error + _errnum = PCF8574_NoError; + + // Write port + ack = _i2c.write(_PCF8574_address | _address,(char *)&data,sizeof(data)); + + // Check error + if(ack != 0) { + _errnum = PCF8574_I2cError; + } +} + +// Write IO pin +void PCF8574::write(uint8_t pin, uint8_t value) +{ + uint8_t data; + + // Read IO port value + data = read(); + + // Change bit pin + if(value) + BIT_SET(data,pin); + else + BIT_CLEAR(data,pin); + + // Write new IO port data + write(data); +} + +// Write to IO pins with a bitset and a bitset mask +void PCF8574::write(bitset<8> data, bitset<8> mask) +{ + uint8_t i; + bitset<8> nd; + + nd = (bitset<8>) read(); + for(i = 0;i < 8;i++) { + if(mask[i]) + nd[i] = data[i]; + } + + write((uint8_t) nd.to_ulong()); +} + +// Set pin and callback interrupt +void PCF8574::interrupt(PinName intr,void (*ftpr)(uint8_t, PCF8574 *)) +{ + + // Check error + if(_errnum) + return; + + // No error + _errnum = PCF8574_NoError; + + // Create InterruptIn instance if needed + if(_intr == NULL) { + _intr = new InterruptIn(intr); + + // Set InterruptIn callback + if(_intr != NULL) + _intr->fall(_InterruptCB); + else + _errnum = PCF8574_IntrError; + } + + _ptf = ftpr; // User callback address + pt_pcf8574 = this; // Object instance + _intr_data = read(); // IO port value + _intr_flag = 1; // Function called +} + +// Get current error number +uint8_t PCF8574::getError(void) +{ + return(_errnum); +} + +// Memorize IO port value and bits that have changed when interrupt + void PCF8574::setIntrData(bitset<8> data, bitset<8> changed) + { + _intr_data = data; + _intr_bits_changed = changed; + } + + // Get IO port value and bits that have changed when interrupt + void PCF8574::getIntrData(bitset<8>& data, bitset<8>& changed) + { + data = _intr_data; + changed = _intr_bits_changed; + } + +// Redefine () operator +PCF8574::operator uint8_t() +{ + return read(); +} + +// Redefine = operator +PCF8574 &PCF8574::operator=(uint8_t data) +{ + write((int)data); + + return(*this); +} \ No newline at end of file
diff -r 000000000000 -r 6d75da25c179 PCF8574.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PCF8574.h Sat Jul 14 08:20:07 2012 +0000 @@ -0,0 +1,202 @@ +#ifndef PCF8574__H_ +#define PCF8574__H_ + +// Includes +#include <string> +#include <bitset> + +#include "mbed.h" + +// Example +/* +#include <string> + +#include "mbed.h" +#include "PCF8574.h" + +#define PCF8574_ADDR 0 // I2c PCF8574 address is 0x00 + +static void myerror(std::string msg) +{ + printf("Error %s\n",msg.c_str()); + exit(1); +} + +void pcf8574_it(uint8_t data, PCF8574 *o) +{ + printf("PCF8574 interrupt data = %02x\n",data); +} + +DigitalOut led2(LED2); + +int main() +{ + PCF8574 pcf(p9,p10,PCF8574_ADDR,true); // Declare PCF8574A i2c with sda = p9 and scl = p10 + uint8_t data; + + led2 = 0; + + // Set all IO port bits to 1 to enable inputs and test error + data = 0xFF; + pcf = data; + if(pcf.getError() != 0) + myerror(pcf.getErrorMessage()); + + // Assign interrupt function to pin 17 + pcf.interrupt(p17,&pcf8574_it); + + if(pcf.getError() != 0) + myerror(pcf.getErrorMessage()); + + // Get IO port (switch is used to flip bit 1) + while(1) { + wait(2.0); + data = pcf; + if(pcf.getError() != 0) + myerror(pcf.getErrorMessage()); + led2 = !led2; + } + + return(0); +} +*/ + +// Defines +#define PCF8574_NoError 0x00 +#define PCF8574_BadAddress 0x01 +#define PCF8574_I2cError 0x02 +#define PCF8574_IntrError 0x03 + +#define PCF8574_MaxError 4 + +#ifndef BIT_SET +#define BIT_SET(x,n) (x=x | (0x01<<n)) +#endif +#ifndef BIT_TEST +#define BIT_TEST(x,n) (x & (0x01<<n)) +#endif +#ifndef BIT_CLEAR +#define BIT_CLEAR(x,n) (x=x & ~(0x01<<n)) +#endif +#ifndef BIT_GET +#define BIT_GET(x,n) ((x >>n) & 0x1) +#endif + +static std::string _ErrorMessagePCF8574[PCF8574_MaxError] = { + "", + "Bad chip address", + "I2C error (nack)", + "Unable to create InterruptIn instance" + }; + +// Class +class PCF8574 { +public: + /* + * Constructor, initialize the PCF8574 on i2c interface. + * @param sda : sda i2c pin (PinName) + * @param scl : scl i2c pin (PinName) + * @param address : PCF8574 address between 0 and 7 (uint8_t) + * @param typeA : PCF8574A if true, default false (bool) + * @return none + */ + PCF8574(PinName sda, PinName scl, uint8_t address, bool typeA = false); + + /* + * Read the IO pins level + * @param : none + * @return port value (uint8_t) + */ + uint8_t read(void); + + /* + * Write to the IO pins + * @param data : The 8 bits to write to the IO port (uint8_t) + * @return none + */ + void write(uint8_t data); + + /* + * Write to individual IO pin + * @param pin : The pin number between 0 and 7 (uint8_t) + * @param value : The pin value (uint8_t) + * @return none + */ + void write(uint8_t pin, uint8_t value); + + /* + * Write to IO pins with a bitset and a bitset mask (only bits set in mask are written) + * @param data : The biset to write to the IO port (bitset<8>) + * @param mask : The biset to write mask (bitset<8>) + * @return none + */ + void write(bitset<8> data, bitset<8> mask); + + /* + * Interrupt IO callback called when PCF8574 IO pins changed (falling edge) + * @param intr : pin name connected to PCF8574 int pin (PinName) + * @param ftpr : user callback function (void ftpr(unit8_t data, PCF8574 *o)). + * data : PCF8574 IO port value + * o : object instance address + * @return none + */ + void interrupt(PinName intr,void (*ftpr)(uint8_t data, PCF8574 *o)); + + /* + * Get current error message + * @param : none + * @return current error message(std::string) + */ + std::string getErrorMessage(void) + { + if(_errnum < PCF8574_MaxError) + return(_ErrorMessagePCF8574[_errnum]); + else + return("errnum out of range"); + } + + /* + * Get the current error number (PCF8574_NoError if no error) + * @param : none + * @return current error number (uint8_t) + */ + uint8_t getError(void); + + /* + * Memorize data IO port value and bits that have changed when interrupt + * @param data : data IO port value when interrupt (bitset<8>) + * @param changed : bits that have changed (bitset<8>) + * @return none + */ + void setIntrData(bitset<8> data, bitset<8> changed); + + /* + * Get data IO port value when and bits that have changed when interrupt + * @param data : data IO port value when interrupt (bitset<8>&) + * @param changed : bits that have changed (bitset<8>&) + * @return none + */ + void getIntrData(bitset<8>& data, bitset<8>& changed); + + /* + *Operator () (read) + */ + operator uint8_t(); + + /* + *Operator = (write) + */ + PCF8574 &operator=(uint8_t data); + +private: + I2C _i2c; // Local i2c communication interface instance + int _address; // Local pcf8574 i2c address + int _PCF8574_address; // PCF8574 address + uint8_t _errnum; // Error number + bitset<8> _intr_data; // IO port value when interrupt + bitset<8> _intr_bits_changed; // Bits that have changed on interrupt + InterruptIn *_intr; // Internal InterruptIn object address + uint8_t _intr_flag; // interrupt function has been called +}; + +#endif \ No newline at end of file