Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
For additional information check out the mbed page of the Analog Devices wiki: https://wiki.analog.com/resources/tools-software/mbed-drivers-all
libraries/CN0396/CN0396.cpp@33:c3ec596a29c2, 2016-11-07 (annotated)
- Committer:
- Adrian Suciu
- Date:
- Mon Nov 07 16:27:12 2016 +0200
- Revision:
- 33:c3ec596a29c2
Added CN0391, CN0396 and CN0397 shields
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| Adrian Suciu |
33:c3ec596a29c2 | 1 | |
| Adrian Suciu |
33:c3ec596a29c2 | 2 | #include "AD5270.h" |
| Adrian Suciu |
33:c3ec596a29c2 | 3 | #include <math.h> |
| Adrian Suciu |
33:c3ec596a29c2 | 4 | #include "CN0396.h" |
| Adrian Suciu |
33:c3ec596a29c2 | 5 | |
| Adrian Suciu |
33:c3ec596a29c2 | 6 | #define ADC_GAIN AD7798_GAIN_1 |
| Adrian Suciu |
33:c3ec596a29c2 | 7 | #define ADC_SPS 0x05 //50SPS |
| Adrian Suciu |
33:c3ec596a29c2 | 8 | |
| Adrian Suciu |
33:c3ec596a29c2 | 9 | #define CO_SENS (75 * pow(10, -9)) /* Sensitivity nA/ppm in 400ppm CO 50 to 100 */ |
| Adrian Suciu |
33:c3ec596a29c2 | 10 | #define CO_RANGE 1000 /* Range ppm CO limit of performance warranty 1,000 */ |
| Adrian Suciu |
33:c3ec596a29c2 | 11 | #define H2S_SENS (700 * pow(10, -9)) /* Sensitivity nA/ppm in 20ppm H2S 450 to 900 */ |
| Adrian Suciu |
33:c3ec596a29c2 | 12 | #define H2S_RANGE 100 /* Range ppm H2S limit of performance warranty 100 */ |
| Adrian Suciu |
33:c3ec596a29c2 | 13 | |
| Adrian Suciu |
33:c3ec596a29c2 | 14 | /* CO side H2S side |
| Adrian Suciu |
33:c3ec596a29c2 | 15 | Temperature Mean Mean*/ |
| Adrian Suciu |
33:c3ec596a29c2 | 16 | |
| Adrian Suciu |
33:c3ec596a29c2 | 17 | extern Serial pc; |
| Adrian Suciu |
33:c3ec596a29c2 | 18 | |
| Adrian Suciu |
33:c3ec596a29c2 | 19 | CN0396::CN0396(PinName csad, PinName csrdac, PinName cstemp) : |
| Adrian Suciu |
33:c3ec596a29c2 | 20 | csad(csad), csrdac(csrdac), cstemp(cstemp), ad(csad), rdac(csrdac), temp(cstemp) |
| Adrian Suciu |
33:c3ec596a29c2 | 21 | { |
| Adrian Suciu |
33:c3ec596a29c2 | 22 | |
| Adrian Suciu |
33:c3ec596a29c2 | 23 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 24 | |
| Adrian Suciu |
33:c3ec596a29c2 | 25 | void CN0396::data_to_voltage(uint16_t adcValue, float *voltage, int gain_adc) |
| Adrian Suciu |
33:c3ec596a29c2 | 26 | { |
| Adrian Suciu |
33:c3ec596a29c2 | 27 | *voltage = (float)(adcValue * V_REF) / (float)(_2_16 * gain_adc); |
| Adrian Suciu |
33:c3ec596a29c2 | 28 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 29 | |
| Adrian Suciu |
33:c3ec596a29c2 | 30 | void CN0396::data_to_voltage_bipolar(uint16_t adcValue, float *voltage, int gain_adc) |
| Adrian Suciu |
33:c3ec596a29c2 | 31 | { |
| Adrian Suciu |
33:c3ec596a29c2 | 32 | *voltage = ((static_cast<float>(adcValue) / _2_15) - 1.0) * (V_REF / static_cast<float>(gain_adc)); |
| Adrian Suciu |
33:c3ec596a29c2 | 33 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 34 | |
| Adrian Suciu |
33:c3ec596a29c2 | 35 | float CN0396::get_feedback_resistor_value(float sensitivity, float range) |
| Adrian Suciu |
33:c3ec596a29c2 | 36 | { |
| Adrian Suciu |
33:c3ec596a29c2 | 37 | return 1.2 / (sensitivity * range); |
| Adrian Suciu |
33:c3ec596a29c2 | 38 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 39 | |
| Adrian Suciu |
33:c3ec596a29c2 | 40 | void CN0396::configure_feedback_resistors(float resistance1, float resistance2) |
| Adrian Suciu |
33:c3ec596a29c2 | 41 | { |
| Adrian Suciu |
33:c3ec596a29c2 | 42 | uint16_t R1 = rdac.calc_RDAC(resistance1); |
| Adrian Suciu |
33:c3ec596a29c2 | 43 | uint16_t R2 = rdac.calc_RDAC(resistance2); |
| Adrian Suciu |
33:c3ec596a29c2 | 44 | |
| Adrian Suciu |
33:c3ec596a29c2 | 45 | csrdac = false; |
| Adrian Suciu |
33:c3ec596a29c2 | 46 | rdac.write_cmd(AD5270::WRITE_CTRL_REG, AD5270::RDAC_WRITE_PROTECT, false); // RDAC register write protect - allow update of wiper position through digital interface |
| Adrian Suciu |
33:c3ec596a29c2 | 47 | rdac.write_cmd(AD5270::WRITE_CTRL_REG, AD5270::RDAC_WRITE_PROTECT, false); // RDAC register write protect - allow update of wiper position through digital interface |
| Adrian Suciu |
33:c3ec596a29c2 | 48 | csrdac = true; |
| Adrian Suciu |
33:c3ec596a29c2 | 49 | wait_us(2); |
| Adrian Suciu |
33:c3ec596a29c2 | 50 | csrdac = false; |
| Adrian Suciu |
33:c3ec596a29c2 | 51 | rdac.write_cmd(AD5270::WRITE_RDAC, R2, false); // write data to the RDAC register |
| Adrian Suciu |
33:c3ec596a29c2 | 52 | rdac.write_cmd(AD5270::WRITE_RDAC, R1, false); // write data to the RDAC register |
| Adrian Suciu |
33:c3ec596a29c2 | 53 | csrdac = true; |
| Adrian Suciu |
33:c3ec596a29c2 | 54 | wait_us(2); |
| Adrian Suciu |
33:c3ec596a29c2 | 55 | csrdac = false; |
| Adrian Suciu |
33:c3ec596a29c2 | 56 | rdac.write_cmd(AD5270::WRITE_CTRL_REG, 0, false); // RDAC register write protect - allow update of wiper position through digital interface |
| Adrian Suciu |
33:c3ec596a29c2 | 57 | rdac.write_cmd(AD5270::WRITE_CTRL_REG, 0, false); // RDAC register write protect - allow update of wiper position through digital interface |
| Adrian Suciu |
33:c3ec596a29c2 | 58 | csrdac = false; |
| Adrian Suciu |
33:c3ec596a29c2 | 59 | wait_us(2); |
| Adrian Suciu |
33:c3ec596a29c2 | 60 | csrdac = false; |
| Adrian Suciu |
33:c3ec596a29c2 | 61 | rdac.write_reg(AD5270::HI_Z_Cmd, false); |
| Adrian Suciu |
33:c3ec596a29c2 | 62 | rdac.write_reg(AD5270::HI_Z_Cmd, false); |
| Adrian Suciu |
33:c3ec596a29c2 | 63 | csrdac = true; |
| Adrian Suciu |
33:c3ec596a29c2 | 64 | wait_us(2); |
| Adrian Suciu |
33:c3ec596a29c2 | 65 | csrdac = false; |
| Adrian Suciu |
33:c3ec596a29c2 | 66 | rdac.write_reg(AD5270::NO_OP_cmd, false); |
| Adrian Suciu |
33:c3ec596a29c2 | 67 | rdac.write_reg(AD5270::NO_OP_cmd, false); |
| Adrian Suciu |
33:c3ec596a29c2 | 68 | csrdac = true; |
| Adrian Suciu |
33:c3ec596a29c2 | 69 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 70 | |
| Adrian Suciu |
33:c3ec596a29c2 | 71 | void CN0396::init() |
| Adrian Suciu |
33:c3ec596a29c2 | 72 | { |
| Adrian Suciu |
33:c3ec596a29c2 | 73 | // set rdac |
| Adrian Suciu |
33:c3ec596a29c2 | 74 | |
| Adrian Suciu |
33:c3ec596a29c2 | 75 | pc.printf("Computing resistor values \r\n"); |
| Adrian Suciu |
33:c3ec596a29c2 | 76 | |
| Adrian Suciu |
33:c3ec596a29c2 | 77 | resistance1 = get_feedback_resistor_value(CO_SENS, CO_RANGE ); |
| Adrian Suciu |
33:c3ec596a29c2 | 78 | resistance0 = get_feedback_resistor_value(H2S_SENS, H2S_RANGE); |
| Adrian Suciu |
33:c3ec596a29c2 | 79 | |
| Adrian Suciu |
33:c3ec596a29c2 | 80 | pc.printf("R1 = %f\r\nR2=%f\r\n", resistance0, resistance1); |
| Adrian Suciu |
33:c3ec596a29c2 | 81 | pc.printf("Configuring feedback resistors\r\n"); |
| Adrian Suciu |
33:c3ec596a29c2 | 82 | configure_feedback_resistors(resistance1, resistance1); |
| Adrian Suciu |
33:c3ec596a29c2 | 83 | pc.printf("Done\r\n"); |
| Adrian Suciu |
33:c3ec596a29c2 | 84 | // config temp |
| Adrian Suciu |
33:c3ec596a29c2 | 85 | pc.printf("Configuring temperature sensor\r\n"); |
| Adrian Suciu |
33:c3ec596a29c2 | 86 | temp.reset(); |
| Adrian Suciu |
33:c3ec596a29c2 | 87 | temp.write_config(0x90); |
| Adrian Suciu |
33:c3ec596a29c2 | 88 | pc.printf("Done\r\n"); |
| Adrian Suciu |
33:c3ec596a29c2 | 89 | |
| Adrian Suciu |
33:c3ec596a29c2 | 90 | pc.printf("Configuring ADC\r\n"); |
| Adrian Suciu |
33:c3ec596a29c2 | 91 | ad.reset(); |
| Adrian Suciu |
33:c3ec596a29c2 | 92 | if(ad.init()) { |
| Adrian Suciu |
33:c3ec596a29c2 | 93 | ad.set_coding_mode(AD7798_UNIPOLAR); |
| Adrian Suciu |
33:c3ec596a29c2 | 94 | ad.set_mode(AD7798_MODE_SINGLE); |
| Adrian Suciu |
33:c3ec596a29c2 | 95 | ad.set_gain(ADC_GAIN); |
| Adrian Suciu |
33:c3ec596a29c2 | 96 | ad.set_filter(ADC_SPS); |
| Adrian Suciu |
33:c3ec596a29c2 | 97 | ad.set_reference(AD7798_REFDET_ENA); |
| Adrian Suciu |
33:c3ec596a29c2 | 98 | pc.printf("ADC Config succesful\r\n"); |
| Adrian Suciu |
33:c3ec596a29c2 | 99 | } else { |
| Adrian Suciu |
33:c3ec596a29c2 | 100 | pc.printf("ADC Config failed\r\n"); |
| Adrian Suciu |
33:c3ec596a29c2 | 101 | |
| Adrian Suciu |
33:c3ec596a29c2 | 102 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 103 | |
| Adrian Suciu |
33:c3ec596a29c2 | 104 | |
| Adrian Suciu |
33:c3ec596a29c2 | 105 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 106 | |
| Adrian Suciu |
33:c3ec596a29c2 | 107 | float CN0396::compensate_ppm(float result, float temp, sensor_type_t sensor) |
| Adrian Suciu |
33:c3ec596a29c2 | 108 | { |
| Adrian Suciu |
33:c3ec596a29c2 | 109 | for(uint8_t i = 1; i < COMPENSATION_TABLE_SIZE; i++) { |
| Adrian Suciu |
33:c3ec596a29c2 | 110 | if(temp < ppm_compensation[i].temp && temp > ppm_compensation[i - 1].temp) { |
| Adrian Suciu |
33:c3ec596a29c2 | 111 | float compensation_coef; |
| Adrian Suciu |
33:c3ec596a29c2 | 112 | if(sensor == H2S_SENSOR) { |
| Adrian Suciu |
33:c3ec596a29c2 | 113 | 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; |
| Adrian Suciu |
33:c3ec596a29c2 | 114 | } else { |
| Adrian Suciu |
33:c3ec596a29c2 | 115 | 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; |
| Adrian Suciu |
33:c3ec596a29c2 | 116 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 117 | |
| Adrian Suciu |
33:c3ec596a29c2 | 118 | return (result * compensation_coef) / 100.0; |
| Adrian Suciu |
33:c3ec596a29c2 | 119 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 120 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 121 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 122 | void CN0396::read() |
| Adrian Suciu |
33:c3ec596a29c2 | 123 | { |
| Adrian Suciu |
33:c3ec596a29c2 | 124 | uint16_t data0, data1; |
| Adrian Suciu |
33:c3ec596a29c2 | 125 | // read temperature |
| Adrian Suciu |
33:c3ec596a29c2 | 126 | uint16_t temp_data = temp.read_temp(); |
| Adrian Suciu |
33:c3ec596a29c2 | 127 | float temp = 0; |
| Adrian Suciu |
33:c3ec596a29c2 | 128 | |
| Adrian Suciu |
33:c3ec596a29c2 | 129 | if(temp_data & 0x8000) { |
| Adrian Suciu |
33:c3ec596a29c2 | 130 | temp = (temp_data - 65536) / (128.0); |
| Adrian Suciu |
33:c3ec596a29c2 | 131 | } else { |
| Adrian Suciu |
33:c3ec596a29c2 | 132 | temp = temp_data / (128.0); |
| Adrian Suciu |
33:c3ec596a29c2 | 133 | } |
| Adrian Suciu |
33:c3ec596a29c2 | 134 | |
| Adrian Suciu |
33:c3ec596a29c2 | 135 | // read channels |
| Adrian Suciu |
33:c3ec596a29c2 | 136 | ad.set_channel(0); |
| Adrian Suciu |
33:c3ec596a29c2 | 137 | ad.read_data(0, &data0); |
| Adrian Suciu |
33:c3ec596a29c2 | 138 | float volt0; |
| Adrian Suciu |
33:c3ec596a29c2 | 139 | data_to_voltage(data0, &volt0); |
| Adrian Suciu |
33:c3ec596a29c2 | 140 | float result0 = (volt0 / resistance0) / CO_SENS; |
| Adrian Suciu |
33:c3ec596a29c2 | 141 | ad.set_channel(1); |
| Adrian Suciu |
33:c3ec596a29c2 | 142 | ad.read_data(1, &data1); |
| Adrian Suciu |
33:c3ec596a29c2 | 143 | float volt1; |
| Adrian Suciu |
33:c3ec596a29c2 | 144 | data_to_voltage(data1, &volt1); |
| Adrian Suciu |
33:c3ec596a29c2 | 145 | float result1 = (volt1 / resistance1) / H2S_SENS; |
| Adrian Suciu |
33:c3ec596a29c2 | 146 | // compute ppm based on formula |
| Adrian Suciu |
33:c3ec596a29c2 | 147 | // return ppm |
| Adrian Suciu |
33:c3ec596a29c2 | 148 | result0 = compensate_ppm(result0, temp, CO_SENSOR); |
| Adrian Suciu |
33:c3ec596a29c2 | 149 | result1 = compensate_ppm(result1, temp, H2S_SENSOR); |
| Adrian Suciu |
33:c3ec596a29c2 | 150 | |
| Adrian Suciu |
33:c3ec596a29c2 | 151 | pc.printf("%f %f %f \r\n", temp, result0, result1); |
| Adrian Suciu |
33:c3ec596a29c2 | 152 | } |
CN0357 - Toxic gas measurement
CN0216 - Weight Scale