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