STM32 F4 ADC DMA Temperature Sensor

Goal: detecting temperature variations using a temperature sensor, ADC with DMA and TIM3 as a trigger (ADC sampling frequency = TIM3 trigger frequency).

Note: Using TIM3 as a trigger is suited for monitoring temperature variations over time. However, this is an overkill for checking the absolute temperature once in a while; a software trigger would be a better option for that.

Description: 
The temperature sensor is connected to ADC1 input channel 16. TIM3 will trigger periodically (CK_CNT/(Autoreload value+1)) and ADC1 will sample the signal using the 3-cycle sample time. I will use the 12-bit resolution which results in the total of 15-cycle conversion time (including the 3-cycle sampling time). Now, the ADC clock frequency is 21MHz (driven by the prescaled APB2). The total conversion time is then 15/21Mhz = ~714 ns (~1.4Msps max). A DMA request is made after each conversion, which places the converted voltage value into a user-specified buffer. An end-of-conversion (EOC) flag generates an interrupt at the TIM3 trigger frequency (with a 15 cycle offset). TIM3 shouldn't trigger faster than the minimum conversion time (~714ns). In the case of measuring the temperature, sampling frequency is not an issue so I'll set up TIM3 to trigger, say, 2000 times per second.

ADC sampling frequency: 2kHz (max is 1.4MHz with a 21MHz ADC clock, 12-bit resolution and 3-cycle sampling).
ADC interrupt frequency: 2kHz (I will use ADC_IRQHandler() to toggle a pin to make sure my calculations are correct, the toggling frequency should be 1kHz).

Now, we end up with some value in the user-defied buffer (which the DMA writes into at the end of each conversion, 2000 times per second). The manual gives us a linear algebraic formula for calculating the temperature: Temperature (in °C) = {(VSENSE – V25) / Avg_Slope} + 25
VSENSE is the 12-bit value we get from ADC in our buffer (this is not the voltage value).
VSENSE should be multiplied by the ADC resolution step (Vref/4095) to get the actual voltage value.
V25 is 0.76V (voltage that sensor outputs at 25°C)
Avg_Slope is 0.0025V/°C (rate at which voltage changes when temperature changes by one degree Celsius)

According to the manual, the offset of the function can be up to 45°C due to process variation so it'll require some calibration if we plan to measure absolute temperature.
I will pass the ADC values through an averaging filter to improve the variation accuracy. The filter also removes highest and lowest samples (can add a more sophisticated mechanism here).

