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
CN0396.cpp@2:8d2d32e76157, 2016-11-08 (annotated)
- Committer:
- adisuciu
- Date:
- Tue Nov 08 09:51:15 2016 +0000
- Revision:
- 2:8d2d32e76157
- Parent:
- 1:024253f170c3
Fix for mbed online compiler (array initialization not allowed within class declaration)
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
adisuciu | 0:ef85449aa57f | 1 | |
adisuciu | 0:ef85449aa57f | 2 | #include "AD5270.h" |
adisuciu | 0:ef85449aa57f | 3 | #include <math.h> |
adisuciu | 0:ef85449aa57f | 4 | #include "CN0396.h" |
adisuciu | 0:ef85449aa57f | 5 | |
adisuciu | 0:ef85449aa57f | 6 | #define ADC_GAIN AD7798_GAIN_1 |
adisuciu | 0:ef85449aa57f | 7 | #define ADC_SPS 0x05 //50SPS |
adisuciu | 0:ef85449aa57f | 8 | |
adisuciu | 1:024253f170c3 | 9 | #define CO_SENS (75 * pow(10.0, -9.0)) /* Sensitivity nA/ppm in 400ppm CO 50 to 100 */ |
adisuciu | 0:ef85449aa57f | 10 | #define CO_RANGE 1000 /* Range ppm CO limit of performance warranty 1,000 */ |
adisuciu | 1:024253f170c3 | 11 | #define H2S_SENS (700 * pow(10.0, -9.0)) /* Sensitivity nA/ppm in 20ppm H2S 450 to 900 */ |
adisuciu | 0:ef85449aa57f | 12 | #define H2S_RANGE 100 /* Range ppm H2S limit of performance warranty 100 */ |
adisuciu | 0:ef85449aa57f | 13 | |
adisuciu | 1:024253f170c3 | 14 | |
adisuciu | 1:024253f170c3 | 15 | //CN0396:: |
adisuciu | 1:024253f170c3 | 16 | |
adisuciu | 1:024253f170c3 | 17 | |
adisuciu | 0:ef85449aa57f | 18 | /* CO side H2S side |
adisuciu | 0:ef85449aa57f | 19 | Temperature Mean Mean*/ |
adisuciu | 0:ef85449aa57f | 20 | |
adisuciu | 0:ef85449aa57f | 21 | extern Serial pc; |
adisuciu | 0:ef85449aa57f | 22 | |
adisuciu | 2:8d2d32e76157 | 23 | |
adisuciu | 2:8d2d32e76157 | 24 | CN0396::ppm_compensation_t ppm_compensation_init[COMPENSATION_TABLE_SIZE] = { |
adisuciu | 1:024253f170c3 | 25 | { -30 , 29.9 , 82.3 }, |
adisuciu | 1:024253f170c3 | 26 | { -20 , 38.8 , 84.6 }, |
adisuciu | 1:024253f170c3 | 27 | { -10 , 53.7 , 88.6 }, |
adisuciu | 1:024253f170c3 | 28 | {0 , 69.6 , 92.2 }, |
adisuciu | 1:024253f170c3 | 29 | {10 , 84.9 , 96.2 }, |
adisuciu | 1:024253f170c3 | 30 | {20 , 100.0 , 100.0}, |
adisuciu | 1:024253f170c3 | 31 | {30 , 112.7 , 103.1}, |
adisuciu | 1:024253f170c3 | 32 | {40 , 123.7 , 105.6}, |
adisuciu | 1:024253f170c3 | 33 | {50 , 133.1 , 107.4} |
adisuciu | 1:024253f170c3 | 34 | }; |
adisuciu | 2:8d2d32e76157 | 35 | CN0396::CN0396(PinName csad, PinName csrdac, PinName cstemp) : |
adisuciu | 2:8d2d32e76157 | 36 | csad(csad), csrdac(csrdac), cstemp(cstemp), ad(csad), rdac(csrdac), temp(cstemp) |
adisuciu | 2:8d2d32e76157 | 37 | { |
adisuciu | 2:8d2d32e76157 | 38 | memcpy(ppm_compensation,ppm_compensation_init,COMPENSATION_TABLE_SIZE*sizeof(ppm_compensation_t)); |
adisuciu | 0:ef85449aa57f | 39 | } |
adisuciu | 0:ef85449aa57f | 40 | |
adisuciu | 0:ef85449aa57f | 41 | void CN0396::data_to_voltage(uint16_t adcValue, float *voltage, int gain_adc) |
adisuciu | 0:ef85449aa57f | 42 | { |
adisuciu | 0:ef85449aa57f | 43 | *voltage = (float)(adcValue * V_REF) / (float)(_2_16 * gain_adc); |
adisuciu | 0:ef85449aa57f | 44 | } |
adisuciu | 0:ef85449aa57f | 45 | |
adisuciu | 0:ef85449aa57f | 46 | void CN0396::data_to_voltage_bipolar(uint16_t adcValue, float *voltage, int gain_adc) |
adisuciu | 0:ef85449aa57f | 47 | { |
adisuciu | 0:ef85449aa57f | 48 | *voltage = ((static_cast<float>(adcValue) / _2_15) - 1.0) * (V_REF / static_cast<float>(gain_adc)); |
adisuciu | 0:ef85449aa57f | 49 | } |
adisuciu | 0:ef85449aa57f | 50 | |
adisuciu | 0:ef85449aa57f | 51 | float CN0396::get_feedback_resistor_value(float sensitivity, float range) |
adisuciu | 0:ef85449aa57f | 52 | { |
adisuciu | 0:ef85449aa57f | 53 | return 1.2 / (sensitivity * range); |
adisuciu | 0:ef85449aa57f | 54 | } |
adisuciu | 0:ef85449aa57f | 55 | |
adisuciu | 0:ef85449aa57f | 56 | void CN0396::configure_feedback_resistors(float resistance1, float resistance2) |
adisuciu | 0:ef85449aa57f | 57 | { |
adisuciu | 0:ef85449aa57f | 58 | uint16_t R1 = rdac.calc_RDAC(resistance1); |
adisuciu | 0:ef85449aa57f | 59 | uint16_t R2 = rdac.calc_RDAC(resistance2); |
adisuciu | 0:ef85449aa57f | 60 | |
adisuciu | 0:ef85449aa57f | 61 | csrdac = false; |
adisuciu | 0:ef85449aa57f | 62 | rdac.write_cmd(AD5270::WRITE_CTRL_REG, AD5270::RDAC_WRITE_PROTECT, false); // RDAC register write protect - allow update of wiper position through digital interface |
adisuciu | 0:ef85449aa57f | 63 | rdac.write_cmd(AD5270::WRITE_CTRL_REG, AD5270::RDAC_WRITE_PROTECT, false); // RDAC register write protect - allow update of wiper position through digital interface |
adisuciu | 0:ef85449aa57f | 64 | csrdac = true; |
adisuciu | 0:ef85449aa57f | 65 | wait_us(2); |
adisuciu | 0:ef85449aa57f | 66 | csrdac = false; |
adisuciu | 0:ef85449aa57f | 67 | rdac.write_cmd(AD5270::WRITE_RDAC, R2, false); // write data to the RDAC register |
adisuciu | 0:ef85449aa57f | 68 | rdac.write_cmd(AD5270::WRITE_RDAC, R1, false); // write data to the RDAC register |
adisuciu | 0:ef85449aa57f | 69 | csrdac = true; |
adisuciu | 0:ef85449aa57f | 70 | wait_us(2); |
adisuciu | 0:ef85449aa57f | 71 | csrdac = false; |
adisuciu | 0:ef85449aa57f | 72 | rdac.write_cmd(AD5270::WRITE_CTRL_REG, 0, false); // RDAC register write protect - allow update of wiper position through digital interface |
adisuciu | 0:ef85449aa57f | 73 | rdac.write_cmd(AD5270::WRITE_CTRL_REG, 0, false); // RDAC register write protect - allow update of wiper position through digital interface |
adisuciu | 0:ef85449aa57f | 74 | csrdac = false; |
adisuciu | 0:ef85449aa57f | 75 | wait_us(2); |
adisuciu | 0:ef85449aa57f | 76 | csrdac = false; |
adisuciu | 0:ef85449aa57f | 77 | rdac.write_reg(AD5270::HI_Z_Cmd, false); |
adisuciu | 0:ef85449aa57f | 78 | rdac.write_reg(AD5270::HI_Z_Cmd, false); |
adisuciu | 0:ef85449aa57f | 79 | csrdac = true; |
adisuciu | 0:ef85449aa57f | 80 | wait_us(2); |
adisuciu | 0:ef85449aa57f | 81 | csrdac = false; |
adisuciu | 0:ef85449aa57f | 82 | rdac.write_reg(AD5270::NO_OP_cmd, false); |
adisuciu | 0:ef85449aa57f | 83 | rdac.write_reg(AD5270::NO_OP_cmd, false); |
adisuciu | 0:ef85449aa57f | 84 | csrdac = true; |
adisuciu | 0:ef85449aa57f | 85 | } |
adisuciu | 0:ef85449aa57f | 86 | |
adisuciu | 0:ef85449aa57f | 87 | void CN0396::init() |
adisuciu | 0:ef85449aa57f | 88 | { |
adisuciu | 0:ef85449aa57f | 89 | // set rdac |
adisuciu | 0:ef85449aa57f | 90 | |
adisuciu | 0:ef85449aa57f | 91 | pc.printf("Computing resistor values \r\n"); |
adisuciu | 0:ef85449aa57f | 92 | |
adisuciu | 0:ef85449aa57f | 93 | resistance1 = get_feedback_resistor_value(CO_SENS, CO_RANGE ); |
adisuciu | 0:ef85449aa57f | 94 | resistance0 = get_feedback_resistor_value(H2S_SENS, H2S_RANGE); |
adisuciu | 0:ef85449aa57f | 95 | |
adisuciu | 0:ef85449aa57f | 96 | pc.printf("R1 = %f\r\nR2=%f\r\n", resistance0, resistance1); |
adisuciu | 0:ef85449aa57f | 97 | pc.printf("Configuring feedback resistors\r\n"); |
adisuciu | 0:ef85449aa57f | 98 | configure_feedback_resistors(resistance1, resistance1); |
adisuciu | 0:ef85449aa57f | 99 | pc.printf("Done\r\n"); |
adisuciu | 0:ef85449aa57f | 100 | // config temp |
adisuciu | 0:ef85449aa57f | 101 | pc.printf("Configuring temperature sensor\r\n"); |
adisuciu | 0:ef85449aa57f | 102 | temp.reset(); |
adisuciu | 0:ef85449aa57f | 103 | temp.write_config(0x90); |
adisuciu | 0:ef85449aa57f | 104 | pc.printf("Done\r\n"); |
adisuciu | 0:ef85449aa57f | 105 | |
adisuciu | 0:ef85449aa57f | 106 | pc.printf("Configuring ADC\r\n"); |
adisuciu | 0:ef85449aa57f | 107 | ad.reset(); |
adisuciu | 0:ef85449aa57f | 108 | if(ad.init()) { |
adisuciu | 0:ef85449aa57f | 109 | ad.set_coding_mode(AD7798_UNIPOLAR); |
adisuciu | 0:ef85449aa57f | 110 | ad.set_mode(AD7798_MODE_SINGLE); |
adisuciu | 0:ef85449aa57f | 111 | ad.set_gain(ADC_GAIN); |
adisuciu | 0:ef85449aa57f | 112 | ad.set_filter(ADC_SPS); |
adisuciu | 0:ef85449aa57f | 113 | ad.set_reference(AD7798_REFDET_ENA); |
adisuciu | 0:ef85449aa57f | 114 | pc.printf("ADC Config succesful\r\n"); |
adisuciu | 0:ef85449aa57f | 115 | } else { |
adisuciu | 0:ef85449aa57f | 116 | pc.printf("ADC Config failed\r\n"); |
adisuciu | 0:ef85449aa57f | 117 | |
adisuciu | 0:ef85449aa57f | 118 | } |
adisuciu | 0:ef85449aa57f | 119 | |
adisuciu | 0:ef85449aa57f | 120 | |
adisuciu | 0:ef85449aa57f | 121 | } |
adisuciu | 0:ef85449aa57f | 122 | |
adisuciu | 0:ef85449aa57f | 123 | float CN0396::compensate_ppm(float result, float temp, sensor_type_t sensor) |
adisuciu | 0:ef85449aa57f | 124 | { |
adisuciu | 0:ef85449aa57f | 125 | for(uint8_t i = 1; i < COMPENSATION_TABLE_SIZE; i++) { |
adisuciu | 0:ef85449aa57f | 126 | if(temp < ppm_compensation[i].temp && temp > ppm_compensation[i - 1].temp) { |
adisuciu | 0:ef85449aa57f | 127 | float compensation_coef; |
adisuciu | 0:ef85449aa57f | 128 | if(sensor == H2S_SENSOR) { |
adisuciu | 0:ef85449aa57f | 129 | 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; |
adisuciu | 0:ef85449aa57f | 130 | } else { |
adisuciu | 0:ef85449aa57f | 131 | 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; |
adisuciu | 0:ef85449aa57f | 132 | } |
adisuciu | 0:ef85449aa57f | 133 | |
adisuciu | 0:ef85449aa57f | 134 | return (result * compensation_coef) / 100.0; |
adisuciu | 0:ef85449aa57f | 135 | } |
adisuciu | 0:ef85449aa57f | 136 | } |
adisuciu | 0:ef85449aa57f | 137 | } |
adisuciu | 0:ef85449aa57f | 138 | void CN0396::read() |
adisuciu | 0:ef85449aa57f | 139 | { |
adisuciu | 0:ef85449aa57f | 140 | uint16_t data0, data1; |
adisuciu | 0:ef85449aa57f | 141 | // read temperature |
adisuciu | 0:ef85449aa57f | 142 | uint16_t temp_data = temp.read_temp(); |
adisuciu | 0:ef85449aa57f | 143 | float temp = 0; |
adisuciu | 0:ef85449aa57f | 144 | |
adisuciu | 0:ef85449aa57f | 145 | if(temp_data & 0x8000) { |
adisuciu | 0:ef85449aa57f | 146 | temp = (temp_data - 65536) / (128.0); |
adisuciu | 0:ef85449aa57f | 147 | } else { |
adisuciu | 0:ef85449aa57f | 148 | temp = temp_data / (128.0); |
adisuciu | 0:ef85449aa57f | 149 | } |
adisuciu | 0:ef85449aa57f | 150 | |
adisuciu | 0:ef85449aa57f | 151 | // read channels |
adisuciu | 0:ef85449aa57f | 152 | ad.set_channel(0); |
adisuciu | 0:ef85449aa57f | 153 | ad.read_data(0, &data0); |
adisuciu | 0:ef85449aa57f | 154 | float volt0; |
adisuciu | 0:ef85449aa57f | 155 | data_to_voltage(data0, &volt0); |
adisuciu | 0:ef85449aa57f | 156 | float result0 = (volt0 / resistance0) / CO_SENS; |
adisuciu | 0:ef85449aa57f | 157 | ad.set_channel(1); |
adisuciu | 0:ef85449aa57f | 158 | ad.read_data(1, &data1); |
adisuciu | 0:ef85449aa57f | 159 | float volt1; |
adisuciu | 0:ef85449aa57f | 160 | data_to_voltage(data1, &volt1); |
adisuciu | 0:ef85449aa57f | 161 | float result1 = (volt1 / resistance1) / H2S_SENS; |
adisuciu | 0:ef85449aa57f | 162 | // compute ppm based on formula |
adisuciu | 0:ef85449aa57f | 163 | // return ppm |
adisuciu | 0:ef85449aa57f | 164 | result0 = compensate_ppm(result0, temp, CO_SENSOR); |
adisuciu | 0:ef85449aa57f | 165 | result1 = compensate_ppm(result1, temp, H2S_SENSOR); |
adisuciu | 0:ef85449aa57f | 166 | |
adisuciu | 0:ef85449aa57f | 167 | pc.printf("%f %f %f \r\n", temp, result0, result1); |
adisuciu | 0:ef85449aa57f | 168 | } |