ADC Values ofsett.

Hello,

We have a sensor with 2 ADCs inputs meauring identical LMT70s on the board. With one of our older firmwares we get the accurate valudes of 22deg or so on both channels. But in intergrating nusim the tempratures seems to have ofssets of 5-10 degree just with a firmware change, and it gets reversed when the older fimware is installed. Is there something in the config or overlay that could cause this issue?

adcSetup();
uint16_t pipeTemp = 0;
uint16_t airTemp = 0;
while(1){
    measureADC(&pipeTemp, &airTemp);
    printf("%04d:%04d\n", pipeTemp, airTemp);
    k_msleep(100);
}

2478:3053
2489:3070
2483:3076
2483:3065
2483:3076
2489:3070
2483:3065
2483:3065
2483:3070
2466:3059
2466:3053

The rest of the code that calls these functions is below. I get much closer values with the code without the software sim. 

/**
 * @brief This function configures two ADC channels (ADC0 and ADC1) for reading temperature values.
 *
 * It sets up specific configurations for both channels, such as gain, reference voltage,
 * acquisition time, channel_id, positive input, and differential mode. It's worth noting
 * that the ADC_REF_INTERNAL (0.6V) and ADC_GAIN_1_2 (0.5x input multiplier) are used to
 * set a measurement range of 0-1.2V.
 *
 * Before setting up the channels, the function checks if the ADC device is ready, returning
 * an error if it is not. It also triggers an offset calibration task on the SAADC.
 *
 * The ADC channels are set up using the `adc_channel_setup` function. If an error occurs
 * during this process, the function will print an error message and return the error code.
 *
 * @return: The function returns 0 if both ADC channels are successfully set up. If the ADC
 * device is not ready or if an error occurs while setting up an ADC channel, it will return
 * a negative error code.
 */
int adcSetup() {
    // ADC0 Pipe ADC Channel
    struct adc_channel_cfg adc0 = {
        .gain = ADC_GAIN_1_2,                      // 0.2x input multiplier
        .reference = ADC_REF_INTERNAL,             // 0.6V Internal refrence
        .acquisition_time = ADC_ACQ_TIME_DEFAULT,  // 0.6V / 0.5 input gain = 0-1.2V Range
        .channel_id = 0,
        .input_positive = SAADC_CH_PSELN_PSELN_AnalogInput0,
        .differential = SAADC_CH_CONFIG_MODE_SE,
    };

    // ADC1 Air ADC Channel
    struct adc_channel_cfg adc1 = {
        .gain = ADC_GAIN_1_2,                      // 0.2x input multiplier
        .reference = ADC_REF_INTERNAL,             // 0.6V Internal refrence
        .acquisition_time = ADC_ACQ_TIME_DEFAULT,  // 0.6V / 0.5 input gain = 0-1.2V Range
        .channel_id = 1,
        .input_positive = SAADC_CH_PSELN_PSELN_AnalogInput1,
        .differential = SAADC_CH_CONFIG_MODE_SE,
    };

    if (!device_is_ready(adc_dev)) {
        printk("ADC device not ready \n");
        return -1;
    }

    // Start calibration
    NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Enabled;
    NRF_SAADC->EVENTS_CALIBRATEDONE = 0;  // Clear the event flag
    NRF_SAADC->TASKS_CALIBRATEOFFSET = 1;
    NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Disabled;

    int err = adc_channel_setup(adc_dev, &adc0);
    if (err != 0) {
        printk("ADC0 (Pipe) setup failed with error %d.\n", err);
        return err;
    }
    err = adc_channel_setup(adc_dev, &adc1);
    if (err != 0) {
        printk("ADC1 (Air) setup failed with error %d.\n", err);
        return err;
    }

    return 0;
}

uint8_t measureADC(uint16_t *pipeTemp, uint16_t *airTemp) {
    led_on(temp_ctrl_dev, LMT70_PIPE);
    led_on(temp_ctrl_dev, LMT70_AIR);

    // Turn ADC on
    NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Enabled;

    // Calibrate
    NRF_SAADC->EVENTS_CALIBRATEDONE = 0;
    NRF_SAADC->TASKS_CALIBRATEOFFSET = 1;

    // // Blocking 500us wait
    // // LMT70 datasheet page 8.7 pg7
    // // 30us typ, 500us max
    k_busy_wait(30);

    adc_read(adc_dev, &pipeTempSeq);
    *pipeTemp = convertToMilliCelcius(adc_buffer[0]);
    adc_read(adc_dev, &airTempSeq);
    *airTemp = convertToMilliCelcius(adc_buffer[0]);

    led_off(temp_ctrl_dev, LMT70_PIPE);
    led_off(temp_ctrl_dev, LMT70_AIR);

    // Turn ADC off
    NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Disabled;

    return 0;
}


/**
 * @brief This function converts a raw ADC value to a temperature in milliCelsius.
 *
 * @param adcVal: The raw ADC value to be converted. This value should be the result
 * of an ADC read operation.
 *
 * @return: The function returns the temperature calculated from the raw ADC value,
 * rounded to the nearest integer, in milliCelsius. The value of milliCelsius is
 * also printed to the standard output.
 *
 * The function uses a calibrated cubic polynomial function to perform the conversion
 * from the ADC reading to the temperature. The conversion factor and polynomial
 * coefficients are hard-coded and specific to the temperature sensor and the ADC being used.
 */
int16_t convertToMilliCelcius(int16_t adcVal) {
    // ( 0.6V internal refrence * 2 (ADC gain of 0.5^-1)) / 4096 (12b) = 0.00029296875 V/b
    // 0.00029296875 V/b * 1000 mV/V = 0.29296875 mV/b
    double tempAO = (adcVal * 0.29296875);  // ads to raw milliVolts!
    double adcA = -1.809628E-09;
    double adcB = -3.325395E-06;
    double adcC = -1.814103E-01;
    double adcD = 2.055894E+02;
    double mC = adcA * (tempAO) * (tempAO) * (tempAO) + adcB * (tempAO) * (tempAO) + adcC * (tempAO) + adcD;
    int16_t miliCelcius = (int)(mC * 100);
    return miliCelcius;
}

Regards,

Sawaiz 

Related