#include <stm32f4xx.h>
#include "mcu_init.h" //====================================================================================
// Global variables for temperature measurements
//====================================================================================
volatile uint16_t ADC_Raw[NS] = {}; // Updated 2000 times per second by DMA
uint16_t Sample_ADC_Raw[NS] = {}; // Non-volatile copy of ADC_Raw[NS]
uint32_t ADC_Average = ; // Average of the samples
float Temp = ; // Temporary register
float Temp_Celsius = ; // Temperature in Celsius
float Calibration_Value = 11.0; // For measuring absolute temperature //====================================================================================
// Functions used in this module
//====================================================================================
void Sort_values(uint16_t [], uint8_t);
float Get_Temperature(void); //====================================================================================
// main function
//====================================================================================
int main()
{ //ADC_Interrupt_Config(); // Indirectly testing my calculations
//GPIOD_Config(); // Indirectly testing my calculations
TIM3_Config();
ADC_Config(); while ()
{
Temp_Celsius = Get_Temperature(); // Monitoring Temp_Celsius in debug mode // Set a threshold value, light up LEDs if Temp_Celsius goes above or below it.
// I put it in the freezer to test :) } } //====================================================================================
// Description: Averaging samples from ADC, calculating temperature in Celsius
//====================================================================================
float Get_Temperature(void)
{
uint8_t i; for(i = ; i < NS; i++)
{
Sample_ADC_Raw[i] = ADC_Raw[i];
} Sort_values(Sample_ADC_Raw, NS); ADC_Average = ;
for(i = SR/; i < NS-SR/; i++)
{
ADC_Average += Sample_ADC_Raw[i];
}
ADC_Average /= (NS-SR); Temp += ADC_Average;
Temp *= ;
Temp /= ;
Temp -= (float)0.76;
Temp /= (float)0.0025;
Temp += (float)25.0;
Temp -= Calibration_Value; return Temp;
} //====================================================================================
// Description: Bubble sort min to max
//====================================================================================
void Sort_values(uint16_t A[], uint8_t L)
{
uint8_t i = ;
uint8_t status = ; while(status == )
{
status = ;
for(i = ; i < L-; i++)
{
if (A[i] > A[i+])
{
A[i]^=A[i+];
A[i+]^=A[i];
A[i]^=A[i+];
status = ;
}
}
}
} void ADC_IRQHandler(void) // (for testing)
{
GPIO_ToggleBits(GPIOD, GPIO_Pin_9);
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
}
#ifndef __MCU_INIT_H
#define __MCU_INIT_H #include <stm32f4xx.h> #define NS 10 // Number of samples to get from ADC
#define SR 4 // Samples removed after sorting, 4=(2 highest & 2 lowest)
#define ADC1_RDR 0x4001204C // ADC1 Regular Data Register (read only) extern volatile uint16_t ADC_Raw[NS]; // DMA writes ADC values into this buffer //====================================================================================
// Functions used for measuring temperature variations
//====================================================================================
void GPIOD_Config(void); // Indirectly testing my calculations
void ADC_Interrupt_Config(void); // Indirectly testing my calculations
void TIM3_Config(void);
void ADC_Config(void); #endif // __MCU_INIT_H
#include "mcu_init.h"
#include <stm32f4xx.h> //====================================================================================
// Configuring TIM3 to trigger at 2kHz which is the ADC sampling rate
//====================================================================================
void TIM3_Config(void)
{
TIM_TimeBaseInitTypeDef TIM3_TimeBase; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_TimeBaseStructInit(&TIM3_TimeBase);
TIM3_TimeBase.TIM_Period = (uint16_t); // Trigger = CK_CNT/(49+1) = 2kHz
TIM3_TimeBase.TIM_Prescaler = ; // CK_CNT = 42MHz/420 = 100kHz
TIM3_TimeBase.TIM_ClockDivision = ;
TIM3_TimeBase.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM3_TimeBase);
TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); TIM_Cmd(TIM3, ENABLE);
} //====================================================================================
// Configuring GPIO PD9 (for testing)
//====================================================================================
void GPIOD_Config(void)
{
GPIO_InitTypeDef gpio_D; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); gpio_D.GPIO_Mode = GPIO_Mode_OUT;
gpio_D.GPIO_OType = GPIO_OType_PP;
gpio_D.GPIO_Pin = GPIO_Pin_9;
gpio_D.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_D.GPIO_Speed = GPIO_Medium_Speed;
GPIO_Init(GPIOD, &gpio_D); } //====================================================================================
// Configuring ADC global interrupt (for testing)
//====================================================================================
void ADC_Interrupt_Config(void)
{
NVIC_InitTypeDef NVIC_ADC1; NVIC_ADC1.NVIC_IRQChannel = ADC_IRQn;
NVIC_ADC1.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_ADC1);
} //====================================================================================
// Configuring ADC with DMA
//====================================================================================
void ADC_Config(void)
{
ADC_InitTypeDef ADC_INIT;
ADC_CommonInitTypeDef ADC_COMMON;
DMA_InitTypeDef DMA_INIT; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); DMA_INIT.DMA_Channel = DMA_Channel_0;
DMA_INIT.DMA_PeripheralBaseAddr = (uint32_t)ADC1_RDR;
DMA_INIT.DMA_Memory0BaseAddr = (uint32_t)&ADC_Raw[];
DMA_INIT.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_INIT.DMA_BufferSize = NS;
DMA_INIT.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_INIT.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_INIT.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_INIT.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_INIT.DMA_Mode = DMA_Mode_Circular;
DMA_INIT.DMA_Priority = DMA_Priority_High;
DMA_INIT.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_INIT.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_INIT.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_INIT.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream4, &DMA_INIT);
DMA_Cmd(DMA2_Stream4, ENABLE); ADC_COMMON.ADC_Mode = ADC_Mode_Independent;
ADC_COMMON.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_COMMON.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_COMMON.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_COMMON); ADC_INIT.ADC_Resolution = ADC_Resolution_12b;
ADC_INIT.ADC_ScanConvMode = DISABLE;
ADC_INIT.ADC_ContinuousConvMode = DISABLE; // ENABLE for max ADC sampling frequency
ADC_INIT.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_INIT.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
ADC_INIT.ADC_DataAlign = ADC_DataAlign_Right;
ADC_INIT.ADC_NbrOfConversion = ;
ADC_Init(ADC1, &ADC_INIT); ADC_RegularChannelConfig(ADC1, ADC_Channel_16, , ADC_SampleTime_3Cycles);
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); // (for testing)
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_TempSensorVrefintCmd(ENABLE);
}
Summary: 

To work with other sensors, follow these steps:
 - choose the ADC channel
 - choose the sampling rate
 - adjust the TIMx trigger frequency
 - configure DMA

