Introducing to STM32 ADC programming. Part2

After we had a quick overview of STM32 ADC peripheral we can start digging deeper in to specifics. In order to understand simple things lets go with simplest case – single conversion mode. In this mode ADC does one conversion and then stops. After ADC conversion result is stored in to 16-bit ADC_DR data register (remember that conversion result is 12-bit), then End of Conversion (EOC) flag is set and interrupt is generated if EOCIE flag is set. Same situation is if injected channel is converted. The difference is that result is stored in to corresponding ADC_DRJx register, JEOC flag is set and interrupt generated if JEOCIE flag is set.

In our example we are going to measure the internal temperature sensor value and send it using USART. Temperature sensor is internally connected to ADC1_IN16 channel. Algorithm will start single conversion and wait for conversion complete flag EOC. Then we are going to read ADC value from ADC_DR register, which later will be used to calculate in temperature value in Celsius and sent via USART. So we should see value in terminal screen. As usually we are going to use Standard peripheral library and CMSIS functions. The code for single conversion is pretty short:

It is connected to APB2 bus. We aren’t using any prescallers here so it works at 24MHz here.

Next thing is to set up ADC mode using ADC_initstructure. Here we select ADC_Mode_Independent which in our case is single conversion. Then we disable scan mode and continuous scan as we want only single conversion and stop. We also disable any external triggering and select data to be right aligned. In order to access temperature sensor we need to bring it from power down by writing TSVREFE bit in ADC_CR2 register. This is done by using command:

ADC_TempSensorVrefintCmd(ENABLE);

After ADC is set up and sensor is waked we can configure channel 16. Here we need to set several parameters including channel number, index in group and channel sampling time:

Next thing is to wait for conversion complete by checking for ADC_FLAG_EOC flag in status register:

while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)){}

Then we can take ADC value from data register:

AD_value=ADC_GetConversionValue(ADC1);

After reading temperature sensor we get some 12-bit digital value. In order to calculate temperature we need to use formula:

Temperature = (V25-AD_value)/Avg_slope+25

V25 and Avg_slope values can be found on microcontroller datasheet:

Here we get that V25 is typically 1.41V and Avg_Slope=4.3. When using 3.3V supply as reference we get approximately V25=1750 and Avg_Slope=5. Having these temperature value can be easily calculated:

TemperatureC = (uint16_t)((V25-AD_value)/Avg_Slope+25);

Don’t actually rely on readings as manufacturer states it can vary from chip to chip up to 45ºC. It can serve only to detect temperature variations inside chip. It is always better to use external sensor for accurate readings.

This is how data looks in terminal screen. First temperature is read normally and second with some heat applied to microcontroller:

This code is only to demonstrate simplest ADC usage. Practically it is Very inefficient because there are loop used for waiting ADC to be complete. There are better ways of doing this like using interrupts or DMA. Next time we will try different ADC mode used in efficient way.

How to find out on which channel was made last conversion. Could you make some example of basic multi channel conversion, where every channel has another function (meaning of different source) and where will be seems how to get these different values without DMA?

Where is it stated that the temp sensor is connected to ADC1_IN16 ? In DS (CD00161566.pdf) on page 20 it is written that temp sensor is connected to ADC12_IN16. But there is no ADC12, right? I am confused now. Could you please explain it? And I tried your code it works but I am getting 433 as adc result and 288 as temperatur. Ohh, maybe because of the clock settings? I have 8MHz external xtal and CPU runs at 72 MHz. What shall I do? Thanks.