powerpic

Replacement board for a Casio CA-53W

/** @file temperature.c
 * 
 * This library implements reading the internal temperature sensor and
 * converting it to degrees.
*/

#include <xc.h>

#include "drivers/adc.h"
#include "drivers/fvr.h"
#include "drivers/nvm.h"

#define LOG_TAG "lib.temperature"
#include "lib/logging.h"

#include "lib/temperature.h"


static int          temperature_cal_degrees = 0;
static unsigned int temperature_cal_adc = 0;

static void temperature_adc_config (void);

int
temperature_read (void)
{
    // Configure ADC for temp sensor use.
    temperature_adc_config();

    // Take a reading
    adc_sample();
    while (adc_status())
    {
        // Wait until conversion is complete
    }
    unsigned int average = (unsigned int)adc_result();

    // 10 Samples
    //
    for (int res = 0; res < 10; res++)
    {
        adc_sample();
        
        while (adc_status())
        {
            // Wait until conversion is complete
        }

        average = (average + (unsigned int)adc_result()) / 2;
    }
    LOG_DEBUG("Raw: %u", average);

    // Calculate temperature based on a calibration reading at 25C. This gives
    // a good range for the middle of the temperature range.
    // This is not very accurate.
    // NOTE: This formula was borrowed from Microchip Forum user mbrowning.(1)
    //     Thanks for your help.
    // (1) https://www.microchip.com/forums/m1214187.aspx
    //
    // long tmp32s = 25 + ((long)(average - 2775L) * 2048L / -15565L);
    long tmp32s = temperature_cal_degrees + ((long)(average - (long)temperature_cal_adc) * 2048L / -15565L);
    int temp_c = (int)tmp32s;
    LOG_DEBUG("Cal@90: %i", temp_c);

    adc_disable();
    fvr_temp_set(FVR_TEMP_OFF);
    fvr_disable();

    return temp_c;
}

void
temperature_calibrate (int degrees)
{
    // To calibrate we take an adc reading of the temperature sensor,
    // and then relate that to a given degrees. We then use this relation in
    // our calculation of actual temp.

    // Configure ADC for temp sensor use.
    temperature_adc_config();

    // Take a reading
    adc_sample();
    while (adc_status())
    {
        // Wait until conversion is complete
    }
    unsigned int average = (unsigned int)adc_result();

    // 10 Samples
    //
    for (int res = 0; res < 10; res++)
    {
        adc_sample();
        
        while (adc_status())
        {
            // Wait until conversion is complete
        }

        average = (average + (unsigned int)adc_result()) / 2;
    }

    // Record the ADC result and given degrees
    temperature_cal_adc = average;
    temperature_cal_degrees = degrees;
}

static void
temperature_adc_config (void)
{
    // Read the calibration value if not set
    //
    if ((0 == temperature_cal_adc) && (0 == temperature_cal_degrees))
    {
        // Read factory calibration stored in DIA.
        temperature_cal_adc = nvm_read(1, 0x0116);
        LOG_DEBUG("90C TSHR: %u", temperature_cal_adc);

        // Set to factory calibration temperature (90C)
        temperature_cal_degrees = 90;
    }

    // Set FVR to output 2.048V to the ADC, and FVR as ADC reference.
    //
    fvr_adc_set(FVR_GAIN_2X);
    adc_reference_set(ADC_REF_FVR);

    // Set our ADC channel to the temperature sensor.
    //
    adc_channel_set(ADC_CHAN_TEMP_SENSOR);

    // Enable TS, FVR, and ADC.
    //
    fvr_enable();
    fvr_temp_set(FVR_TEMP_HIGH);
    adc_enable();

    // Wait for ADC to settle after configuring it to the temp sensor.
    // (Minimum 25us)
    __delay_us(50);
}

// EOF //