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. 20155338 2016-2017-2 《Java程序设计》第6周学习总结

    20155338 2016-2017-2 <Java程序设计>第6周学习总结 教材学习内容总结 输入和输出 • 串流设计概念 要想活用输入/输出API,一定先要了解Java中如何以串流抽象 ...

  2. 第7月第27天 c++11 boost

    1. #include <boost/asio.hpp> #include <cstdlib> #include <memory> namespace asio = ...

  3. DataTable转Json(兼容easyUI特殊json分页)

    用法:上述方法是DataTable的扩展方法:静态类静态方法,变量前用this (一)ps:普通datatable转标准json DataTable dt = 获取db中的datatable数据. s ...

  4. Servlet、ServletConfig、ServletContext深入学习

    1.Servlet学习 1.Servlet生命周期 Servlet 加载—>实例化—>服务—>销毁. init(servletConfig):(经过自己的测试发现会先调用这个而不是i ...

  5. 环形缓冲区-模仿linux kfifo【转】

    转自:https://blog.csdn.net/vertor11/article/details/53741681 struct kfifo{ uint8_t *buffer; uint32_t i ...

  6. 【原创】Linux环境下的图形系统和AMD R600显卡编程(2)——Framebuffer、DRM、EXA和Mesa简介【转】

    转自:http://www.cnblogs.com/shoemaker/p/linux_graphics02.html 1. Framebuffer Framebuffer驱动提供基本的显示,fram ...

  7. mac 安装gevent报错

    运行pip install gevent报错 错误信息如下 xcrun: error: invalid active developer path (/Library/Developer/Comman ...

  8. 在阿里云申请Symantec免费SSL证书操作流程

    2016年阿里云与国内证书颁发机构天威诚信推出了基于Symantec(赛门铁克)的免费SSL证书,有需要免费SSL证书产品的可以前往阿里云进行申请. 申请地址:阿里云云盾证书服务—Symantec免费 ...

  9. JavaScript:document.execCommand()的用法

    document.execCommand()的用法小记 一.语法 execCommand方法是执行一个对当前文档,当前选择或者给出范围的命令.处理Html数据时常用. 如下格式:document.ex ...

  10. Carbon 的 diffForHumans 方法

    Carbon 是继承自 PHP DateTime 类 的子类,但比后者提供了更加丰富.更加语义化的 API.其中一个比较实用的 API 就是 diffForHumans 方法,几乎每个用 Larave ...