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.

MSP430F5419A Internal Temperature Calibration Issue

Other Parts Discussed in Thread: MSP430F5419A, MSP430F1611, MSP430WARE

I have read many posts on similar issues but couldn't figure out why I am not getting correct internal temperature readings.

ADC2_DEG() holds the temperature in 10's degree C. The temperature values I am getting makes no sense to real temperature readings.

/* special function reg*/

SFR_16BIT(ADC_GAINCAL);

SFR_16BIT(ADC_OFFSETCAL);

SFR_16BIT(ADC_2V5CAL);

SFR_16BIT(ADC_2V5T30CAL);

SFR_16BIT(ADC_2V5T85CAL);

#define ADC2_10DEG() ((uint16) (((ADC12MEM6-ADC_2V5T30CAL)*(850L-300L)/(ADC_2V5T85CAL-ADC_2V5T30CAL)+300L)))

/* init_adc routine */

void init_adc(void)

{

REFCTL0 &= ~REFMSTR;
ADC12CTL0 = ADC12SHT0_1 | ADC12SHT1_7 | ADC12ON | ADC12MSC | ADC12REF2_5V | ADC12REFON;  
ADC12CTL1 = ADC12CSTARTADD_4 | ADC12SHP | ADC12SHS_3 | ADC12CONSEQ_1;
ADC12CTL2 = ADC12RES_2 | ADC12SR;

/* Conversion Memory Control Register
* Channels 4 - VBAT, 5 - VLOW, 6 - internal Temperature sensor
* Input channel select
*/
      ADC12MCTL4 = ADC12SREF_1 | ADC12INCH_14;                 /* A14 = VSYS */
      ADC12MCTL5 = ADC12SREF_1 | ADC12INCH_15;                 /* A15 = VLOW */
      ADC12MCTL6 = ADC12SREF_1 | ADC12INCH_10;                 /* A10 = THERM */

      ADC12MCTL6 |= ADC12EOS; /* End of sequence */

}

/* timer ISR */

__interrupt void TIMER0_B1_ISR(void)
{
     if (TBIV & TB0IV_TB0IFG) 
     {
          ADC12CTL0 &= ~ADC12ENC, ADC12CTL0 |= ADC12ENC; /* trigger ADC */

     }
}

I got the following in my linker file:

ADC_GAINCAL = 0x1A16;
ADC_OFFSETCAL = 0x1A18;
ADC_2V5CAL = 0x1A20;
ADC_2V5T30CAL = 0x1A22;
ADC_2V5T85CAL = 0x1A24;

