Library for NXP (Philips) PCF8591 I2C 4 Channel, 8bit Analog to Digital converter and a 1 Channel, 8bit Digital to Analog converter.

Dependents:   812_hello PCF8591_test_LAAS

Files at this revision

API Documentation at this revision

Comitter:
wim
Date:
Sat Sep 21 19:25:09 2013 +0000
Commit message:
Extended functionality to include mbed type AnalogIn and AnalogOut; Fixed issue related to LPC812 I2C bug

Changed in this revision

PCF8591.cpp Show annotated file Show diff for this revision Revisions of this file
PCF8591.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 1116b0d151fc PCF8591.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PCF8591.cpp	Sat Sep 21 19:25:09 2013 +0000
@@ -0,0 +1,293 @@
+/* PCF8591 - I2C 8 bit 4 channel A/D and 8 bit 1 channel D/A converter
+ * Copyright (c) 2013 Wim Huiskamp
+ *
+ * Released under the MIT License: http://mbed.org/license/mit
+ *
+ * version 0.2 Initial Release
+*/
+#include "mbed.h"
+#include "PCF8591.h"
+
+
+/** Create a PCF8591 AD and DA object using a specified I2C bus and slaveaddress
+  *
+  * @param I2C &i2c the I2C port to connect to 
+  * @param char deviceAddress the address of the PCF8591
+  */  
+PCF8591::PCF8591(I2C *i2c, uint8_t deviceAddress) : _i2c(i2c) {
+    
+  _slaveAddress = deviceAddress;
+  _init(); 
+}
+
+
+#if(0)
+//These methods are disabled since they interfere with normal expected behaviour for mbed type Analog I/O
+//
+  
+/** Set ADC mode
+  *
+  * @param adc_mode      ADC Channel mode, valid Range (PCF8591_4S, PCF8591_3D, PCF8591_2S_1D, PCF8591_2D)
+  */  
+void PCF8591::setADCMode(uint8_t mode){
+  uint8_t data[6];
+  
+  _mode = _mode & ~PCF8591_2D; // Clear ADC mode bits
+  
+  switch (mode) {
+    case PCF8591_4S:
+                _mode = _mode | PCF8591_4S;
+                break; 
+    case PCF8591_3D:
+                _mode = _mode | PCF8591_3D;
+                break;    
+    case PCF8591_2S_1D:
+                _mode = _mode | PCF8591_2S_1D;
+                break;    
+    case PCF8591_2D:
+                _mode = _mode | PCF8591_2D;
+                break;    
+    default:
+                _mode = _mode | PCF8591_4S;    
+                break; 
+  }    
+        
+  data[0] = _mode;             // Control Reg
+  
+  // write data to the device
+  _i2c->write(_slaveAddress, (char*) data, 1);  
+   
+};
+
+/** Set DAC mode
+  *
+  * @param dac_mode      DAC Channel mode, valid Range (false=disable, true=enable)
+  */  
+void PCF8591::setDACMode(bool mode) {
+  uint8_t data[6];
+   
+  if (mode) 
+    _mode = _mode | PCF8591_AOUT;  // Enable DAC output
+  else
+    _mode = _mode & ~PCF8591_AOUT; // Disable DAC output
+
+  data[0] = _mode;             // Control Reg
+  
+  // write data to the device
+  _i2c->write(_slaveAddress, (char*) data, 1);  
+  
+};
+#endif
+
+
+/** Write Analog Output
+  *
+  * @param  analogOut  output value (0..255)
+  */  
+void PCF8591::write(uint8_t analogOut) {
+  uint8_t data[6];
+   
+  data[0] = _mode;             // Control Reg
+  data[1] = analogOut;         // DAC Channel
+  
+  // write data to the device
+  _i2c->write(_slaveAddress, (char*) data, 2);  
+};        
+
+/** Read Analog Channel 
+  *
+  * @param Channel   ADC channel, valid range PCF8591_ADC0, PCF8591_ADC1, PCF8591_ADC2 or PCF8591_ADC3
+  * @return value    uint8_t AD converted value (0..255, representing 0V..Vcc)   
+  */  
+#if(0)
+// I2C block operations test
+// Causes problems with Ticker and printf on LPC812. No issues found on LPC1768
+uint8_t PCF8591::read(uint8_t channel) {
+//  uint8_t data[6];
+  uint8_t data[6] = {123,123,123,123,123,123}; //test to check failed read
+    
+  _mode   = _mode & ~PCF8591_CHMSK;            // Clear ADC Channel bits
+  _mode   = _mode | (channel & PCF8591_CHMSK); // Set ADC Channel bits
+    
+  data[0] = _mode;             // Init Control Reg
+  
+  // write data to the device to select the ADC channel
+  _i2c->write(_slaveAddress, (char*) data, 1);  
+ 
+  // read selected ADC channel
+  // note that first byte is a 'dummy' and should be ignored
+  _i2c->read(_slaveAddress, (char*) data, 2);  
+  return data[1];
+};
+
+#else
+// I2C single byte operations test
+// No issues found on LPC812
+uint8_t PCF8591::read(uint8_t channel) {
+  uint8_t data[6];
+//  uint8_t data[6] = {123,123,123,123,123,123}; //test to check failed read
+    
+  _mode   = _mode & ~PCF8591_CHMSK;            // Clear ADC Channel bits
+  _mode   = _mode | (channel & PCF8591_CHMSK); // Set ADC Channel bits
+    
+  data[0] = _mode;             // Init Control Reg
+  
+  // write data to the device to select the ADC channel
+  _i2c->start();  
+  _i2c->write(_slaveAddress);        // Slave write address 
+  _i2c->write(data[0]);  
+  _i2c->stop();  
+   
+  // read selected ADC channel
+  // note that first byte is a 'dummy' and should be ignored
+  _i2c->start();  
+  _i2c->write(_slaveAddress | 0x01); // Slave read address 
+  data[0]=_i2c->read(1);  //ack
+  data[1]=_i2c->read(0);  //nack  
+  _i2c->stop();  
+  
+  return data[1];
+};
+
+#endif
+
+
+/** Initialise AD and DA driver 
+  *
+  */  
+void PCF8591::_init() {
+  uint8_t data[6];
+  
+  _mode   = PCF8591_CTRL_DEF;  // Init mode
+  
+  data[0] = _mode;             // Init Control Reg
+  data[1] = 0x00;              // Init DAC Channel to 0
+  
+  // write data to the device
+  _i2c->write(_slaveAddress, (char*) data, 2);  
+}; 
+
+
+
+
+
+/** Create a PCF8591 AnalogOut object connected to the specified I2C bus and deviceAddress
+ *
+ */
+PCF8591_AnalogOut::PCF8591_AnalogOut(I2C *i2c, uint8_t deviceAddress) : _i2c(i2c) {
+    
+  _slaveAddress = deviceAddress;
+  _init(); 
+}
+ 
+
+/** Write Analog Output
+  *
+  * @param  analogOut value (0 .. 1.0)
+  */  
+void PCF8591_AnalogOut::write(float analogOut) {
+  uint8_t data[6];
+
+  data[0] = _mode;             // Control Reg
+
+  // DAC Channel
+  if (analogOut < 0.0) data[1] = 0;
+  else if (analogOut >= 1.0) data[1] = 255;
+       else data[1] = uint8_t (analogOut * 255.0);      
+   
+  // write data to the device
+  _i2c->write(_slaveAddress, (char*) data, 2);  
+};        
+
+/** Operator = Analog Output
+  *
+  * @param  analogOut value (0.0f .. 1.0f)
+  */  
+PCF8591_AnalogOut& PCF8591_AnalogOut::operator= (float value){  
+  write(value);     
+  return *this;   
+}
+ 
+
+/** Initialise DAC driver 
+  *
+  */  
+void PCF8591_AnalogOut::_init() {
+  uint8_t data[6];
+  
+  _mode   = PCF8591_CTRL_DEF;  // Init mode
+  
+  data[0] = _mode;             // Init Control Reg
+  data[1] = 0x00;              // Init DAC Channel to 0
+  
+  // write data to the device
+  _i2c->write(_slaveAddress, (char*) data, 2);  
+}; 
+
+
+
+/** Create a PCF8591 AnalogIn object connected to the specified I2C bus and deviceAddress
+ *
+ */
+PCF8591_AnalogIn::PCF8591_AnalogIn(I2C *i2c, uint8_t channel, uint8_t deviceAddress) : _i2c(i2c) {
+    
+  _slaveAddress = deviceAddress;
+  _channel = channel;  
+  _init(); 
+}
+ 
+
+/** Read Analog input
+  *
+  * @return analogIn value (0 .. 1.0)
+  */  
+float PCF8591_AnalogIn::read() {
+  uint8_t data[6];
+    
+  data[0] = _mode;             // Init Control Reg
+  
+  // write data to the device to select the ADC channel
+  _i2c->start();  
+  _i2c->write(_slaveAddress);        // Slave write address 
+  _i2c->write(data[0]);  
+  _i2c->stop();  
+   
+  // read selected ADC channel
+  // note that first byte is a 'dummy' and should be ignored
+  _i2c->start();  
+  _i2c->write(_slaveAddress | 0x01); // Slave read address 
+  data[0]=_i2c->read(1);  //ack
+  data[1]=_i2c->read(0);  //nack  
+  _i2c->stop();  
+  
+ 
+  // ADC Channel
+  return ((float) data[1] / 255.0);       
+  
+};
+
+
+/** Operator float Analog Input
+  *
+  * @return analogIn value (0 .. 1.0)
+  */  
+PCF8591_AnalogIn::operator float(){
+  return (read());    
+}
+ 
+
+/** Initialise ADC driver 
+  *
+  */  
+void PCF8591_AnalogIn::_init() {
+  uint8_t data[6];
+  
+  _mode   = PCF8591_CTRL_DEF;  // Init mode
+  _mode   = _mode & ~PCF8591_CHMSK;             // Clear ADC Channel bits
+  _mode   = _mode | (_channel & PCF8591_CHMSK); // Set ADC Channel bits
+      
+  data[0] = _mode;             // Init Control Reg
+  
+  // write data to the device
+  _i2c->write(_slaveAddress, (char*) data, 1);  
+}; 
diff -r 000000000000 -r 1116b0d151fc PCF8591.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PCF8591.h	Sat Sep 21 19:25:09 2013 +0000
@@ -0,0 +1,221 @@
+/* PCF8591 - I2C 8 bit, 4 channel A/D and 8 bit, 1 channel D/A converter
+ * Copyright (c) 2013 Wim Huiskamp
+ *
+ * Released under the MIT License: http://mbed.org/license/mit
+ *
+ * version 0.2 Initial Release
+*/
+#ifndef _PCF8591_H
+#define _PCF8591_H
+
+/** Driver for PCF8591 I2C I2C A/D and D/A converter
+ *
+ * @code
+ * #include "mbed.h"
+ * #include "PCF8591.h"
+ * 
+ * // I2C Communication
+ * I2C i2c_adcdac(p28,p27); // SDA, SCL for LPC1768
+ * //I2C i2c_adcdac(P0_10,P0_11); // SDA, SCL for LPC812
+ *
+ * //Declare a composite ADC and DAC device that may be used through public methods
+ * PCF8591 adc_dac(&i2c_adcdac); // I2C bus, Default PCF8591 Slaveaddress
+ *
+ * //Declare independent ADC and DAC objects that may be used similar to mbed AnalogIn and AnalogOut pins
+ * //PCF8591_AnalogOut anaOut(&i2c_bus);
+ * //
+ * //PCF8591_AnalogIn anaIn(&i2c_bus, PCF8591_ADC0);
+ *
+ * 
+ * int main() {
+ *   uint8_t count = 0; 
+ *   uint8_t analog;  
+ *
+ *   while(1) {
+ *     wait(0.2);    
+ *     count++;       
+ *
+ *     // Composite device methods    
+ *     adc_dac.write(count);                 // write D/A value
+ *     analog = adc_dac.read(PCF8591_ADC0);  // read A/D value for Channel 0
+ * 
+ *     // mbed pin type methods    
+ *     //anaOut = (float)count / 255.0;        // write D/A value using range 0.0f .. 1.0f
+ *     //analog = anaIn * 33.0;                // read A/D value for Channel 0 in (Volts*10)
+ *   }
+ *  
+ * }
+ * @endcode
+ */
+
+
+//Address Defines for PCF8591
+#define PCF8591_SA0 0x90
+#define PCF8591_SA1 0x92
+#define PCF8591_SA2 0x94
+#define PCF8591_SA3 0x96
+#define PCF8591_SA4 0x98
+#define PCF8591_SA5 0x9A
+#define PCF8591_SA6 0x9C
+#define PCF8591_SA7 0x9E
+
+//Register Defines for PCF8591
+#define PCF8591_CTRL 0x00
+#define PCF8591_ADR0 0x01
+#define PCF8591_ADR1 0x02
+#define PCF8591_ADR2 0x03
+#define PCF8591_ADR3 0x04
+#define PCF8591_DAR  0x01
+
+//Control Register Defines for PCF8591
+//AD Channels
+#define PCF8591_ADC0 0x00
+#define PCF8591_ADC1 0x01
+#define PCF8591_ADC2 0x02
+#define PCF8591_ADC3 0x03
+//AD Channel Mask
+#define PCF8591_CHMSK   0x03
+
+//Auto Increment flag (0=disable, 1=enable)
+#define PCF8591_AIF  0x04
+
+//AD Channels Input modes
+//4 Single Channels (Chan0=AIN0, Chan1=AIN0, Chan2=AIN0, Chan3=AIN0)
+#define PCF8591_4S   0x00
+//3 Diff Channels (Chan0=AIN0-AIN3, Chan1=AIN1-AIN3, Chan2=AIN2-AIN3)
+#define PCF8591_3D   0x10
+//2 Single Channels (Chan0=AIN0, Chan1=AIN1)
+//1 Diff Channel    (Chan2=AIN2-AIN3)
+#define PCF8591_2S_1D 0x20
+//2 Diff Channels (Chan0=AIN0-AIN1, Chan1=AIN2-AIN3)
+#define PCF8591_2D   0x30
+//ChannelMode Mask
+#define PCF8591_CHMDMSK  0x30
+
+//Analog Output enable flag (0=disable, 1=enable)
+#define PCF8591_AOUT 0x40
+
+//Default Mode: ADC0, AutoIncr Off, 4 Single Chan, AnalogOut On
+#define PCF8591_CTRL_DEF (PCF8591_ADC0 | PCF8591_4S | PCF8591_AOUT)
+
+
+/** Create a PCF8591 object connected to the specified I2C bus and deviceAddress
+ *
+*/
+class PCF8591 {
+public:
+  /** Create a PCF8591 AD and DA object using a specified I2C bus and slaveaddress
+   *
+   * @param I2C &i2c the I2C port to connect to 
+   * @param char deviceAddress the address of the PCF8591
+  */  
+  PCF8591(I2C *i2c, uint8_t deviceAddress = PCF8591_SA0);
+  
+#if(0)
+//These methods are disabled since they interfere with normal expected behaviour for mbed type Analog I/O
+//
+  /** Set ADC mode
+   *
+   * @param adc_mode      ADC Channel mode, valid Range (PCF8591_4S, PCF8591_3D, PCF8591_2S_1D, PCF8591_2D)
+  */  
+  void setADCMode(uint8_t mode);
+
+  /** Set DAC mode
+   *
+   * @param dac_mode      DAC Channel mode, valid Range (false=disable, true=enable)
+  */  
+  void setDACMode(bool mode);
+#endif
+
+/** Write Analog Output
+  *
+  * @param  analogOut  output value (0..255)
+  */  
+  void write(uint8_t analogOut);        
+
+/** Read Analog Channel 
+  *
+  * @param Channel   ADC channel, valid range PCF8591_ADC0, PCF8591_ADC1, PCF8591_ADC2 or PCF8591_ADC3
+  * @return value    uint8_t AD converted value (0..255, representing 0V..Vcc)   
+  */  
+  uint8_t read(uint8_t channel);
+
+
+  
+protected:
+  I2C *_i2c;                    //I2C bus reference
+  uint8_t _slaveAddress;        //I2C Slave address of device
+  uint8_t _mode;                //Device mode  
+
+/** Initialise AD and DA driver 
+  *
+  */  
+  void _init(); 
+};
+
+
+/** Create a PCF8591 AnalogOut object connected to the specified I2C bus and deviceAddress
+ *
+*/
+class PCF8591_AnalogOut {
+public:
+  /** Create a PCF8591 Analogout object using a specified I2C bus and slaveaddress
+   *
+   * @param I2C &i2c the I2C port to connect to 
+   * @param char deviceAddress the address of the PCF8591
+  */  
+  PCF8591_AnalogOut(I2C *i2c, uint8_t deviceAddress = PCF8591_SA0);
+
+  void write(float value);
+  #ifdef MBED_OPERATORS
+  /** An operator shorthand for write()
+   */ 
+  PCF8591_AnalogOut& operator= (float value);
+  #endif
+
+protected:
+  I2C *_i2c;                    //I2C bus reference
+  uint8_t _slaveAddress;        //I2C Slave address of device
+  uint8_t _mode;                //Device mode  
+
+/** Initialise DAC driver 
+  *
+  */  
+  void _init(); 
+};
+
+
+/** Create a PCF8591 AnalogIn object connected to the specified I2C bus and deviceAddress
+ *
+*/
+class PCF8591_AnalogIn {
+public:
+  /** Create a PCF8591 AnalogIn object using a specified I2C bus and slaveaddress
+   *
+   * @param I2C &i2c the I2C port to connect to 
+   * @param channel ADC channel of the PCF8591   
+   * @param char deviceAddress the address of the PCF8591
+  */  
+  PCF8591_AnalogIn(I2C *i2c, uint8_t channel, uint8_t deviceAddress = PCF8591_SA0);
+
+  float read();
+  #ifdef MBED_OPERATORS
+  /** An operator shorthand for read()
+   */ 
+  operator float();
+  #endif
+
+protected:
+  I2C *_i2c;                    //I2C bus reference
+  uint8_t _slaveAddress;        //I2C Slave address of device
+  uint8_t _mode;                //Device mode  
+  uint8_t _channel;             //Channel    
+
+/** Initialise DAC driver 
+  *
+  */  
+  void _init(); 
+};
+
+
+#endif
\ No newline at end of file