Complete PCF8574 and PCF74A library with interrupt handling

Dependents:   Nucleo_PCF8574_led_iic Dawson_Controller

Files at this revision

API Documentation at this revision

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
--- /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
--- /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