CN0396 (4-Wire Electrochemical Dual Toxic Gas Sensing System)
Dependencies: AD5270 AD7798 ADT7310
For additional information check out the mbed page of the Analog Devices wiki: https://wiki.analog.com/resources/tools-software/mbed-drivers-all
Revision 0:ef85449aa57f, committed 2016-11-07
- 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