STM32 F4 ADC DMA Temperature Sensor
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);
}
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的更多相关文章
- STM32 F4 DAC DMA Waveform Generator
STM32 F4 DAC DMA Waveform Generator Goal: generating an arbitrary periodic waveform using a DAC with ...
- 关于Stm32定时器+ADC+DMA进行AD采样的实现
Stm32的ADC有DMA功能这都毋庸置疑,也是我们用的最多的!然而,如果我们要对一个信号(比如脉搏信号)进行定时采样(也就是隔一段时间,比如说2ms),有三种方法: 1.使用定时器中断每隔一定时间进 ...
- 【STM32】用DMA实现多路ADC通道数据采集
今天尝试了下STM32的ADC采样,并利用DMA实现采样数据的直接搬运存储,这样就不用CPU去参与操作了. 找了不少例子参考,ADC和DMA的设置了解了个大概,并直接利用开发板来做一些实验来验证相关的 ...
- STM32 多通道ADC采样,采用Timer1进行采样率控制,利用DMA进行传输
http://blog.csdn.net/varding/article/details/17559399 http://www.51hei.com/stm32/3842.html https://w ...
- STM32之ADC实例(基于DMA方式)
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zouleideboke/article/details/75112224 ADC简介: ADC(An ...
- STM32之ADC+步骤小技巧(英文)
神通广大的各位互联网的网友们.大家早上中午晚上好好好.今早起来很准时的收到了两条10086的扣月租的信息.心痛不已.怀着这心情.又开始了STM32的研究.早上做了计算机控制的PID实验,又让我想起了飞 ...
- STM32 双ADC同步规则采样
最近需要用到两个ADC对电压电流进行同步采样,看了一下STM32的ADC介绍,发现STM32最多有3个独立ADC,有在双AD模式下可以进行同步测量,正好满足我的要求.参考官方给的例子在结合自己的需 ...
- 硬件——STM32,ADC篇
未完,待续...... 也就是stm32f10X系列的adc采集出来的结果是12位的 stm32f10X系列有两个16位adc 关于程序的编写方法:一般 “某某.c文件”:都是用来设置“某某”的一些 ...
- STM32之串口DMA接收不定长数据
STM32之串口DMA接收不定长数据 引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? 同学A:数据来了就会进入串口 ...
随机推荐
- 20155339 2016-2017-2 《Java程序设计》第8周学习总结
20155339 2016-2017-2 <Java程序设计>第8周学习总结 教材学习内容总结 第十四章NIO与NIO2 NIO使用频道来衔接数据节点,在处理数据时,NIO可以让你设定缓冲 ...
- UI渲染回顾简单笔记
UI渲染的简单过程: CPU,GPU,显示器协同工作,CPU 中计算显示内容,比如视图的创建.布局计算.图片解码.文本绘制等,然后将计算结果提交给GPU,由 GPU 进行变换.合成.渲染.随后 GPU ...
- UVALive 7456 Least Crucial Node
题目链接 题意: 给定一个无向图,一个汇集点,问哪一个点是最关键的,如果有多个关键点,输出序号最小的那个. 因为数据量比较小,所以暴力搜索就行,每去掉一个点,寻找和汇集点相连的还剩几个点,以此确定哪个 ...
- 第10月第5天 v8
1. brew install v8 http://www.cnblogs.com/tinyjian/archive/2017/01/17/6294352.html http://blog.csdn. ...
- ajax调用WebService 不能跨域
http://www.cnblogs.com/dojo-lzz/p/4265637.html "Access-Control-Allow-Origin":'http://local ...
- IOC创建对象的几种方式
接上一篇IOC入门 IOC创建对象的几种方式 1)调用无参数构造器 2)带参数构造器 3)工厂创建对象 工厂类:静态方法创建对象 工厂类:非静态方法创建对象 1.对之前的User类进行一些修改,加上一 ...
- 2018年长沙理工大学程序设计竞赛 J - 杯子
题意: 链接:https://www.nowcoder.com/acm/contest/96/J一天durong同学买了一个无限长的杯子,同时买了n个球,并且标号为1,2,3......n,duron ...
- CSS------div无法覆盖图片全部如何处理
如图: 代码:(需要将li中的样式属性display设置为inline-block) //获取Url地址中的参数 function getParameter(name) { //正则表达式 var r ...
- oj提交时常见错误归纳
Presentation Error: 常见的PE错误应该有以下的几种情况: 每行输出之后有空行 每两行输出之间有空行 一行中,每个输出数字(或字符串,等)之间有空格 一行中,每个输出数字(或字符串, ...
- mybatis3中@SelectProvider的使用技巧
mybatis的原身是ibatis,现在已经脱离了apache基金会,新官网是http://www.mybatis.org/. mybatis3中增加了使用注解来配置Mapper的新特性,本篇文章主要 ...