This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/TM4C129ENCPDT: Internal temperature measurement setup

Part Number: TM4C129ENCPDT

Tool/software: Code Composer Studio

With reference to the http://e2e.ti.com/support/microcontrollers/other/f/908/t/594823 it still seems unclear how to make a correct setup.

In an application we have ADC0 running at full speed (2MHz) and so also ADC1.  ADC1 is only used for the internal temperature measurement.

By using the configuration giving 64 ADC-clocks for sample-hold we get some measurements much lower than expected (range 30-47 degrees C when using heat-gun up till 100 degree C).

We have tried to read out 3-10 consecutive readings (having global interrupt disabled), but still poor readings of temperature.

Only by adding oversampling we are getting useful results: ADCHardwareOversampleConfigure(ADC1_BASE, 64);

This seems strange as this is not logical compared to the errata.

Any good explanation ? and have we missed something ?

The code is shown below:

Initialisation:

   //
    // Set the clock for both ADCs (ADC1_BASE sets the clock for both ADCs)
    // ADC_CLOCK_DIVIDER == 30 -> set the ADC clock to 16MHz corresponding to 1Msp/s
    // ADC_CLOCK_DIVIDER == 15 -> set the ADC clock to 32MHz corresponding to 2Msp/s
    //
    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, ADC_CLOCK_DIVIDER);

//------------------------

//
// Initialize the internal ADC1 peripheral used for internal chip temperature measurement.
// The temperature measurement is done in the -in another function - see below.
//    

//
// Set the sequencer number
//
uint32_t g_uisequencernum = 3;
static void ADC1Init(void)
{

    //
    // The ADC1 peripheral must be enabled for use.
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);

    //
    // Wait for the ADC1 peripheral to be ready.
    //
    while (!MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_ADC1))
    {}

      //
      //set the auto avergage to 64.
    //
    ADCHardwareOversampleConfigure(ADC1_BASE, 64);

    //
    // Make sure ADC1 sample sequencer is disabled before configuring it.
    //
    MAP_ADCSequenceDisable(ADC1_BASE, g_uisequencernum);

    //
    // Enable sample sequence with a processor signal trigger.
    //
    MAP_ADCSequenceConfigure(ADC1_BASE, g_uisequencernum, ADC_TRIGGER_PROCESSOR, 0);

    //
    // Configure step 0 on sequence.
    //
    //MAP_ADCSequenceStepConfigure(ADC1_BASE, 3, 0, ADC_CTL_TS | ADC_CTL_IE | ADC_CTL_END| ADC_CTL_SHOLD_16);
    
    /*
    *         Set up the ADC sequencer for temperature reading
    *
    * ADC_CTL_TS        : Temperature sensor select
    * ADC_CTL_IE        : Interrupt enable
    * ADC_CTL_SHOLD_16    : Sample and hold 16 ADC clocks
    * ADC_CTL_END         : Sequence end select
    *
    */
    MAP_ADCSequenceStepConfigure(ADC1_BASE, g_uisequencernum, 0, ADC_CTL_TS | ADC_CTL_IE | ADC_CTL_SHOLD_64 | ADC_CTL_END);

    //
    // Since sample sequence is now configured, it must be enabled.
    //
    MAP_ADCSequenceEnable(ADC1_BASE, g_uisequencernum);

    //
    // Clear the interrupt status flag.  This is done to make sure the
    // interrupt flag is cleared before we sample.
    //
    MAP_ADCIntClear(ADC1_BASE, ADCIntClear);

    return;
}

Function for reading out the temperature:

{
    uint32_t ui32Temperature = 0; // used for reading values from the adc
    int32_t i32Temperature = 0; // used for calculating the actual temperature
    uint8_t i = 3;
    
#if defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1)
    // If platform is TM4C129
    /*uint32_t n;
    taskENTER_CRITICAL();     //<----- Enter critical section !
    IntMasterDisable();     //<----- Disable interrupts !
    for(n=0;n<1000;n++)
    {
        MAP_ADCIntStatus(ADC1_BASE, 3, false);
    }*/
    //while(!MAP_ADCIntStatus(ADC1_BASE, 3, false)) {} // Wait for conversion to be completed, there could still be some samples left in the ping-pong queue initiated by the normal sample mode.
    for (i=3; i>0; i--) //run 3 loops to meet setling time of temperature sensor internally (see errata: www.ti.com/.../spmz850c.pdf)
    {
        // See TivaWare example in: <tivaware>/examples/peripherals/adc/temperature_sensor.c
        MAP_ADCProcessorTrigger(ADC1_BASE, g_uisequencernum); // Trigger the ADC conversion.
        while(!MAP_ADCIntStatus(ADC1_BASE, g_uisequencernum, false)) {} // Wait for conversion to be completed.
        MAP_ADCIntClear(ADC1_BASE, g_uisequencernum); // Clear the ADC interrupt flag.
        MAP_ADCSequenceDataGet(ADC1_BASE, g_uisequencernum, &ui32Temperature); // Read ADC Value.
        i32Temperature = (int32_t)ui32Temperature;
    }
    /*IntMasterEnable();        //<----- Enable interrupts !
    taskEXIT_CRITICAL();    //<----- Exit critical section !*/
    i32Temperature = (1475-(i32Temperature*2475)/4096)/10; // Use non-calibrated conversion provided in the data sheet.
#else
    // If platform is LM3S9D92
    for (i=3; i>0; i--) //run 3 loops to meet setling time of temperature sensor internally (see errata)
    {
        ADCIntClear(ADC0_BASE, g_uisequencernum); //clear any old interrupts
        ADCProcessorTrigger(ADC0_BASE, g_uisequencernum); //Initiate the next adc-temperature conversion
        while(!ADCIntStatus(ADC0_BASE, g_uisequencernum, false)) {} // Wait until the sample sequence has completed.
        ADCSequenceDataGet(ADC0_BASE, g_uisequencernum, &ui32Temperature);
        i32Temperature = (int32_t)ui32Temperature;
    }
    i32Temperature = 147-((i32Temperature*225)/1024); // calculate in celcius
#endif

   pushinteger(int32_t)i32Temperature;

    return 1;
}

  • I tried to reproduce your results, but did not. I did see a much more stable reading using hardware oversampling, but this is because it averaged out the noise on my reference voltage (I was using a Launchpad). I saw no issues with the response to external temperature change.
  • At the input pin PortE3 we have a voltage divider (220ohm/10kohm) based on the digital Vcc(=3,38V measured) so the input voltage to the pin is slightly below Vcc. The PE3 is not initialized, so high impedance input. By lowering this input voltage, the temperature measurement becomes normal.

    We tried to make the E3 an analog input (Ain0), but still the same result.

    We assume that some input protection circuit becomes active and makes influence on the temperature measurement.

    Is this expected behavior ?

  • As long as you are below Vccad, the input protection circuit is not the issue. It does appear that between samples the analog multiplexor switch defaults to channel 0, which means the voltage on pin PE3 can affect the ADC results if the source resistance is too high for the allocated sample time. The source resistance on the temperature sensor is high. Running the ADC at 16MHz and using only a 4 clock sample period, I saw about a 10C difference when PE3 was tied to 3.3V as opposed to GND. When I increased the sample time to 16 clock cycles, that difference disappeared.

        ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_TS | ADC_CTL_IE |
                                 ADC_CTL_SHOLD_16 | ADC_CTL_END);
    

  • Actually we also tried to use more clock cycles for the sample time, but it still gave low readings for higher temperatures like 80 degrees Celcius.

    It's of interest if this behavior also applies for other analog inputs, as we use the processor to do measurements in the full 12 bit range at Ain1 and Ain2. It's of interest if the documentation about source resistance versus sample time is still valid ?

    We know about the errata for Ain0 (ADC#13) and the errata for the temperature sensor (ADC#09), but maybe the text should be extended for this behavior.

  • The issue is a universal one of ADCs in that the previous charge on the sample capacitor will affect the next conversion if the sample time is not long enough for the sample capacitor to equalize to the external voltage to be converted. The maximum resistance of the analog multiplexor (2.5K Ohm) and the maximum capacitance of the sample capacitor (10pF) are documented in the datasheet.

  • You are right, that the charging of the sample capacitor is documented in the data sheet. But it require some very clever reading to understand the sequencers actual sequence. By knowing the actual 'where does it come from' level and input, we can take this into account for precise high speed measurements. Surprising is it, that even when A0 is configured as a digital input it seems to be 'where it comes from input pin' to the temperatures sensor. It also seems from our measurements, that this pin affect both of the ADC channels A0 and A1 in terms of memory from last sample of the charge capacitor. We do have some serial resistors on other high speed ADC inputs to slow down high frequency ringing, but it seems that even 100 ohm is a high impedance when it comes to charging of the sample capacitor.
    Question: is it possible to have a more detailed description of the actaul sequence that the capacitor will see ?
  • The ADC sequencer will connect the analog multiplexor channel selected for each step during the sample time and disconnect during the conversion time. The resultant voltage retained on the sample capacitor will affect the next step's channel if there is insufficient sample time. The anecdotal evidence suggests that between sequences, the multiplexor connects analog channel 0. Whichever channel is selected during the first step will be affected by the voltage on the sample capacitor from channel 0 if there is insufficient sample time.

    Peter Johansen said:
    it seems that even 100 ohm is a high impedance when it comes to charging of the sample capacitor

    The internal resistance of the analog multiplexor can be up to 2.5K Ohms. Even with that, I have not seen a problem when using the standard 4 cycle sample time on a low impedance (100 Ohm) source.

  • Thank you for the discussion and validation as far as it has been possible.