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.

MSP430F5418A: How can I read the internal temperature?

Part Number: MSP430F5418A


Hi,

ADC12 channels 0-6 are taken for other ADC readings, so I want to output the temperature on ADC12MEM7.

Until now, an external Temperature sensor has been  used. I need to read the MSP430's internal temperature sensor.

In the User's Guide:

28.2.8 Using the Integrated Temperature Sensor
To use the on-chip temperature sensor, select the analog input channel INCHx = 1010. Any other
configuration is done as if an external channel were selected, including reference selection and
conversion-memory selection.

The temperature sensor is part of the reference. Therefore, for devices with
the REF module, in addition to the input channels selection INCHx = 1010, configuring ADC12REFON = 1
(for REFMSTR = 0) or REFON = 1 (for REFMSTR = 1) is required to enable the temperature sensor.
For the MSP430F54xx (non-A) devices, which do not include the REF module, selecting the temperature
sensor by configuring INCHx = 1010 automatically enables the reference generator required for the
temperature sensor. Any other configuration is done as if an external channel were selected, including
reference selection and conversion-memory selection.

I tried to use the example program msp430x54x_adc12_10.c - but I'm not getting any values that resemble temperature values...

Any help would be appreciated.

Thanks,

Mechi

  • Hi Mechi,

    Can you please share what values are you getting for temperatureDegC and temperatureDegF? 

  • Hi Aaron,

    Thanks for the prompt reply.

    Here's the code:

    // Initializes the I/O devices (I/O ports' direction and output values, A2D conversion)
    void InitDev()
    {
    //...
    
        /* The A/D Control Register */
      
       
      ADC12CTL0 = ADC12ON +  ADC12MSC + ADC12SHT03 + ADC12REFON;   // Sample 32 ADC12CLK cycles - turn REF on for Temperature.
                                                                                                                                   // Multiple sample and conversion.
      ADC12CTL1 = ADC12SHP + ADC12DIV_7 + ADC12SSEL_1 + ADC12CONSEQ_1 ; // Sequence conversion, ADC12CLK: ACLK/8.
    
      /* Set the Conversion Memory Registers (a sequence that starts with A0 and ends with A1. */
      
      ADC12MCTL0 = ADC12INCH_2 + ADC12REF2_5V; // ADC12MEM0 will read A2 
      ADC12MCTL1 = ADC12INCH_3 + ADC12REF2_5V; // ADC12MEM1 will read A3 
      ADC12MCTL2 = ADC12INCH_1 + ADC12REF2_5V; // ADC12MEM2 will read A1 
      ADC12MCTL3 = ADC12INCH_0 + ADC12REF2_5V; // ADC12MEM3 - will read A0 
      ADC12MCTL4 = ADC12INCH_4 + ADC12REF2_5V; // ADC12MEM4 - will read A4 
      ADC12MCTL5 = ADC12INCH_5 + ADC12REF2_5V; // ADC12MEM5 - will read A5 
      ADC12MCTL6 = ADC12INCH_6 + ADC12REF2_5V; // ADC12MEM6 - will read A6 
      ADC12MCTL7 = ADC12INCH_10 + ADC12REF2_5V + ADC12EOS; // ADC12MEM7 - will read A10 -temperature sensor
                                                                                                                    // End of Sequence for the last channel
      
      ADC12IE = 0x01; // A/D Interrupt Enable
    }
    
    
    // The A2D interrupt - called when the conversion of the last channel in the sequence is complete.
    // Reads the channel digital values after conversion.
    #pragma vector=ADC12_VECTOR
    __interrupt void Sampler(void)
    {
      while ( (ADC12IFG & ADC12BUSY) == 0); // ADC12IFG - Interrupt Flag
    
        smp_acc_0 += ADC12MEM0;
        smp_acc_1 += ADC12MEM1;
        smp_acc_2 += ADC12MEM2;
        smp_acc_3 += ADC12MEM3;
        smp_acc_4 += ADC12MEM4;
        smp_acc_5 += ADC12MEM5;
        smp_acc_6 += ADC12MEM6;
        smp_acc_7 += ADC12MEM7;   // temperature
    
    
      ADC12CTL0 &= ~ADC12ENC;       // Disable conversion
      ADC12CTL0 &= ~ADC12SC;   // Start A/D conversion
    }
    

    You were following my question from another thread.

    Here too, this code worked with IAR. I went back to the example code (mentioned above) to see if all of the steps are done correctly.

    The other A2D channels (Voltages, etc. are read with actual values) but the Temperature (ADC12MEM7) comes out with very very low values.

    In the sample, the code for conversion:

        // Temperature in Celsius
        // ((A10/4096*1500mV) - 894mV)*(1/3.66mV) = (A10/4096*410) - 244
        // = (A10 - 2438) * (410 / 4096)
        IntDegC = ((temp - 2438) * 410) / 4096;
    
        // Temperature in Fahrenheit
        // ((A10/4096*1500mV) - 829mV)*(1/2.033mV) = (A10/4096*738) - 408
        // = (A10 - 2264) * (738 / 4096)
        IntDegF = ((temp - 2264) * 738) / 4096;

    So the final value ends up being very very low...

  • I sometimes get readings of 0xb or 0xE or 0x7 in ADC12MEM7 - but nothing higher

  • What results do you get with msp430x54x_adc12_10.c ? (Just set a breakpoint and look at the variables.) That experiment will tell you which direction to look.

    -----------------

    ADC12MCTL7 = ADC12INCH_10 + ADC12REF2_5V + ADC12EOS; // ADC12MEM7 - will read A10 -temperature sensor

    The ADC12REF2_5V bit is in ADC12CTL0, not ADC12MCTLx. This requests SREF=4, which is, um, I'm not sure what it means exactly. I suspect you wanted:

    ADC12MCTL7 = ADC12INCH_10 + ADC12SREF_1 + ADC12EOS; // ADC12MEM7 - will read A10 -temperature sensor (VREF vs VSS)

    -----------------

    >ADC12IE= 0x01;

    This sets ADC12IE0, which will fire after the first conversion is done. Particularly with such a slow ADC clock (ACLK/8, SHT=256) this will be long before the ADC has reached MCTL7. Try:

    >ADC12IE = ADC12IE7;   // Interrupt after the 8th conversion

    -----------------

    >  while ( (ADC12IFG & ADC12BUSY) == 0);

    This test is backwards, so it either won't wait at all or will wait forever. With the IE change above, you don't need this test, but the proper form would be:

    >  while ( (ADC12IFG & ADC12BUSY) != 0);

    -----------------

    // Temperature in Celsius
    // ((A10/4096*1500mV) - 894mV)*(1/3.66mV) = (A10/4096*410) - 244
    // = (A10 - 2438) * (410 / 4096)
    IntDegC = ((temp - 2438) * 410) / 4096;
    I'm not completely sure where all these constants are coming from, but it looks as though you're assuming a 1.5V reference, but you seem to want a 2.5V reference. It's also important to use the correct calibration constants for the reference you're using. [Ref User Guide (SLAU208Q) Sec 1.13.5.3, or just adapt the code from the Example.]
    [Edit: Fixed section reference]
  • Thanks for the very detailed answer!

    I took all of your suggestions - but still I don't get temperature readings.

    I ran the example (msp430x54x_adc12_10.c) - and I'm only getting values between 0x00 and 0x12 - in a cyclical manner (values go up from 0 - 5 - 10 - 16 and then back down to 0).

    BTW - the constants for calculating Fahrenheit and Celcius are in the example itself.

    Thanks again,

    Mechi

  • >BTW - the constants for calculating Fahrenheit and Celcius are in the example itself.

    What I see in the Example is:

    > temperatureDegC = (float)(((long)temp - CALADC12_15V_30C) * (85 - 30)) /
    >                                 (CALADC12_15V_85C - CALADC12_15V_30C) + 30.0f;

    where the interesting constants are in your chip (I don't know what they are).

    ----------------

    Where do you see the small-integer results?

  • /* --COPYRIGHT--,BSD_EX
     * Copyright (c) 2012, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     *******************************************************************************
     * 
     *                       MSP430 CODE EXAMPLE DISCLAIMER
     *
     * MSP430 code examples are self-contained low-level programs that typically
     * demonstrate a single peripheral function or device feature in a highly
     * concise manner. For this the code may rely on the device's power-on default
     * register values and settings such as the clock configuration and care must
     * be taken when combining code from several examples to avoid potential side
     * effects. Also see www.ti.com/grace for a GUI- and www.ti.com/msp430ware
     * for an API functional library-approach to peripheral configuration.
     *
     * --/COPYRIGHT--*/
    //******************************************************************************
    //  MSP430F54x Demo - ADC12, Sample A10 Temp and Convert to oC and oF
    //
    //  Description: A single sample is made on A10 with reference to internal
    //  1.5V Vref. Software sets ADC12SC to start sample and conversion - ADC12SC
    //  automatically cleared at EOC. ADC12 internal oscillator times sample
    //  and conversion. In Mainloop MSP430 waits in LPM4 to save power until
    //  ADC10 conversion complete, ADC12_ISR will force exit from any LPMx in
    //  Mainloop on reti.
    //  ACLK = n/a, MCLK = SMCLK = default DCO ~ 1.045MHz, ADC12CLK = ADC12OSC
    //
    //  Uncalibrated temperature measured from device to devive will vary do to
    //  slope and offset variance from device to device - please see datasheet.
    //
    //                MSP430F5438
    //             -----------------
    //         /|\|              XIN|-
    //          | |                 |
    //          --|RST          XOUT|-
    //            |                 |
    //            |A10              |
    //
    //   W. Goh
    //   Texas Instruments Inc.
    //   November 2008
    //   Built with CCE Version: 3.2.2 and IAR Embedded Workbench Version: 4.11B
    //******************************************************************************
    #include <msp430.h>
    
    volatile long temp;
    volatile long IntDegF;
    volatile long IntDegC;
    
    int main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
      ADC12CTL0 = ADC12SHT0_8 + ADC12REFON + ADC12ON;
                                                // Internal ref = 1.5V
      ADC12CTL1 = ADC12SHP;                     // enable sample timer
      ADC12MCTL0 = ADC12SREF_1 + ADC12INCH_10;  // ADC i/p ch A10 = temp sense i/p
      ADC12IE = 0x001;                          // ADC_IFG upon conv result-ADCMEMO
      __delay_cycles(37);                       // 35us delay to allow Ref to settle
                                                // based on default DCO frequency.
                                                // See Datasheet for typical settle
                                                // time.
      ADC12CTL0 |= ADC12ENC;
    
      while(1)
      {
        ADC12CTL0 |= ADC12SC;                   // Sampling and conversion start
    
        __bis_SR_register(LPM4_bits + GIE);     // LPM0 with interrupts enabled
        __no_operation();
    
        // Temperature in Celsius
        // ((A10/4096*1500mV) - 894mV)*(1/3.66mV) = (A10/4096*410) - 244
        // = (A10 - 2438) * (410 / 4096)
        IntDegC = ((temp - 2438) * 410) / 4096;
    
        // Temperature in Fahrenheit
        // ((A10/4096*1500mV) - 829mV)*(1/2.033mV) = (A10/4096*738) - 408
        // = (A10 - 2264) * (738 / 4096)
        IntDegF = ((temp - 2264) * 738) / 4096;
        __no_operation();                       // SET BREAKPOINT HERE
      }
    }
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=ADC12_VECTOR
    __interrupt void ADC12ISR (void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(ADC12_VECTOR))) ADC12ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(ADC12IV,34))
      {
      case  0: break;                           // Vector  0:  No interrupt
      case  2: break;                           // Vector  2:  ADC overflow
      case  4: break;                           // Vector  4:  ADC timing overflow
      case  6:                                  // Vector  6:  ADC12IFG0
        temp = ADC12MEM0;                       // Move results, IFG is cleared
        __bic_SR_register_on_exit(LPM4_bits);   // Exit active CPU
        break;
      case  8: break;                           // Vector  8:  ADC12IFG1
      case 10: break;                           // Vector 10:  ADC12IFG2
      case 12: break;                           // Vector 12:  ADC12IFG3
      case 14: break;                           // Vector 14:  ADC12IFG4
      case 16: break;                           // Vector 16:  ADC12IFG5
      case 18: break;                           // Vector 18:  ADC12IFG6
      case 20: break;                           // Vector 20:  ADC12IFG7
      case 22: break;                           // Vector 22:  ADC12IFG8
      case 24: break;                           // Vector 24:  ADC12IFG9
      case 26: break;                           // Vector 26:  ADC12IFG10
      case 28: break;                           // Vector 28:  ADC12IFG11
      case 30: break;                           // Vector 30:  ADC12IFG12
      case 32: break;                           // Vector 32:  ADC12IFG13
      case 34: break;                           // Vector 34:  ADC12IFG14
      default: break;
      }
    }
    

    I attached the example which I followed.

    The small values I read from ADC12MEM7 - in the interrupt. The other channels have much larger values...

  • The code you attached doesn't use ADC12MEM7, rather ADC12MEM0. Also the arithmetic looks different from TI's (TiRex) version here:

    https://dev.ti.com/tirex/explore/node?node=APsQQTgq5NLZnfNWrGyOlg__IOGqZri__LATEST

    --------------------

    I don't have your equipment, but here's what I see using the analogous (and very similar) Example on the F5529:

  • The sample you show is really different than the only I downloaded...

    One difference is this line:

      /* Initialize the shared reference module */
      REFCTL0 |= REFMSTR + REFVSEL_0 + REFON;    // Enable internal 1.5V reference
    

    When I tried the sample you mentioned, there are no readings at all.

    How can I transfer the Temperature reading to ADC12MEM7 - which is the only ADC channel free in our device?

    This is what I tried - but the interrupt is not called:

    int main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
    
      /* Initialize the shared reference module */
      REFCTL0 |= REFMSTR + REFVSEL_0 + REFON;    // Enable internal 1.5V reference
    
      /* Initialize ADC12_A */
      ADC12CTL0 = ADC12SHT0_8 + ADC12ON + ADC12REFON;            // Set sample time
      ADC12CTL1 = ADC12SHP;                     // Enable sample timer
      ADC12MCTL7 = ADC12SREF_1 + ADC12INCH_10;  // ADC input ch A10 => temp sense
      ADC12IE = ADC12IE7; //  0x0001            // ADC_IFG upon conv result-ADCMEMO
    
      __delay_cycles(75);                       // delay to allow Ref to settle
                                                // based on default DCO frequency.
                                                // See Datasheet for typical settle
                                                // time.
      ADC12CTL0 |= ADC12ENC;
    
      while(1)
      {
        ADC12CTL0 &= ~ADC12SC;
        ADC12CTL0 |= ADC12SC;                   // Sampling and conversion start
    
        __bis_SR_register(LPM4_bits + GIE);     // LPM4 with interrupts enabled
        __no_operation();
    
        // Temperature in Celsius. See the Device Descriptor Table section in the
        // System Resets, Interrupts, and Operating Modes, System Control Module
        // chapter in the device user's guide for background information on the
        // used formula.
        temperatureDegC = (float)(((long)temp - CALADC12_15V_30C) * (85 - 30)) /
                (CALADC12_15V_85C - CALADC12_15V_30C) + 30.0f;
    
        // Temperature in Fahrenheit Tf = (9/5)*Tc + 32
        temperatureDegF = temperatureDegC * 9.0f / 5.0f + 32.0f;
    
        __no_operation();                       // SET BREAKPOINT HERE
      }
    }
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=ADC12_VECTOR
    __interrupt void ADC12ISR (void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(ADC12_VECTOR))) ADC12ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(ADC12IV,34))
      {
      case  0: break;                           // Vector  0:  No interrupt
      case  2: break;                           // Vector  2:  ADC overflow
      case  4: break;                           // Vector  4:  ADC timing overflow
      case  6:                                  // Vector  6:  ADC12IFG0
        temp = ADC12MEM7;                       // Move results, IFG is cleared
        __bic_SR_register_on_exit(LPM4_bits);   // Exit active CPU
        break;
      case  8: break;                           // Vector  8:  ADC12IFG1
      case 10: break;                           // Vector 10:  ADC12IFG2
      case 12: break;                           // Vector 12:  ADC12IFG3
      case 14: break;                           // Vector 14:  ADC12IFG4
      case 16: break;                           // Vector 16:  ADC12IFG5
      case 18: break;                           // Vector 18:  ADC12IFG6
      case 20: break;                           // Vector 20:  ADC12IFG7
      case 22: break;                           // Vector 22:  ADC12IFG8
      case 24: break;                           // Vector 24:  ADC12IFG9
      case 26: break;                           // Vector 26:  ADC12IFG10
      case 28: break;                           // Vector 28:  ADC12IFG11
      case 30: break;                           // Vector 30:  ADC12IFG12
      case 32: break;                           // Vector 32:  ADC12IFG13
      case 34: break;                           // Vector 34:  ADC12IFG14
      default: break;
      }
    }

  • Good news - I finally have a temperature reading in the ADC (see code below).

    Now I have to figure out the correct conversion. There are many different equations (as we saw in the different samples...). The values that I'm reading are around 0x08AE.There's supposed to be a TLV mapping for the temperature in the data sheet - but I can't find it.

    // Initializes the I/O devices (I/O ports' direction and output values, 4-20mA output and A2D conversion)
    void InitDev()
    {
    ...
    ...
      
        /* The A/D Control Register */
      /* Initialize the shared reference module */
      REFCTL0 |= REFMSTR + REFVSEL_0 + REFON;    // Enable internal 1.5V reference
    
       
      ADC12CTL0 = ADC12ON + ADC12MSC + ADC12SHT03 + ADC12REFON;   // Sample 32 ADC12CLK cycles - turn REF on for Temperature.
                                                                   // Multiple sample and conversion.
      ADC12CTL1 = ADC12SHP + ADC12DIV_7 + ADC12SSEL_1 + ADC12CONSEQ_1 ; // Sequence conversion, ADC12CLK: ACLK/8.
    
      /* Set the Conversion Memory Registers (a sequence that starts with A0 and ends with A1. */
      
      ADC12MCTL0 = ADC12INCH_2 + ADC12REF2_5V; // ADC12MEM0 will read A2 (VCC -3v ) in the range VE_REF- (GND_420) to VE_REF+ (VCC).
      ADC12MCTL1 = ADC12INCH_3 + ADC12REF2_5V; // ADC12MEM1 will read A3 (VDD- 5v divided by 2) in the range VE_REF- (GND_420) to VE_REF+ (VCC).
      ADC12MCTL2 = ADC12INCH_1 + ADC12REF2_5V; // ADC12MEM2 will read A1 (sns_Vin -divided by 11) in the range VE_REF- (GND_420) to VE_REF+ (VCC).
      ADC12MCTL3 = ADC12INCH_0 + ADC12REF2_5V; // ADC12MEM3 - will read A0 (sns_HEATER) in the range VE_REF- (GND_420) to VE_REF+ (VCC).
      ADC12MCTL4 = ADC12INCH_4 + ADC12REF2_5V; // ADC12MEM4 - will read A4 (sns_12v - factor is 0.203187) in the range VE_REF- (GND_420) to VE_REF+ (VCC).
      ADC12MCTL5 = ADC12INCH_5 + ADC12REF2_5V; // ADC12MEM5 - will read A5 (sns_HV - between 1 and 2.6v) in the range VE_REF- (GND_420) to VE_REF+ (VCC).
      ADC12MCTL6 = ADC12INCH_6 + ADC12REF2_5V; // ADC12MEM6 - will read A6 (sns_HV - between 1 and 2.6v) in the range VE_REF- (GND_420) to VE_REF+ (VCC).
      ADC12MCTL7 = ADC12INCH_10 + ADC12SREF_1 + ADC12EOS; // ADC12MEM7 - will read A10 -temperature sensor
                                          // End of Sequence for the last channel
      
      ADC12IE = 0x0F;  // A/D Interrupt Enable for all 8 channels
    ...
    ...
    }

  • I used the formula from the example you pointed out - and everything is working well. Thanks for all of your help.

**Attention** This is a public forum