How to use Reference Voltage in ADC

This is the Tenth tutorial in the STM32 ADC series. In this series will see how to use the ADC peripheral of the STM32 to read the data from the Analog devices. We will cover how to use the ADC in different modes, that includes polling mode, interrupt mode and the DMA mode. We will also see how to use the multiple channels to read multiple analog devices using the same ADC. Later in the series we will cover more advanced features like differential ADC, combined ADCs, ADC watchdogs, injected channels etc.

This tutorial will cover how to use the external reference voltage to utilise full scale resolution even in low input voltages.

The availability of Vref pin is needed to provide the external reference voltage. While some MCUs have a separate Vref pin, others has it connected with the VDDA. We will see both types in this tutorial.

Let’s first see the types of voltages related to analog circuitry in STM32.

VDD and VSS are the main supply voltages. Most of the STM32 MCUs can run between 1.8V ~ 3.3V VDD. When powered normally, they run at 3.3V.

VDDA and VSSA is the separate analog supply and it is used to power the analog circuits in the MCU. Some of the STM32 MCUs has separate pins for VDDA/VSSA, but almost in all the development boards it is connected to the VDD. Although there is an option to make some hardware modification to separate these pins from the VDD.

Vref+ and Vref- are used to provide external reference voltage. These pins are also not available in all the MCUs, but wherever they are available, they are tied to VDDA by default. We again have an option to separate them from VDDA by making some hardware changes.

VREFINT is the fixed internal reference voltage. This voltage does not vary with the VDD and remains fixed even if the VDD drops to 1.8V. Hence it can be used to measure the voltage supply on the Vref+ pin or even the VDD, if the Vref+ is connected to VDD.

The parameters like availability of physical Vref+ pin, its connection with VDDA or VDD changes according to the design of the development board. For the same MCU, one dev board can have the Vref+ tied to the VDDA to VDD, while the other could have a separate Vref+ and a separate VDDA. Therefore while focussing on the MCUs, we will also see the schematic of the dev board.

Measure VDD on Nucleo F446RE

Let’s start with the nucleo F446RE. Below is the image from the datasheet of F446RE showing the pin availabilities in different MCUs.

As you can see above, the Vref+, VDDA and VSSA are all separate pins on a 144 pin package. But we only have Vref+, VDDA as separate pins on a 100 pin package. The nucleo F446RE is a LQFP64 and hence the Vref+ is tied with VDDA itself.

Below is the image from the schematic of the nucleo F446RE.

Although the VDAA/Vref+ is a separate pin on the nucleo F446, it is connected to the VDD as per the schematic in the image above. We can still separate this pin from the VDD by removing the physical connection SB57. Once removed, the VDAA becomes independent and we can provide external reference voltage on this pin for the ADC.

I am not doing it for the F446. Instead I am going to vary the VDD itself. Since the VDDA/Vref+ is connected to VDD, if the VDD varies, the VDDA and hence the Vref+ varies too. The Internal Reference Voltage is used to measure the Vref+, but since the Vref+ is connected to VDD, we ca use the VREFINT to measure the VDD itself.

Measuring VDD also becomes important is certain situations. Imagine a scenario where the MCU is powered at 3.3V using a battery. The ADC is going to take this 3.3V as the reference voltage and we are also calculating the input voltage using 3.3V in the formula. When the battery voltage starts dropping, the VDD will also drop. The battery voltage has reached 2V now.

If we are not providing a separate voltage for ADC (through VDDA/Vref+), the ADC reference voltage has also dropped to 2V now. If the ADC input voltage is 2V, the ADC will generate the maximum value for this voltage. And since we are using 3.3V in the formula, the ADC will show this 2V input as 3.3V.

Hence if you are using an unmodified dev board and implementing a battery in your design, you must measure the VDD after every certain amount of time. Then update the reference voltage in the ADC formula, so that new reference voltage can be used to calculate the input voltage.

CubeMX Configuration

I have enabled the Vrefint channel in ADC1. There is another channel (channel 0) configured as well to measure the input voltage. I have enabled DMA, so multiple conversions can be done.

