STM32F4 Temperature Sensor and FreeRTOS
These last few days temperatures outside were low so I decided to measure them without getting out too often :). I used STM32F4, its internal temperature sensor and HC05 bluetooth module and received data on Android with my ABTerminal. Additionally I used FreeRTOS just to make it more interesting. The project can be found on GitHub profile dumpram or just go to Repository section on this blog. I used Keil uVision4. Here is how I did it.
Measurment often implies work with ADC peripheral. It is pretty easy to get it working using Standard Peripheral Drivers. Serial communication tutorial can be found on this link. First initialize ADC. You need to enable clock on suiting peripheral bus. For ADC1 bus is APB2. Other properties are given in this snippet:
ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent; ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div8; ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; ADC_CommonInit(&ADC_CommonInitStruct); ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b; ADC_InitStruct.ADC_ScanConvMode = DISABLE; ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_NbrOfConversion = 1; ADC_Init(ADC1, &ADC_InitStruct);
Furthermore, for configuring temperature sensor use this code:
/* Connect ADC1 to temperature sensor */ ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor, 1, ADC_SampleTime_144Cycles); /* Enable internal temperature senosor */ ADC_TempSensorVrefintCmd(ENABLE);
After initialization you can get temperature from ADC with following function. I think, I found magic numbers in code in STM32F4 datasheet.
/** * Function gets current temperature value. * @return current temperature value */ double get_temperature() { double tempValue; ADC_SoftwareStartConv(ADC1); //adc starts conversion while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET) { //waiting conversion to end } //these next calculations i found somewhere in stm32f4 //manual tempValue = ADC_GetConversionValue(ADC1); tempValue *= 3300; tempValue /= 0xfff; //this gets mV tempValue /= 1000.0; //this gets V tempValue -= 0.760; // subtract referent voltage tempValue /= .0025; // divided by step tempValue -= 11.0; // some kind of magic number return tempValue; }
Ok, this was pretty easy and should work after little debugging. Next step is to make interesting and introduce FreeRTOS. If you don't know what FreeRTOS. FreeRTOS is open source Real Time Operating System and it is very popular in embedded domain. But why do we need FreeRTOS? Ok for this particular project its use is little streched but that's why this project is ideal for first implementation(it is not very complicated). Download FreeRTOS source from here. Exract files from archive. You'll need files from folder FreeRTOS/Source. In "FreeRTOS/Source" folder there is folder "portable". From "portable" use only files for your device. In this case STM32F4. You should choose "port.c" and "portmacro.h" from "FreeRTOS/Source/RVDS/ARM_CM4F". Also you should use files from "MemMang" in "portable". This are files that manage heap memory in FreeRTOS. You need just one of them. I used heap_2.c. After adding all source files in project and including header in build path you should write "FreeRTOSConfig.h" header file. If you are starting with FreeRTOS and you don't know how, you can use config files from project which are included in examples in FreeRTOS downloaded archive. Find example for CortexM4 and it will suit STM32F4. You can delete macros defined for specific project in that file. At this point building project is easy. Before I point out more problems. Let's see the source "main.c".
#include "usart.h" #include "temp.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" #define NUM_OF_SAMPLES 100 xQueueHandle tempQueue; void delay() { int i = 8000000; while(i--); } void calcAverageTemperature() { int i; double avrTemp = 0; init_ADC(); while(1) { for (i = 0; i < NUM_OF_SAMPLES; i++) { avrTemp = (avrTemp + get_temperature())/2; xQueueSendToBack(tempQueue, &avrTemp, 0); } } } void sendTemperature() { double avrTemp; char temp[10]; init_USART1(9600); while(1) { USART_puts(BLUETOOTH_TERMINAL, "Current temperature is: "); xQueueReceive(tempQueue, &avrTemp, 0); sprintf(temp, "%f", avrTemp); USART_puts(BLUETOOTH_TERMINAL, temp); USART_puts(BLUETOOTH_TERMINAL, "\n"); delay(); } } int main(){ tempQueue = xQueueCreate(1, sizeof(double)); xTaskCreate(sendTemperature, "temp_send", 128, NULL, 1, NULL); xTaskCreate(calcAverageTemperature, "temp_calc", 128, NULL, 1, NULL); vTaskStartScheduler(); }
In main function there is nothing special. Just creating tasks that FreeRTOS will execute. Also tempQueue is created for sending temperature value from task that's calculating average value of 100 samples because values vary between few measurements. There is function that's calculating average temperature and function which is sending data to bluetooth terminal. This way task which sends data is seperated from task calculating and getting data from sensor. This way code isn't monolithic and it is easier to debug. After getting 100 samples from ADC calcAverageTemperature task is putting value in Queue(FIFO principle - FirstInFirstOut). Furthermore, sendTemperature task is receiving value from queue and sends it to terminal.
Final summation
I spend two hours debugging this code. Because tasks were not executing. Why? Let's go back to FreeRTOSConfig header file. Make sure you define next things:
#define xPortPendSVHandler PendSV_Handler #define vPortSVCHandler SVC_Handler #define xPortSysTickHandler SysTick_Handler
This will ensure that only FreeRTOS SVHandlers are invoked and not the dummy ones in STM32F4 startup file. OK, this was pretty much it. Stay tuned for updates. Come here if you deer.









