Friday, August 1, 2014

Using Texas Instruments Tiva Microcontroller Temperature Sensor

The Tiva line of processors from Texas Instruments has an internal temperature sensor. Code to do this is below. To use this,
  1. Call halTemperatureSensorInit() to initialize the sensor and ADC. Only needs to be done once.
  2. Call halGetTemperature() to get the temperature in Celsius
  3. Optionally, call halConvertTemperatureFromCtoF to convert it to Fahrenheit
Example code is below.

/**
* Tiva Microcontroller Internal Temperature Utility Functions
*
* @section license License
* This work is PUBLIC DOMAIN
*
* YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS”
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO
* EVENT SHALL  TESLA CONTROLS BE LIABLE OR OBLIGATED UNDER CONTRACT, NEGLIGENCE,
* STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR
* INDIRECT DAMAGES OR EXPENSE INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT,
* PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE
* GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY
* DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
*/

/**
 * Initialize the ADC for use by the temperature sensor.
 * @see TivaWare/examples/peripherals/adc/temperature_sensor.c
 * @post temperature sensor can be read with halGetTemperature()
 */
static void halTemperatureSensorInit()
{
    /** Enable the ADC */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);

#define ADC_SEQUENCE    3
#define ADC_STEP        0

    /* Enable sample sequence 3 with a processor signal trigger.
     * Sequence 3 will do a single sample when the processor sends a signal to start the conversion. */
    ADCSequenceConfigure(ADC0_BASE, ADC_SEQUENCE, ADC_TRIGGER_PROCESSOR, 0);

    /* Configure step 0 on sequence 3:
     * - Sample the temperature sensor (ADC_CTL_TS) in single-ended mode (default)
     * - Configure the interrupt flag (ADC_CTL_IE) to be set when the sample is done
     * - Tell the ADC logic that this is the last conversion on sequence 3 (ADC_CTL_END). */
    ADCSequenceStepConfigure(ADC0_BASE, ADC_SEQUENCE, ADC_STEP, ADC_CTL_TS | ADC_CTL_IE | ADC_CTL_END);

    /* Enable the ADC Sequence we're using */
    ADCSequenceEnable(ADC0_BASE, ADC_SEQUENCE);

    /* Clear the interrupt status flag. This is done to make sure the interrupt flag is cleared before we sample. */
    ADCIntClear(ADC0_BASE, ADC_SEQUENCE);
}


/**
 * Read the processor's internal temperature sensor. Accuracy is +/-5C
 * @pre halTemperatureSensorInit() was called to initialize the ADC
 * @return temperature of processor in degrees Celsius
 * @note to convert: VTSENS = 2.7 - ((TEMP + 55) / 75)
 */
int32_t halGetTemperature()
{
    /* This array is used for storing the data read from the ADC FIFO. It must be as large as the
    FIFO for the sequencer in use.  This example uses sequence 3 which has a FIFO depth of 1. */
    uint32_t ulADC0_Value[1];

    /* Our output value */
    int32_t temperatureInDegreesC;

    /* Manually Trigger the ADC conversion */
    ADCProcessorTrigger(ADC0_BASE, 3);

    /* Wait for conversion to be completed */
    while(!ADCIntStatus(ADC0_BASE, 3, false))
    {
    }

    /* Clear the ADC interrupt flag. */
    ADCIntClear(ADC0_BASE, 3);

    /* Read ADC Value. */
    ADCSequenceDataGet(ADC0_BASE, 3, ulADC0_Value);

    //UARTprintf("ADC Value = %u\n", ulADC0_Value[0]);

    /* Reference voltage of 3.3V, in mV.  */
#define REFERENCE_VOLTAGE_MV                (3300l)

    /* ADC is 12 bit resolution */
#define NUMBER_OF_STEPS_12_BIT_RESOLUTION   (4096l)

    /* The voltage from the ADC, in millivolts */
    uint32_t adcMv = (uint32_t) ((ulADC0_Value[0] * REFERENCE_VOLTAGE_MV) / NUMBER_OF_STEPS_12_BIT_RESOLUTION);

    /* Use non-calibrated conversion provided in the data sheet. Divide last to avoid dropout. */
    temperatureInDegreesC = (147500l - 75l * adcMv) / 1000;

    return temperatureInDegreesC;
}


/**
 * Utility function to convert a celsius temperature to fahrenheit.
 * @param temperatureInDegreesC the temp in C
 * @return the temp in F
 */
int32_t halConvertTemperatureFromCtoF(int32_t temperatureInDegreesC)
{
    return ((temperatureInDegreesC * 9) + 160) / 5;
}