Introducing to STM32 ADC programming(Internal Temperature Sensor)

CPU/STM32F103 2014. 4. 12. 13:13

http://www.embedds.com/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.

STM_internal_temperature_sensor

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include "stm32f10x.h"
#include "usart.h"
#include <stdio.h>
uint16_t AD_value;
const uint16_t V25 = 1750;// when V25=1.41V at ref 3.3V
const uint16_t Avg_Slope = 5; //when avg_slope=4.3mV/C at ref 3.3V
uint16_t TemperatureC;
int main(void)
{
//initialize USART1
Usart1Init();
//enable ADC1 clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_InitTypeDef ADC_InitStructure;
//ADC1 configuration
//select independent conversion mode (single)
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
//We will convert single channel only
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
//we will convert one time
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
//select no external triggering
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
//right 12-bit data alignment in ADC data register
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
//single channel conversion
ADC_InitStructure.ADC_NbrOfChannel = 1;
//load structure values to control and status registers
ADC_Init(ADC1, &ADC_InitStructure);
//wake up temperature sensor
ADC_TempSensorVrefintCmd(ENABLE);
//ADC1 channel16 configuration
//we select 41.5 cycles conversion for channel16
//and rank=1 which doesn't matter in single mode
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_41Cycles5);
//Enable ADC1
ADC_Cmd(ADC1, ENABLE);
//Enable ADC1 reset calibration register
ADC_ResetCalibration(ADC1);
//Check the end of ADC1 reset calibration register
while(ADC_GetResetCalibrationStatus(ADC1));
//Start ADC1 calibration
ADC_StartCalibration(ADC1);
//Check the end of ADC1 calibration
while(ADC_GetCalibrationStatus(ADC1));
//Start ADC1 Software Conversion
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
//wait for conversion complete
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)){}
//read ADC value
AD_value=ADC_GetConversionValue(ADC1);
//clear EOC flag
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
printf("\r\n ADC value: %d \r\n", AD_value);
TemperatureC = (uint16_t)((V25-AD_value)/Avg_Slope+25);
printf("Temperature: %d%cC\r\n", TemperatureC, 176);
while (1)
  {
    //interrupts does the job
  }
}

Using standard peripheral functions everything becomes obviously simple. First we set enable ADC peripheral clock:

1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

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:

1
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:

1
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_41Cycles5);

Then we enable ADC:

1
ADC_Cmd(ADC1, ENABLE);

After microcontroller is powered on it is recommended to run ADC self calibration. This calculates error correction codes for capacitors and reduces over all error in result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Enable ADC1 reset calibration register
 
ADC_ResetCalibration(ADC1);
 
//Check the end of ADC1 reset calibration register
 
while(ADC_GetResetCalibrationStatus(ADC1));
 
//Start ADC1 calibration
 
ADC_StartCalibration(ADC1);
 
//Check the end of ADC1 calibration
 
while(ADC_GetCalibrationStatus(ADC1));

Now its time to start conversion. The function for this is:

1
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

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

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

Then we can take ADC value from data register:

1
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:

1
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.

Example source code is here: STM32DiscoveryADCSingle.zip

'CPU > STM32F103' 카테고리의 다른 글

STOP MODE 설정방법  (0) 2014.04.13
EXTI (외부인터럽트 설정)  (0) 2014.04.13
IAR 브레이크 포인트 잡히지 않을때 ???  (0) 2014.04.10
stm32f103c8 메모리 설정 확인  (0) 2014.04.09
BOOT MODE에 관한 내용  (0) 2013.06.20