검색결과 리스트
글
Introducing to STM32 ADC programming(Internal Temperature Sensor)
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.
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 |