While configuring the Vrefint channel, the care must be taken to choose the sampling time. As per the datasheet of the MCU, the sampling time for this channel should be at least 10us.

As shown in the image above, the Vrefint is around 1.21V. There is also calibration data for the internal reference voltage stored in the memory. The calibration was done at VDDA = 3.3V.


The Code

uint16_t ADC_VAL[2];
int voltage;
int vref = 0;
#define Vref_CAL *VREFINT_CAL_ADDR

The array ADC_VAL[2] will be used to store the Raw ADC values. The variable vref will store the reference voltage or VDD (in this case) whereas the variable voltage will store the ADC input voltage on channel 0.

The Vref_CAL will fetch the internal reference calibration data from the memory.

int main ()
{
  ....
  HAL_ADC_Start_DMA(&hadc1, ADC_VAL, 2);
  while (1){}
}

Inside the main function we will simply start the ADC in DMA mode. The Raw data will be stored in the array ADC_VAL and here we are converting 2 channels.

Once both the conversions are finished, an interrupt will trigger and the HAL_ADC_ConvCpltCallback will be called.

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
	vref = 3300*Vref_CAL/ADC_VAL[0];
	voltage = ADC_VAL[1]*vref/4095;
}

Inside this callback we will first calculate the Vref/VDD using the calibrated value. The calibration was done at 3.3V, hence the value 3300 is used here.

Then using this Vref we will calculate the input voltage to the ADC.


Result

In order to provide the variable supply voltage to the nucleo F446, we need to first remove its main supply from the ST link.

The Highlighted Jumper on the nucleo board allows it to receive the power from the ST link. SO we need to remove this jumper and then provide supply on the 5V pin of the board.

Below are the images showing the result of the project.

Here I am providing the supply voltage of 2V to the nucleo board. You can see the Vref is also showing the same value around 200mV.

The first image shows when the ADC input voltage is 0.5V, the live expression shows the same voltage 499mV. Also note that the ADC Raw value is 1047 for this 0.5V input.

The second image shows when the ADC input voltage is 2V, the live expression shows the same voltage 1997mV. The ADC Raw value is 4095 for this 2V input.

Note that the ADC value changes from 0 to 4095 for just 2V variation in the input voltage. Now imagine if we do not calculate the Vref and set the ADC calculation to take 3.3V as the reference. This 2V input will be shown as 3.3V.

We can also use this method to monitor the VDD and set a kind of alarm system to indicate the low voltage of the battery.



Measuring actual Vref on H750

We saw that on nucleo F446 the Vref pin is connected to the VDDA, which is then connected to the VDD. Hence we were able to measure the VDD using the Vrefint channel. Now we will take a look at the H750 Based Custom Development Board. It has separate pins for Vref and VDDA, therefore we will use the Vrefint to calculate the actual variable Vref this time, while keeping the VDD constant.

Below is the image showing the schematic of this board.

The Red Line shows the connection of the Vref+ to 3.3V. Whereas the Green line shows the connection of VDDA to 3.3V. There is a 0 Ohm Resistor (R11), which can be removed to make the Vref+ an independent pin. I did the same on this board and now the Vref+ can be used to provide an external reference voltage for the ADC.

CubeMX Configuration

Below is the configuration for Vrefint channel.

The Vrefint channel is available in ADC3. I have disabled the continuous conversion mode and we will read this channel in the blocking mode.

While configuring the Vrefint channel, the care must be taken to choose the sampling time. As per the datasheet of the MCU, the sampling time for this channel should be at least 4.3us.

As shown in the image above, the Vrefint is around 1.216V. There is also calibration data for the internal reference voltage stored in the memory. The calibration was done at VDDA = 3.3V.

Below is the image showing the configuration for ADC input channel.

I have enabled ADC1 channel 3 in single ended mode. We will provide the variable input voltage on this pin. The ADC is configured in Circular DMA mode, so the data reading will continue on its own.


The Code

uint16_t ADC_VAL[2];
int voltage;
int vref = 0;
#define Vref_CAL *VREFINT_CAL_ADDR

The array ADC_VAL[2] will be used to store the Raw ADC values. The variable vref will store the reference voltage or VDD (in this case) whereas the variable voltage will store the ADC input voltage on channel 0.

