I've got an STM32 with a straightforward voltage divider bringing in a battery level line to an ADC. The problem is that I'm getting a value that doesn't make a ton of sense to me. According to my scope, B_LEV (which is the divided line, going to GPIOC pin 1 / ADC1 channel 11) is 2.49V, with a VREF of 3.3V. The value I'm getting is 2148 (12bit adc), which should translate to 2148 / 4096 * 3.3 = 1.78V, which is obviously not true..

Am I screwing up on the math, or my ADC setting?

Here's the initialization and reading code:

void InitADC() {
  ADC_InitTypeDef ADC_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_239Cycles5);    


  ADC_SoftwareStartConvCmd(ADC1, ENABLE);  

int ReadBatteryValue() {
  // Make sure we have conversion completion
  if(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)
    return 500;
  // Reset the flag
  ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
  // Get the conversion value
  return ADC_GetConversionValue(ADC1);
  • \$\begingroup\$ Why are you shifting the conversion result twice to the right? \$\endgroup\$
    – dext0rb
    Jan 4, 2013 at 5:37
  • \$\begingroup\$ i'll take that out of the post - it's not really salient here. this is sent over the wire, and the consumer expects it to be a 10 bit value, not 12 bit. \$\endgroup\$
    – kolosy
    Jan 4, 2013 at 5:52
  • \$\begingroup\$ do you have a common ground between your scope, the micro and the adc? \$\endgroup\$
    – stanri
    Jan 4, 2013 at 6:03
  • \$\begingroup\$ yes. it's all packed up on a third-party module, but i do. \$\endgroup\$
    – kolosy
    Jan 4, 2013 at 6:08
  • 3
    \$\begingroup\$ Out of curiosity (since your sampling time appears to be large), what are resistor values in your divider? \$\endgroup\$
    – Thorn
    Jan 4, 2013 at 7:24

2 Answers 2


Take a peek at the input impedance of the device vs the values of the resistors in your divider. You might be pullng down your input.

You say that you've got a "straightforward voltage divider" going into your ADC. I'm suggesting that it might be a bit less straightforward than you think.

In an ideal world, you assume that the resistance of an analog input (or any amplifier) is infinite. In fact, the input should be considered to be a finite resistance. On a very good ADC, like the type you would expect to see on a National Instruments card, the input impedance is in the MegaOhm range. On typical microcontrollers, though, its much lower, usually around 10 kiloohms.

Let's assume, your straightforward voltage divider is 5v going through a 10K resistor in series with a 20K resistor, and your voltage at the ADC would be 5V*20Kohms/30Kohms, or 5V*0.66=3.35V. In reality, because the input impedance of your device is 10K, that 20K resistor is really 20K in parallel with the device's 10K, or 6.7K!!! Now what the ADC actually sees is 5V*(6.7k/16.7K), or 5V*0.40, or 2V.

Easiest way to tell what's going on: with the circuit on, use a multimeter to measure the voltage at the ADC, instead of assuming the simple voltage divider is acting the way you think it is.

In fact, an STM32 datasheet on page 124 says the input impedance of the ADC is 50K, suggesting that your voltage divider is in the neighborhood of a 50K in series with a 100K from the numbers you've given, if this is in fact the issue.

Try to keep your voltage divider resistors on the order of 1k to 2k to avoid this problem, or buffer your analog input with an op amp configured as a voltage follower.

Correction: looks like the actual input impedance, at least of the device in the datasheet I randomly chose, is 6K, or maybe even a funky function of sampling frequency (like a switched cap function) -- use the numbers at your own risk, but I think you're having a low input impedance problem.

  • \$\begingroup\$ This comment exhausts my EE knowledge. I know the concept of impedance, but not enough to be able to check what you're suggesting. Can you provide a bit more detail? \$\endgroup\$
    – kolosy
    Jan 4, 2013 at 21:35
  • \$\begingroup\$ Sure. I'll edit my answer \$\endgroup\$ Jan 4, 2013 at 22:06
  • \$\begingroup\$ Sorry for the cryptic original post \$\endgroup\$ Jan 4, 2013 at 22:17
  • \$\begingroup\$ thanks for clarifying! that said, a few things - the 2.49V coming in is measured between ground and the adc pin, not the actual battery voltage. second - the divider is a 5.4k and a 3.4k resistor, which seems to be in the range you're suggesting... \$\endgroup\$
    – kolosy
    Jan 4, 2013 at 22:46
  • \$\begingroup\$ I see now, you do have a scope measurement. Is that measurement taken with the whole circuit on, or with your divider disconnected from the ADC? Since the \$ R_{in} \$ is less than I thought it was, so are the guidelines for the suggested resistors. Hundreds of ohms would be better, or a voltage follower as a buffer is even better. Of course, if your scope measurement was in the full functioning circuit, this can't be the problem. \$\endgroup\$ Jan 4, 2013 at 22:56

So - after digging around some more, I realized that the problem is far simpler - I'm writing code for a third-party module that's based on an STM32 chip, and they had their GPIOs remapped. I was looking at the wrong ADC channel (sigh).


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.