A software trigger is suited for reading sensor's values on demand (turn off ADC when not using).

STM32 F4 ADC DMA Temperature Sensor的更多相关文章

  1. STM32 F4 DAC DMA Waveform Generator

    STM32 F4 DAC DMA Waveform Generator Goal: generating an arbitrary periodic waveform using a DAC with ...

  2. 关于Stm32定时器+ADC+DMA进行AD采样的实现

    Stm32的ADC有DMA功能这都毋庸置疑,也是我们用的最多的!然而,如果我们要对一个信号(比如脉搏信号)进行定时采样(也就是隔一段时间,比如说2ms),有三种方法: 1.使用定时器中断每隔一定时间进 ...

  3. 【STM32】用DMA实现多路ADC通道数据采集

    今天尝试了下STM32的ADC采样,并利用DMA实现采样数据的直接搬运存储,这样就不用CPU去参与操作了. 找了不少例子参考,ADC和DMA的设置了解了个大概,并直接利用开发板来做一些实验来验证相关的 ...

  4. STM32 多通道ADC采样,采用Timer1进行采样率控制,利用DMA进行传输

    http://blog.csdn.net/varding/article/details/17559399 http://www.51hei.com/stm32/3842.html https://w ...

  5. STM32之ADC实例(基于DMA方式)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zouleideboke/article/details/75112224 ADC简介: ADC(An ...

  6. STM32之ADC+步骤小技巧(英文)

    神通广大的各位互联网的网友们.大家早上中午晚上好好好.今早起来很准时的收到了两条10086的扣月租的信息.心痛不已.怀着这心情.又开始了STM32的研究.早上做了计算机控制的PID实验,又让我想起了飞 ...

  7. STM32 双ADC同步规则采样

      最近需要用到两个ADC对电压电流进行同步采样,看了一下STM32的ADC介绍,发现STM32最多有3个独立ADC,有在双AD模式下可以进行同步测量,正好满足我的要求.参考官方给的例子在结合自己的需 ...

  8. 硬件——STM32,ADC篇

    未完,待续...... 也就是stm32f10X系列的adc采集出来的结果是12位的 stm32f10X系列有两个16位adc 关于程序的编写方法:一般  “某某.c文件”:都是用来设置“某某”的一些 ...

  9. STM32之串口DMA接收不定长数据

    STM32之串口DMA接收不定长数据 引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? 同学A:数据来了就会进入串口 ...

随机推荐

  1. 20155314 2016-2017-2 《Java程序设计》第6周学习总结

    20155314 2016-2017-2 <Java程序设计>第6周学习总结 教材学习内容总结 理解流与IO 理解InputStream/OutPutStream的继承架构 理解Reade ...

  2. HDU 1005 Number Sequence (模拟)

    题目链接 Problem Description A number sequence is defined as follows: f(1) = 1, f(2) = 1, f(n) = (A * f( ...

  3. shell expect的简单用法【转】

    用expect实现自动登录的脚本,网上有很多,可是都没有一个明白的说明,初学者一般都是照抄.收藏.可是为什么要这么写却不知其然.本文用一个最短的例子说明脚本的原理. 脚本代码如下:  ######## ...

  4. shell脚本 ------ 输出带颜色的字体

    shell脚本中echo显示内容带颜色显示,echo显示带颜色,需要使用参数-e 格式如下: echo -e “\033[字背景颜色:文字颜色m字符串\033[0m” 例如: echo -e “\03 ...

  5. fastdfs:安装nginx

    #安装依赖 yum -y install gcc yum -y install gcc-c++ yum -y install zlib-devel yum -y install pcre-devel ...

  6. 数组的splice方法

    splice 该方法向或者从数组中添加或者删除项目,返回被删除的项目,同时也会改变原数组. splice(index,howmany,item1,...itemX) index参数:必须,整数,规定添 ...

  7. PHP取整函数之ceil,floor,round,intval的区别

    我们经常用到的PHP取整函数,主要是:ceil,floor,round,intval. ceil -- 进一法取整 说明 float ceil ( float value ) 返回不小于 value ...

  8. Video.js 简单的使用介绍

    vedio.js 是一款视频播放插件,它会自动检测浏览器对 HTML5 的支持情况,如果不支持 HTML5 则自动使用 Flash 播放器.下面来介绍下它的使用: 引用video-js.cs样式文件和 ...

  9. 使用div模拟出frameset效果

    <!doctype html> <html xmlns="http://www.w3.org/1999/xhtml" > <head> < ...

  10. JS高级程序设计 表单部分

    目录:     表单:1.引用表单           2.提交表单           3.重置表单           4.表单字段 在HTML中,表单是由<form>元素来表示的,而 ...