CN0396 (4-Wire Electrochemical Dual Toxic Gas Sensing System)

Dependencies:   AD5270 AD7798 ADT7310

Dependents:   cn0396-helloworld

For additional information check out the mbed page of the Analog Devices wiki: https://wiki.analog.com/resources/tools-software/mbed-drivers-all

Files at this revision

API Documentation at this revision

Comitter:
adisuciu
Date:
Mon Nov 07 15:56:24 2016 +0000
Child:
1:024253f170c3
Commit message:
Initial revision

Changed in this revision

CN0396.cpp Show annotated file Show diff for this revision Revisions of this file
CN0396.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CN0396.cpp	Mon Nov 07 15:56:24 2016 +0000
@@ -0,0 +1,152 @@
+
+#include "AD5270.h"
+#include <math.h>
+#include "CN0396.h"
+
+#define ADC_GAIN      AD7798_GAIN_1
+#define ADC_SPS        0x05  //50SPS
+
+#define CO_SENS    (75 * pow(10, -9))    /* Sensitivity nA/ppm in 400ppm CO 50 to 100 */
+#define CO_RANGE   1000 /* Range ppm CO limit of performance warranty 1,000 */
+#define H2S_SENS   (700 * pow(10, -9)) /* Sensitivity nA/ppm in 20ppm H2S 450 to 900 */
+#define H2S_RANGE  100  /* Range ppm H2S limit of performance warranty 100 */
+
+/*	CO side	H2S side
+Temperature	Mean 	Mean*/
+
+extern Serial pc;
+
+CN0396::CN0396(PinName csad, PinName csrdac, PinName cstemp) :
+    csad(csad), csrdac(csrdac), cstemp(cstemp), ad(csad), rdac(csrdac), temp(cstemp)
+{
+
+}
+
+void CN0396::data_to_voltage(uint16_t adcValue, float *voltage, int gain_adc)
+{
+    *voltage = (float)(adcValue * V_REF) / (float)(_2_16 * gain_adc);
+}
+
+void CN0396::data_to_voltage_bipolar(uint16_t adcValue, float *voltage, int gain_adc)
+{
+    *voltage = ((static_cast<float>(adcValue) / _2_15) - 1.0) * (V_REF / static_cast<float>(gain_adc));
+}
+
+float CN0396::get_feedback_resistor_value(float sensitivity, float range)
+{
+    return 1.2 / (sensitivity * range);
+}
+
+void CN0396::configure_feedback_resistors(float resistance1, float resistance2)
+{
+    uint16_t R1 = rdac.calc_RDAC(resistance1);
+    uint16_t R2 = rdac.calc_RDAC(resistance2);
+
+    csrdac = false;
+    rdac.write_cmd(AD5270::WRITE_CTRL_REG,  AD5270::RDAC_WRITE_PROTECT, false); // RDAC register write protect -  allow update of wiper position through digital interface
+    rdac.write_cmd(AD5270::WRITE_CTRL_REG,  AD5270::RDAC_WRITE_PROTECT, false); // RDAC register write protect -  allow update of wiper position through digital interface
+    csrdac = true;
+    wait_us(2);
+    csrdac = false;
+    rdac.write_cmd(AD5270::WRITE_RDAC, R2, false); // write data to the RDAC register
+    rdac.write_cmd(AD5270::WRITE_RDAC, R1, false); // write data to the RDAC register
+    csrdac = true;
+    wait_us(2);
+    csrdac = false;
+    rdac.write_cmd(AD5270::WRITE_CTRL_REG, 0, false); // RDAC register write protect -  allow update of wiper position through digital interface
+    rdac.write_cmd(AD5270::WRITE_CTRL_REG, 0, false); // RDAC register write protect -  allow update of wiper position through digital interface
+    csrdac = false;
+    wait_us(2);
+    csrdac = false;
+    rdac.write_reg(AD5270::HI_Z_Cmd, false);
+    rdac.write_reg(AD5270::HI_Z_Cmd, false);
+    csrdac = true;
+    wait_us(2);
+    csrdac = false;
+    rdac.write_reg(AD5270::NO_OP_cmd, false);
+    rdac.write_reg(AD5270::NO_OP_cmd, false);
+    csrdac = true;
+}
+
+void CN0396::init()
+{
+    // set rdac
+
+    pc.printf("Computing resistor values \r\n");
+
+    resistance1 = get_feedback_resistor_value(CO_SENS,  CO_RANGE );
+    resistance0 = get_feedback_resistor_value(H2S_SENS, H2S_RANGE);
+
+    pc.printf("R1 = %f\r\nR2=%f\r\n", resistance0, resistance1);
+    pc.printf("Configuring feedback resistors\r\n");
+    configure_feedback_resistors(resistance1, resistance1);
+    pc.printf("Done\r\n");
+    // config temp
+    pc.printf("Configuring temperature sensor\r\n");
+    temp.reset();
+    temp.write_config(0x90);
+    pc.printf("Done\r\n");
+
+    pc.printf("Configuring ADC\r\n");
+    ad.reset();
+    if(ad.init()) {
+        ad.set_coding_mode(AD7798_UNIPOLAR);
+        ad.set_mode(AD7798_MODE_SINGLE);
+        ad.set_gain(ADC_GAIN);
+        ad.set_filter(ADC_SPS);
+        ad.set_reference(AD7798_REFDET_ENA);
+        pc.printf("ADC Config succesful\r\n");
+    } else {
+        pc.printf("ADC Config failed\r\n");
+
+    }
+
+
+}
+
+float CN0396::compensate_ppm(float result, float temp, sensor_type_t sensor)
+{
+    for(uint8_t i = 1; i < COMPENSATION_TABLE_SIZE; i++) {
+        if(temp < ppm_compensation[i].temp && temp > ppm_compensation[i - 1].temp) {
+            float compensation_coef;
+            if(sensor == H2S_SENSOR) {
+                compensation_coef = (((temp - (ppm_compensation[i - 1].temp )) * (ppm_compensation[i].H2S_percent - ppm_compensation[i - 1].H2S_percent)) / (ppm_compensation[i].temp  - ppm_compensation[i - 1].temp)) + ppm_compensation[i - 1].H2S_percent;
+            } else {
+                compensation_coef = (((temp - (ppm_compensation[i - 1].temp )) * (ppm_compensation[i].CO_percent - ppm_compensation[i - 1].CO_percent)) / (ppm_compensation[i].temp  - ppm_compensation[i - 1].temp)) + ppm_compensation[i - 1].CO_percent;
+            }
+
+            return (result * compensation_coef) / 100.0;
+        }
+    }
+}
+void CN0396::read()
+{
+    uint16_t data0, data1;
+    // read temperature
+    uint16_t temp_data = temp.read_temp();
+    float temp = 0;
+
+    if(temp_data & 0x8000) {
+        temp = (temp_data - 65536) / (128.0);
+    } else {
+        temp = temp_data / (128.0);
+    }
+
+    // read channels
+    ad.set_channel(0);
+    ad.read_data(0, &data0);
+    float volt0;
+    data_to_voltage(data0, &volt0);
+    float result0 = (volt0 / resistance0) / CO_SENS;
+    ad.set_channel(1);
+    ad.read_data(1, &data1);
+    float volt1;
+    data_to_voltage(data1, &volt1);
+    float result1 = (volt1 / resistance1) / H2S_SENS;
+    // compute ppm based on formula
+    // return ppm
+    result0 = compensate_ppm(result0, temp, CO_SENSOR);
+    result1 = compensate_ppm(result1, temp, H2S_SENSOR);
+
+    pc.printf("%f %f %f \r\n", temp, result0, result1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CN0396.h	Mon Nov 07 15:56:24 2016 +0000
@@ -0,0 +1,113 @@
+#ifndef _CN0396_H_
+#define _CN0396_H_
+#include <mbed.h>
+#include "AD7798.h"
+#include "AD5270.h"
+#include "ADT7310.h"
+
+/**
+ * @brief The CN0396 class
+ */
+class CN0396
+{
+public:
+
+#define V_REF                1.200    // [V]
+#define _2_16                65535.0   // 2^16
+#define _2_15                32767.0   // 2^16
+#define COMPENSATION_TABLE_SIZE 9
+
+    typedef enum {
+        CO_SENSOR,
+        H2S_SENSOR
+    } sensor_type_t;
+
+    typedef struct {
+        int8_t temp;
+        float CO_percent;
+        float H2S_percent;
+    } ppm_compensation_t;
+
+	/**
+	 *  @brief compensation look-up table
+	 */
+    const ppm_compensation_t ppm_compensation[COMPENSATION_TABLE_SIZE] = {
+        { -30	, 29.9  , 82.3 },
+        { -20	, 38.8  , 84.6 },
+        { -10	, 53.7  , 88.6 },
+        {0		, 69.6  , 92.2 },
+        {10		, 84.9  , 96.2 },
+        {20		, 100.0 , 100.0},
+        {30		, 112.7 , 103.1},
+        {40		, 123.7 , 105.6},
+        {50		, 133.1 , 107.4}
+    };
+
+
+    /**
+     * @brief CN0396 class constructor
+     * @param csad  - chipselect pin of the ADC
+     * @param csrdac - chipselect pin of the RDAC
+     * @param cstemp - chipselect pin of the temperature sensor
+     */
+    CN0396(PinName csad, PinName csrdac, PinName cstemp);
+    /**
+     * @brief Initializes the board
+     */
+    void init();
+
+    /**
+     * @brief - Reads the sensor and computes the PPM values
+     */
+    void read();
+
+    /**
+     * @brief computes the feedback resistor value for the sensor
+     * @param sensitivity - sensor sensitivity
+     * @param range - sensor range
+     * @return resistor value
+     */
+    float get_feedback_resistor_value(float sensitivity, float range);
+
+    /**
+     * @brief configures the RDACs with the resistance values
+     * @param resistance1 - resistance of RDAC1
+     * @param resistance2 - resistance of RDAC2
+     * @return
+     */
+    void configure_feedback_resistors(float resistance1, float resistance2);
+
+    /**
+    * @brief computes ADC counts-to-voltage in unipolar configuration
+    * @param adcValue - value in counts
+    * @param voltage - voltage value returned by the method
+    * @param gain_adc - the gain of the adc
+    */
+    void data_to_voltage(uint16_t adcValue, float *voltage, int gain_adc = 1);
+
+    /**
+     * @brief computes ADC counts-to-voltage in bipolar configuration
+     * @param adcValue - value in counts
+     * @param voltage - voltage value returned by the method
+     * @param gain_adc - the gain of the adc
+     */
+    void data_to_voltage_bipolar(uint16_t adcValue, float *voltage, int gain_adc = 1);
+
+    /**
+     * @brief compensates ppm value based on temperature reading
+     * @param result - ppm value before compensation
+     * @param temp - temperature used in compensation
+     * @param sensor - sensor id
+     * @return compensated value
+     */
+    float compensate_ppm(float result, float temp, sensor_type_t sensor);
+    DigitalOut csad, csrdac, cstemp;
+    AD7798 ad;
+    AD5270 rdac;
+    ADT7310 temp;
+    float resistance0, resistance1;
+private:
+
+
+};
+#endif