Any help is greatly appreciated!

  • sandesh mukartihal said:
    ADC2_DEG() holds the temperature in 10's degree C. The temperature values I am getting makes no sense to real temperature readings.

    You can get better responses if you give all the information we need to do the math. So please show all data needed to calculate result: calibration values, ADC reading and room/chip temperature you measure

  • ADC Reading: ADC12MEM6 = 1864

    Calibration values: ADC_2V5T30CAL = 1320, ADC_2V5T85CAL = 1532

    temperature(C) = { (1864-1320) * [(85-30)/(1532-1320)] } + 30 = 171.13

  • If those calibration parameters are correct, and your reference is setup properly, then the math is correct.

    But I would question the one of those first two assumptions. Are you sure you have the calibration values for 2.5V reference? Are you sure that the 2.5V reference is properly selected and turned on?

  • You don't post the part of the code that shows when you sample the temperature, but be advised that when using the reference module, it has a settling time that you have to wait before it is stable and can be used for making measurements. Check the User's Guide and datasheets for your device.

  • sandesh mukartihal said:
    ADC12CTL0 &= ~ADC12ENC, ADC12CTL0 |= ADC12ENC; /* trigger ADC */

    This line won't work. First, ',' means 'synchronous operation'. Which may or may not execute in the order written. But you want execution in the proper sequence. So use ';'.

    Also, you use ADC12SHS_3, so CCR1 will trigger the ADC. But right in the ISR whcih is triggered at the same tiem the ADC starts the sequence, you disable and re-enable ENC, which interrupts the sequence and leaves unpredictable results.

    So let the timer trigger the ADC sequence start , or do it manually, but not both.

    sandesh mukartihal said:
       if (TBIV & TB0IV_TB0IFG) 

    Bad idea. Reading TBIV for the comparison is destructive. If it isn't TB0IFG, then you have no way to figure out what else it was.

    If TB0IFG is the only interrupt that can happen, you don't need the IF at all (as it is the only situation in which the ISR could have been called). To clear the interrupt, you can do "TB0CTL &= ~TBIFG;" Or (this is a destructive read too) "(TB0IV);"

    And if you have more than one interrupt, this IF will prevent you from properly detecting which one it was if it wans't the first one you checked.

  • I am able to use the following code and make it work from the temerature range -40 to +85degC in MSP430F5419A micro.  I used on time sensor value read.  The only problem i faced is i have explictly do ADC12CTL0 &= ~(ADC12SC); to stop the conversion.  Otherwise, the conversion never stopping and it is always in continuous BUSY. Also the ADC value in ADC12MEM4 is not available immediatley even after IFG flag is set.  I have to give a delay to read the ADC12MEM4 value after bit in IFG1 is set.

    #define ADC_2V5T30CAL 0x1A22
    #define ADC_2V5T85CAL 0x1A24

    void ReadOnChipTemperatureSensorValue()
    {
      // Allow ADC_12 to control everything
      REFCTL0 = 0;
      ADC12MEM4 = 0;
      ADC12CTL0 = ADC12SHT0_1 | ADC12SHT1_1 | ADC12ON | ADC12REF2_5V | ADC12REFON; 
      ADC12CTL1 = ADC12CSTARTADD_4 | ADC12SHS_0 | ADC12CONSEQ_0 | ADC12DIV_0;
     
      ADC12CTL2 = ADC12RES_2 | ADC12SR;
      ADC12MCTL4 = ADC12SREF_1 | ADC12INCH_10;                 /* A10 = THERM */
     
      ADC12MCTL4 |= ADC12EOS; /* End of sequence */
      ADC12CTL0 |= (ADC12ENC | ADC12SC); /* trigger ADC */

      // wait for the conversion to happen
      for (volatile int j=0; j<500; j++);

      ADC12CTL0 &= ~(ADC12SC); /* Stop conversion */
     
      // wait for the data to be updated
      while (ADC12MEM4 == 0);
     
      uint16 adc85CalVal = *((uint16 *)ADC_2V5T85CAL);
      uint16 adc30CalVal = *((uint16 *)ADC_2V5T30CAL);
     
      int32 g_unTemperature = (int32)(8500 - 3000)/((int32)adc85CalVal-(int32)adc30CalVal);
      g_unTemperature = g_unTemperature * ((int32)ADC12MEM4-(int32)adc30CalVal);
      g_unTemperature += (int32)3000;
      g_fTemperature = ((fp32)g_unTemperature)/100.0; 
    }

  • prasad samudrala said:
    i have explictly do ADC12CTL0 &= ~(ADC12SC); to stop the conversion.


    Not to stop, but to start! You don't set the ADC12SHP bit. So ADC12SC directly controls the sample&hold gate. When you set SC, charging the sampling capacitor starts, and when you clear it, sampling ends and the conversion is started. This mode is mainly used in conjunction with a timer signal,controlling the ADC timing.
    If you set ADC12SHP, then ADC12SC is only used as trigger, and the smapling timing is done by the internal sample timer, controlled by the ADC12SHTx bits.

  • Thanks Michael, it was my misunderstanding of ADC12SC bit.

  • @Prasad:

    I tried this code, i get values in tens of degrees. For example, g_fTemperature=182 which makes more sense as 18.2 C.

    Any ideas why?

  • The result shoud be in 1/100 C, but 1.82C seems wrong. It could be that
    1) the reference voltage hasn't settled )the code doesn't wait fo rthe reference, so depending on CPU speed, the first conversion is probably wrong)
    2) the calibration values stored in the chip are wrong. It has happened in the past that one of the values was wrong for a whole batch of devices. A rare case, but possible. On my own devices (based on MSP430F1611 without these calibration values), I usually do my own 2-point calibration.
    It'S a good idea to make a probability check: take the two values and ensure that the 85C value is significantly larger than the 30C value. If not, complain.

  • Thanks Michael for your time.

    According to User Guide, section "1.13.4.3 temperature Sensor Calibration", the Temp (in C) is calculated as below:

    Temp = ( ADC(raw) - CAL_ADC_15T30) x  ( (85-30)/(CAL_ADC_15T85-CAL_ADC_15T30) ) + 30

    So, How would the result be in 1/100C?

    I read the temperature value using "ADC2_10DEG()" at regular intervals of 1 second.

    #define ADC2_10DEG() ((uint16) (((ADC12MEM6-ADC_2V5T30CAL)*(850L-300L)/(ADC_2V5T85CAL-ADC_2V5T30CAL)+300L)))

    The values I get are as below:

    ADC Reading: ADC12MEM6 = 1864

    Calibration values: ADC_2V5T30CAL = 1320, ADC_2V5T85CAL = 1532

    temperature(C) = { (1864-1320) * [(85-30)/(1532-1320)] } + 30 = 171.13.

    Do you think the above calibration values are wrong?

    I use the same ADC settings to read the system voltage through another channel (A14) and it works perfectly for me:

    /* Internal reference */
    #define REF_VOLTAGE				2500L	
    
    /* 12 bit */
    #define ADC_RES					4096L	
    
    /* VSYS voltage divider circuit */
    #define VSYS_R1					68L
    #define VSYS_R2					10L
    
    #define VSYS2VOLTS() ((uint16) (ADC12MEM4 * REF_VOLTAGE *(VSYS_R1+VSYS_R2)/(VSYS_R2*ADC_RES)))
    

  • use 7.12

    http://www.mikrokontroler.pl/content/7-mikrokontrolery-msp430-obsluga-adc/strona/0/4

    making mul and div, convert values into float,

  • The first formula you cited assumes 85C-30C = 55 steps difference between the calibration values.
    The #define you posted uses 850-300 =550 steps (which means, it gives a result in 1/10C)

    And the formula in the Prasad’s post used 8500-3000 = 5500 steps, giving centigrades.

    When doing integer arithmetic, a reading difference of e.g. 550 gives 55/550 = 0 (float would give 0.1) with the simple formula, while doing the calculation with centigrades gives 5500/550 gives 10. (10 centigrades per ADC reading step). Here, float isn’t really required. For even higher precision/resolution, milligrades should be used.

  • Thanks Michael, I was waiting to receive next batch of boards to do the testing.

    I am still getting the temperature reading in tens of degrees. I have changed the #define now to use 85 and 30 instead of 850 and 300.

    I believe it is working properly, but only the reading I get are in tens of degrees.

    Do you think the values I posted(Calibration values at 85C and 30C, and the ADCMEM readings) in my previous post are correct?

  • It seems so. The difference between 85°C and 30°C value is only 212 counts (seems low to me, only 4 counts per degree), but you are 544 counts over 30°C value. So 171°C seems to be the proper result for these values.

    Are you sure that 0x1a22/0x1a44 are the correct locations of the calibration values?

    Did you try with 1.5V reference? It gives you a higher resolution and also a second set of reference values.

    What about the required minimum sampling time? I think, ADC12SHT0_1 isn’t long enough (see users guide/datasheet). ADC12SHT0_x is for ADC12MEM0..7 and ADC12SHT1_x for ADC12MEM8..15. Not for the input channels 0..7 and 8..15.
    This may significantly affect the reading on input channel 10.

  • Thanks Michael,

    The calibration values are correct as per the datasheet.

    I tried with 1.5V calibration values. Looks like the reference temperature values are incorrect for 1.5V reference. They are as below:

    ADC_1_5V_30C(0x1A1C) = 2510

    ADC_1_5V_85C(0x1A1E) = 1601.

    I believe the values for 85C is incorrect.

    I changed the sampling time to be greater than 512 cycles with 2.5V reference. This fixed the issue. I am now getting proper readings.

    Thank you all for your time and help to resolve this issue.

  • Looking at the TLV structure description, I’d expect the 1.5V values @ 0x1a1a and 0x1a1c. 0x1a1e is the 30° value for 2.0V.

    You (or whoever defined the addresses) was apparently using the TLV structure for the non-A version of the 5419. Where indeed 0x1a1c and 0x1a1e are the ones. (the values on the non-A were interleaved with the ADC reference calibration value, while on the A version, they are after the three temperature data sets, so the values for 2.5V are coincidentally at the same place).

  • Jens-Michael Gross said:
    You (or whoever defined the addresses) was apparently using the TLV structure for the non-A version of the 5419.

    This is where the correct way is to use a "TLV walking" function to get the data rather than a hard-coded address. (MSP430Ware has examples).

    Do this once during startup and store the calibration values in a local static structure.

    Downside is it takes a little more code and time. Upside is you do it once and it is portable across devices in the same family.

  • @Brian: Can you please provide the exact location of the example that uses the TLV walking?

    I am referring to example: msp430x54xA_adc12_10.c in slac375e and this doesn't use TLV walking.

  • examples\driverlib\MSP430F5xx_6xx\tlv

  • Sorry, Brian, I have to disagree here. Besides the fact that header files should be correct (and the correct one used), the TLV walking only gives your ‘these n bytes are calibration values’, but not which is which. The content of the TLV field depends on the MSP and you are prone to the very same mistake when doing it yourself: looking into the wrong datasheet and taking the wrong value. So with additional effort, you’re in the same situation as before.
    Well, when you scan the TLV on your own, it’s clear whom to blame if it doesn’t work :)

    Sandesh, he was not referring to an example that uses TLV walking for your purpose, but to an example of doing TLV walking in general (which you must adapt for your purpose)

**Attention** This is a public forum