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.

Measuring VCC using ADC channel 11

Other Parts Discussed in Thread: MSP430F5438

Hi Guys

I got an code in which the vcc is calculated and it is compared with reference voltage 1.15v(0x0311 in hex). I just want to know how the hex value 0x0311 is equal to 1.15 v?

It will be a great help if somebody gives me an idea as to how I can calculate corresponding hex values for a particular voltage value?

Regards

  • Which MSP are you using, and which ADC does it have? Theres ADC10, ADC12, SD16, SD24... On all of them, values have different meaning.

    On the ADC10 or ADC12, the conversion result x equals a voltage based on the formula "V = x*(Vref/((2^n)-1)" where 'n' is the bit depth of the ADC (10 for the ADC10 and 8,10 or default 12 for the ADC12)

    'channel 11' seems to indicate an ADC10 or ADC12. The readng from this channel results in VCC/2 being measured. The value 0x311 for 1.15V seems to indicate that the 1.5V internal reference was used and it is an ADC10. So VCC = 2* 0x311 * (1.5/1023) = 2.302V.

    Or in the other direction, every count of the conversion result equals 1.5V/1023 = 1.466275mV.
    When using the internal 2.5V reference, this changes to 2.5V/1023 = 2.4438mV
    When VCC is used as reference (which makes no sense when measuring channel 11, as this would always give ~0x1ff, as VCC/2 == 1/2 VCC) the meaning of the conversion result changes accordingly.

  • Hi Jens

    Thank you for the reply, it helped me a lot, still have some doubts.

    Jens-Michael Gross said:

    Which MSP are you using, and which ADC does it have? Theres ADC10, ADC12, SD16, SD24... On all of them, values have different meaning.

    I am using MSP430F5438 , I think I am suppossed to use ADC12. right?

    Jens-Michael Gross said:
    'channel 11' seems to indicate an ADC10 or ADC12. The readng from this channel results in VCC/2 being measured. The value 0x311 for 1.15V seems to indicate that the 1.5V internal reference was used and it is an ADC10. So VCC = 2* 0x311 * (1.5/1023) = 2.302V

    You have divided by 1023, may I know how it is 1023?

    Thank you and Best regards

  • Harshith Ramesh said:
    I am using MSP430F5438 , I think I am suppossed to use ADC12. right?

    Yes. That surprises me, as it doesn't fit the values you provided, unless the ADC12 is operated in 10 bit mode.

    Harshith Ramesh said:
    You have divided by 1023, may I know how it is 1023?

    This was for a 10 bit conversion. Since 0 equals 0 and (2^n-1)== 0x3FF equals the used reference, each step is Vref/((2^n-1)). For 10 bit conversion with 1.5V reference it is 1.5V/1023. For 12 bit it is Vref/4095. If you don't mind the error, 2^n may be a good enough approximation (and a shift by 10/12 bit is faster and smaller than a division).

    However, getting 1.15V from the value 0x311 on 12 bit conversion would require a reference of 3V. Which of course is possible if VCC is used as reference and VCC is 3V. However, VCC/2, as measured with channel 11, then cannot be 1.15V and would have to be 1.5V instead, so I assumed 1.5V reference and 10 bit conversion, where the value fits. (reading channel 11 with VCC as reference always returns ~2^(n-1), which is 0x1ff for a 10 bit and 0x7ff for a 12 bit conversion).
    The only otehr possibility owuld have been an externally applied reference of 3V (Veref) while VCC is 2.3V. But this wouldn't be an allowed combinations since external the reference must not be higher than VCC. And actually cannot be higher than (VCC+0.2V)

    So the only scenario where 0x311 == 1.15V is VCC=2.3V, 1.5V reference and 10 bit conversion.

  • The following is my code, just a trial code to measure voltage, please do tell me whats wrong in it? 

    #include "msp430f5438.h"
    #include "hal_board_cfg.h"
    #include "hal_lcd.h"
    #include <stdint.h>
    #include <string.h>
    #include <stdio.h>
    int _system_pre_init(void)
    {
    /* Insert your low-level initializations here */
    WDTCTL = WDTPW + WDTHOLD; // Stop Watchdog timer
    /*==================================*/
    /* Choose if segment initialization */
    /* should be done or not. */
    /* Return: 0 to omit initialization */
    /* 1 to run initialization */
    /*==================================*/
    return (1);
    }

    void main(void)
    {
    //float vcc,vref = 1.5;
    uint16_t Voltage;
    char temp[20];
    P1DIR |= BIT0 | BIT1;
    //ADC12CTL0 = ADC12ON; // turn on ADC
    halLcdInit();
    halLcdBackLightInit();
    halLcdSetBackLight(8);
    ADC12IFG = 0x00;
    ADC12IE = ADC12IE11;
    // ADC12CTL1 = ADC12CONSEQ_2;
    // ADC12CTL0 = ADC12ON;
    // ADC12CTL2 |= ADC12RES_2;
    // ADC12CTL1 |= ADC12CSTARTADD_11;

    for(;;)
    {
    ADC12CTL1 = ADC12CONSEQ_2;
    ADC12CTL0 = ADC12ON;
    ADC12CTL2 |= ADC12RES_2;
    ADC12CTL1 |= ADC12CSTARTADD_11;
    ADC12MCTL11 |= ADC12INCH_11 + ADC12SREF_2;// + ADC12EOS;
    ADC12CTL1 |= ADC12SHP;
    ADC12CTL0 |= ADC12SHT1_4 + ADC12ENC + ADC12SC + ADC12MSC;
    _delay_cycles(5);
    while(!(ADC12CTL1 & ADC12BUSY));
    while( !(ADC12IFG & ADC12IFG11));
    Voltage = ADC12MEM11;
    if(Voltage == 0)
    {
    P1OUT = BIT0;
    }
    if(Voltage > 0xB77)
    {
    //vcc = 2*Voltage*(vref/4095);
    HalLcd_HW_Clear();
    sprintf(temp,"voltage : %d",Voltage);
    HalLcd_HW_WriteLine(temp, 2);
    P1OUT = BIT1;
    }
    else if(Voltage < 0xB77)
    {
    //vcc = 2*Voltage*(vref/4095);
    sprintf(temp,"voltage : %d",Voltage);
    HalLcd_HW_WriteLine(temp, 2);
    HalLcd_HW_WriteLine("Voltage low", 4);
    P1OUT ^= BIT1;
    }
    ADC12IFG = 0x0000;
    ADC12MEM11 = 0x0000;
    _delay_cycles(65000);
    }
    }
  • The following is my code, just a trial code to measure voltage, please do tell me whats wrong in it? 

    #include "msp430f5438.h"
    #include "hal_board_cfg.h"
    #include "hal_lcd.h"
    #include <stdint.h>
    #include <string.h>
    #include <stdio.h>
    int _system_pre_init(void)
    {
    /* Insert your low-level initializations here */
    WDTCTL = WDTPW + WDTHOLD; // Stop Watchdog timer
    /*==================================*/
    /* Choose if segment initialization */
    /* should be done or not. */
    /* Return: 0 to omit initialization */
    /* 1 to run initialization */
    /*==================================*/
    return (1);
    }

    void main(void)
    {
    //float vcc,vref = 1.5;
    uint16_t Voltage;
    char temp[20];
    P1DIR |= BIT0 | BIT1;
    //ADC12CTL0 = ADC12ON; // turn on ADC
    halLcdInit();
    halLcdBackLightInit();
    halLcdSetBackLight(8);
    ADC12IFG = 0x00;
    ADC12IE = ADC12IE11;
    // ADC12CTL1 = ADC12CONSEQ_2;
    // ADC12CTL0 = ADC12ON;
    // ADC12CTL2 |= ADC12RES_2;
    // ADC12CTL1 |= ADC12CSTARTADD_11;

    for(;;)
    {
    ADC12CTL1 = ADC12CONSEQ_2;
    ADC12CTL0 = ADC12ON;
    ADC12CTL2 |= ADC12RES_2;
    ADC12CTL1 |= ADC12CSTARTADD_11;
    ADC12MCTL11 |= ADC12INCH_11 + ADC12SREF_2;// + ADC12EOS;
    ADC12CTL1 |= ADC12SHP;
    ADC12CTL0 |= ADC12SHT1_4 + ADC12ENC + ADC12SC + ADC12MSC;
    _delay_cycles(5);
    while(!(ADC12CTL1 & ADC12BUSY));
    while( !(ADC12IFG & ADC12IFG11));
    Voltage = ADC12MEM11;
    if(Voltage == 0)
    {
    P1OUT = BIT0;
    }
    if(Voltage > 0xB77)
    {
    //vcc = 2*Voltage*(vref/4095);
    HalLcd_HW_Clear();
    sprintf(temp,"voltage : %d",Voltage);
    HalLcd_HW_WriteLine(temp, 2);
    P1OUT = BIT1;
    }
    else if(Voltage < 0xB77)
    {
    //vcc = 2*Voltage*(vref/4095);
    sprintf(temp,"voltage : %d",Voltage);
    HalLcd_HW_WriteLine(temp, 2);
    HalLcd_HW_WriteLine("Voltage low", 4);
    P1OUT ^= BIT1;
    }
    ADC12IFG = 0x0000;
    ADC12MEM11 = 0x0000;
    _delay_cycles(65000);
    }
    }
  • Harshith Ramesh said:
    ADC12CTL1 |= ADC12CSTARTADD_11;

    You don' tneed to use ADC12MCTL11 for INCH_11. The conversion memory and the used channel are independent of each other (else there would be no need to set the channel at all). You can sample any channel on any MCTLx slot.

    Harshith Ramesh said:
    ADC12MCTL11 |= ADC12INCH_11 + ADC12SREF_2;// + ADC12EOS

    This selects external reference. ADC12SREF_1 selects the internal reference.
    However, I don't see you enabling the internal reference. (on 5438 non-A, it is done by setting the REFON bit. And waiting some time for it to settle before starting the first conversion)

    Harshith Ramesh said:
    //vcc = 2*Voltage*(vref/4095);

    Whiel this formula is correct, it is slow and inefficient.
    If you combine the 1.5V reference and the *2 factor, you get an integer 3V reference. And the /4095 can be approximated by a >>12 shift (which is a /4096 division, close enough in most cases).

    so "volt = (Voltage * 3) >> 12" gives you the integer part of the voltage, while "mvolt = ((Voltage *3)>>2)& 0x3ff" gives you the millivolt part. Well, almost, as mvolt can be >999, as each count is 1/1024V, not 1/1000V. So it can be still a bit refined. But it is an integer and no float operation and much, much faster and smaller than doing any float calculations.

    Or you go directly for millivolts:
    volt = (Voltage*375L)>>9;
    (>>10 for all other channels but the VCC/2 channel).
    if you want a resolution of 100µV, use 3750L. The 'L' is important, as the multiplication needs to be performed as 32 bit.

    You can then print it in sprintf as
    sprintf(temp, voltage : %d.%04d", voltage/1000, voltage%1000);
    or, more efficiently:
    mvolt =  (Voltage*375L)>>9;
    volt = mvolt/1000;
    mvolt = mvolt - (volt*1000);
    sprintf(temp, voltage : %d.%04d", volt, mvolt);
    as it replaces one division by a multiplication and subtraction. Divisions are slooow.

  • Hi Jens

    Jens-Michael Gross said:
    ADC12SREF_1 selects the internal reference.
    However, I don't see you enabling the internal reference. (on 5438 non-A, it is done by setting the REFON bit. And waiting some time for it to settle before starting the first conversion)

    Now I am switching on the REFON and waiting for some time,

    ADC12CTL0 = ADC12ON + ADC12REFON ;
    _delay_cycles(1000);
    
    
    am also using internal reference ,
    ADC12MCTL11 |= ADC12INCH_11 + ADC12SREF_1;// VR+ = VREF+ and VR- = AVSS //+ ADC12EOS;

    but the problem with channel 11 is it gives a constant value 4095? What is the problem? and if I change to ADC12SREF_0 it gives 2053 and it remains constant. Why is this happenning? Is ADC12EOS required?

    Then I used channel zero and gave external voltage and the ADC memory showed value, I used the same formula to calculate the voltage (Vcc= 2*value*Vref/4095) and I got the value applied at channel Zero. Is the way I am working is proper? Is it the same formula even for channel 0? 

    When I use two new batteries I get the value 3959 (i.e Vcc = 2*3959*1.5/4095 = 2.9 V), in channel 0 , A0/P6.0 . 

    And then to calculate the battery voltage I made use of one more internal channel, Channel 8. I used the same formula, and the voltage I get looks valid? But the reference am using is ADC12SREF_0 (i.e VR+ = AVCC and VR- = AVSS) .Please tell where am I going wrong?

    while(!(ADC12CTL1 & ADC12BUSY)); 
    while( !(ADC12IFG & ADC12IFG11));
    
    
    Is the above two lines , is the right way to wait till the conversion is done?
    
    
    Jens you are the only saviour...:-)  :-) Thanks in advance.....
  • Harshith Ramesh said:
    but the problem with channel 11 is it gives a constant value 4095? What is the problem? and if I change to ADC12SREF_0 it gives 2053 and it remains constant. Why is this happenning? Is ADC12EOS required?

    Well, masuring VCC/2 against VCC as reference always gives 1/2 as a result. 2053 is ~1/2 of 4095. So that's what I would expect.

    Abotu the MAX reading (4095 = max = overflow indicator) with interrnal reference: is it possible that your VCC is >3V? VCC/2 then is >1.5V which is the internal reference. You'll need to switch the internal reference to 2.5V by setting the REF2_5V bit. Then you can measure a VCC of up to ~5V :)

    Taking into account that your VCC/2 against VCC did result in 2053 and not 2047 (so channel 11 gives 0.3% more than expected), you'll get an overflow (max reading) with 1.5V reference on VCC >= 2.99V, if the internal reference is exactly 1.5V. If it is lower (which it usually is, see datasheet), you'll get an overflow for even lower VCC.

**Attention** This is a public forum