The Vref_CAL will fetch the internal reference calibration data from the memory.

volatile int isDone =0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
	isDone = 1;
}

When the ADC finished the conversion and the data is transferred via the DMA, the ConvCpltCallback will be called. Inside this callback we will simply set the variable isDone. The data will be processed in the while loop.

int main(void)
{
  ....
  ....
  HAL_ADC_Start_DMA(&hadc1, &ADC_VAL[0], 1);
  while (1)
  {
	  HAL_ADC_Start(&hadc3);
	  HAL_ADC_PollForConversion(&hadc3, 10);
	  ADC_VAL[1] = HAL_ADC_GetValue(&hadc3);
	  HAL_ADC_Stop(&hadc3);
	  vref = 3300*vref_cal/ADC_VAL[1];
	  if (isDone)
	  {
		  voltage = ADC_VAL[0]*vref/65535;
		  isDone = 0;
	  }
	  HAL_Delay(250);
  }
}

Inside the main function, we will start the ADC1 in the DMA mode. The ADC Raw value will be stored in the 0th position of the ADC_VAL array.

Inside the while loop, we will read the ADC channel 3 in the blocking mode. The ADC Raw data for this Vrefint channel will be stored in the 1st position of the ADC_VAL array.
Then calculate the Vref using the calibrated value. The calibration was done at 3.3V, hence the value 3300 is used here.

Then we will check if the ADC1 has finished converting channel 3. If it did, we will calculate the input voltage using the vref value we just calculated.

Basically we are first calculating the Vref using internal reference voltage, and then using this Vref as the reference voltage for the ADC input. Therefore if the Vref varies on Vref+ pin, our ADC input will be measured as according to the updated voltage.


Result

Below are the images showing the result of the project.

As shown in the image above, the external supply on the Vref+ pin is around 2V and the same is being measured as it can be seen in the live expression. When the input voltage on the ADC pin is 0.6V, the voltage variable also shows 625mV.

Now the input voltage is 2V and the voltage variable in the live expression shows the same value. Also note that the ADC raw value has reached around maximum 65535.

Using a custom reference voltage becomes important when the sensor does not does not outputs a typical 3.3V. For example, if there is a sensor which only outputs the voltage between 0 to 2V, we can use a custom Vref of 2V to get the maximum resolution for this type of sensor.

We can also get the voltage using a regular 3.3V reference voltage, but then the resolution will be limited to less than 2/3rd of the maximum.



Internal Reference Voltage VREFBUF

Some of the STM32 MCUs has a peripheral to generate the custom Reference voltage on the Vref+ pin. This can be achieved by using the peripheral Analog Voltage Reference Buffer, VREFBUF.

The VREFBUF is supplied via the VDDA pin. When enabled, it can provide a reference voltage in the range of: 1.65V, 1,8V, 2.048V or 2.5V. The VREFBUF can be used to provide an analog voltage reference for:

  • ADC internal peripheral
  • External components through the dedicated VREF+ pin.

The VREFBUF can be left unused. In this case, an external voltage regulator can provide reference voltage to VREF+ pin.

Even though the VREF+ pin can be used to provide the voltage to external component, note that the current supply in very limited on this pin, approximately around 4mA.

Below is the image showing the configuration for the VREFBUF.

I have configured the VREFBUF Mode as Internal Voltage Reference. In this mode the VREFBUF generates a custom reference voltage on VREF+ pin.

There are multiple voltage output options available on this MCU. I am selecting the 1.5V Output Voltage.

Note that the VREF+ pin is automatically configured as VREFBUF_OUT. We can even use this pin to power our sensor, but remember that the current output is very low (around 4mA).

Result

Although we do not need to measure the VREF now, since we are using VREFBUF to generate it. But I am keeping the code same, so that we can get an idea if the voltage generated is same.

Also I have removed the external power supply from this Vref+ pin.

Below is the image showing the result of this project.

You can see the Vref is measured as 1500mV, which is the same as generated by the VREFBUF. Also note that we are able to measure the input voltage using this internally generated reference voltage.



Check out the Video Below




Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

Subscribe
Notify of

0 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments