实验目的
STM32内部集成了一个温度传感器,可以用来测量芯片的温度,本章学习如何读取此温度值,数据手册请参看第11章。
实验简介
STM32的内部温度传感器在内部和ADCx_IN16输入通道相连接,此通道把传感器输出的电压换成数字。温度传感器模拟输入推荐采样时间是17.1us。STM32的内部温度传感器支持的范围为-40~125度。精度比较差,为±1.5度左右。STM32内部温度传感器的使用很简单,只要设置一下内部ADC,并激活其内部通道就差不多了。
读温度
为使用传感器:
1.选择ADC1_IN16输入通道
2.选择采样时间为17.1us
3.设置ADC控制寄存器2(ADC_CR2)的TSVREFE位,以唤醒关电模式下温度传感器
4.通过设置ADON位启动ADC转换(或用外部触发)
5.读ADC数据寄存器上的Vsense数据结果
6.利用下列公式得出温度
温度( °C )= {(V25 - Vsense)/Avg_Slope } +25;
这里:
V25 = Vsense在25°C时的数值
Avg_Slope = 温度与Vsense曲线的平均斜率(单位为mV/°C或uV/°C)
其中V25和Avg_Slope的参考值在下图中
代码
main.c
#include "MyIncludes.h"
char buff[100];
int16_t Val;
u16 sys_cnt = 0;
void systick_isr(void)
{
if(sys_cnt < 1000)
sys_cnt++;
else
{
sys_cnt = 0;
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4|GPIO_PIN_5);
}
}
int main(void)
{
System_Init();
LED_Init();
SysTick_Init(systick_isr);
USART1_Init(115200,NULL,NULL);
STM32_TempSample_Init();
//内部温度采集
while(1)
{
delay_ms(1000);
Val = Temp_Sample();
if(Val < 0)
sprintf(buff,"Chip Temp: -%d%d.%d'C\n",Val/1000,Val%1000/100,Val%100/10);
else
sprintf(buff,"Chip Temp: %d%d.%d'C\n",Val/1000,Val%1000/100,Val%100/10);
printf(buff);
}
}
adc.c
#ifndef __ADC_H_
#define __ADC_H_
#include "stm32f1xx.h"
#include "stm32_types.h"
#include "stm32f1xx_hal.h"
#define ADC_DMA_ENABLE
//使能DMA传输
typedef struct
{
void (*isr_op)(void);
//中断处理
} _ADC_ISR_;
extern ADC_HandleTypeDef AdcHandle;
void STM32_ADC_Init(ADC_TypeDef *ADCx,uint32_t Channel,void(*ISR)(void));
//ADC初始化
uint32_t Vol_Sample(void);
//电压采样
void STM32_VRefSample_Init(void);
//内部参考电压Vref采集
uint16_t VRef_Sample(void);
//内部参考电压Vref采样
void STM32_TempSample_Init(void);
//内部温度传感器采集
int Temp_Sample(void);
//内部温度采样
#endif
adc.c
#include "adc.h"
ADC_HandleTypeDef AdcHandle;
//ADC句柄结构变量声明
_ADC_ISR_ adc_isr;
//DMA句柄结构变量声明
#ifdef ADC_DMA_ENABLE
//使能DMA
uint32_t aADCxConvertedValues;
#endif
//在HAL_ADC_Init中调用
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{
GPIO_InitTypeDef GPIO_InitStruct;
//GPIO初始化结构变量声明
static DMA_HandleTypeDef hdma_adc;
//DMA句柄结构变量声明
RCC_PeriphCLKInitTypeDef PeriphClkInit;
//RCC扩展时钟结构变量声明
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
//要配置的扩展时钟 ADC外围时钟
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
//ADC时钟源 可以是预分频器的值 PCLK(IO接口时钟)2/6
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);//RCC外围时钟源配置
__ADC1_CLK_ENABLE();
//使能ADC1时钟
__GPIOA_CLK_ENABLE();
//使能GPIOA时钟
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
//模拟输入模式
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
//GPIO初始化配置
#ifdef ADC_DMA_ENABLE
__HAL_RCC_DMA1_CLK_ENABLE();
//使能DMA1时钟
hdma_adc.Instance = DMA1_Channel1;
//寄存器基址
hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;
//外设到内存方向
hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;
//外围增量模式禁用
hdma_adc.Init.MemInc = DMA_MINC_ENABLE;
//内存增量模式启用
hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
//外围数据对齐 半字
hdma_adc.Init.MemDataAlignment = DMA_PDATAALIGN_HALFWORD;
//外围数据对齐 半字
hdma_adc.Init.Mode = DMA_CIRCULAR;
//圆形模式
hdma_adc.Init.Priority = DMA_PRIORITY_HIGH;
//优先级 高
HAL_DMA_Init(&hdma_adc);
//DMA 初始化
__HAL_LINKDMA(hadc, DMA_Handle, hdma_adc);
//将初始化的DMA句柄与ADC句柄关联
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
//设置优先级
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);//设置中断源
#endif
}
void STM32_ADC_Init(ADC_TypeDef *ADCx, uint32_t Channel, void (*ISR)(void))
{
ADC_ChannelConfTypeDef sConfig;
//ADC通道结构变量声明
adc_isr.isr_op = ISR;
//挂载中断处理函数
//配置ADC外设
AdcHandle.Instance = ADCx;
//寄存器基址ADC1
AdcHandle.Init.ScanConvMode = ADC_SCAN_DISABLE;
//禁止扫描模式
AdcHandle.Init.ContinuousConvMode = ENABLE;
//使能连续转换
AdcHandle.Init.DiscontinuousConvMode = DISABLE;
//禁止常规通道不连续采样
AdcHandle.Init.NbrOfDiscConversion = 0;
//不连续采样模式下的转换常规通道
AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
//软件启动
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
//右对齐
AdcHandle.Init.NbrOfConversion = 1; //常规通道序列长度,2次转换
HAL_ADC_Init(&AdcHandle);
//ADC初始化
//配置ADC通道
sConfig.Channel = Channel;
//转换通道
sConfig.Rank = ADC_REGULAR_RANK_1;
//指定常规组序列器的列组
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
//要为选定通道设置的采样时间值
HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
//ADC配置通道
HAL_ADCEx_Calibration_Start(&AdcHandle);
//ADC校准
#ifdef ADC_DMA_ENABLE //ʹÄÜDMA
//使能DMA
HAL_ADC_Start_DMA(&AdcHandle, &aADCxConvertedValues, sizeof(aADCxConvertedValues));
#else
HAL_ADC_Start_IT(&AdcHandle);
//使能ADC中断,启动ADC
#endif
}
void ADC_IRQHandler(void)
//ADC中断函数
{
HAL_ADC_IRQHandler(&AdcHandle);
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
//转换完成回调,在HAL_ADC_IRQHandler中调用
{
if(adc_isr.isr_op != NULL) adc_isr.isr_op();
}
#ifdef ADC_DMA_ENABLE
//使能DMA
void DMA1_Channel1_IRQHandler(void)
//DMA中断
{
HAL_DMA_IRQHandler(AdcHandle.DMA_Handle);
}
#endif
//电压采样
uint32_t Vol_Sample(void)
{
#ifdef ADC_DMA_ENABLE
//使能DMA
return (aADCxConvertedValues&0xFFF)*3300/4096;
#else
uint32_t Vol_ADC_Val;
Vol_ADC_Val = HAL_ADC_GetValue(&AdcHandle);
return Vol_ADC_Val*3300/4096;
//返回电压值扩大1000倍
#endif
}
ADC_HandleTypeDef AdcBatHandle;
//ADC句柄结构变量声明
void STM32_VRefSample_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
//配置ADC常规组结构变量声明
__ADC1_CLK_ENABLE();
//使能ADC1时钟
//配置ADC外设
AdcBatHandle.Instance = ADC1;
//寄存器基址
AdcBatHandle.Init.ScanConvMode = ADC_SCAN_DISABLE;
//禁止扫描模式
AdcBatHandle.Init.ContinuousConvMode = ENABLE;
//使能连续转换
AdcBatHandle.Init.DiscontinuousConvMode = DISABLE;
//禁止常规通道的不连续采样模式
AdcBatHandle.Init.NbrOfDiscConversion = 0;
//不连续采样模式下的转换常规通道数
AdcBatHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
//软件启动
AdcBatHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
//右对齐
AdcBatHandle.Init.NbrOfConversion = 1; //常规通道序列长度 2次转换
HAL_ADC_Init(&AdcBatHandle);
//ADC配置初始化
//配置ADC通道
sConfig.Channel = ADC_CHANNEL_VREFINT; //VBAT检测,通道为ADC1_IN17
sConfig.Rank = ADC_REGULAR_RANK_2;
//指定常规组序列器的列组
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
//要为选定通道设置的采样时间值
HAL_ADC_ConfigChannel(&AdcBatHandle, &sConfig);
//ADC配置通道
HAL_ADC_Start(&AdcBatHandle);
//启动ADC
}
uint16_t VRef_Sample(void)
//内部参考电压Vref采样
{
uint32_t VBat_ADC_Val;
VBat_ADC_Val = HAL_ADC_GetValue(&AdcBatHandle);
return VBat_ADC_Val*3300/4096;
//返回电压值扩大1000倍
}
void STM32_TempSample_Init(void)
//内部温度传感器采集
{
ADC_ChannelConfTypeDef sConfig;
__ADC1_CLK_ENABLE();
//使能ADC1时钟
AdcBatHandle.Instance = ADC1;
//寄存器基址
AdcBatHandle.Init.ScanConvMode = ADC_SCAN_DISABLE;
//禁止扫描模式
AdcBatHandle.Init.ContinuousConvMode = ENABLE;
//使能连续转换
AdcBatHandle.Init.DiscontinuousConvMode = DISABLE;
//禁止常规通道的不连续采样模式
AdcBatHandle.Init.NbrOfDiscConversion = 0;
//不连续采样模式下的转换常规通道数
AdcBatHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
//软件启动
AdcBatHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
//右对齐
AdcBatHandle.Init.NbrOfConversion = 1; //常规通道序列长度 2次转换
HAL_ADC_Init(&AdcBatHandle);
//ADC初始化
//配置通道
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
//内部温度采集 通道为ADC1_IN16;
sConfig.Rank = 1;
//指定常规组序列器的列组
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
//要为选定通道设置的采样时间值
HAL_ADC_ConfigChannel(&AdcBatHandle, &sConfig);
//ADC通道设置
HAL_ADC_Start(&AdcBatHandle);
//ADC启动
}
//内部温度采样
//温度 = {(V25 - VSENSE)/Avg_Slope} + 25;
//其中v25 = 1.43,Avg_Slope = 0.0043
//参考数据手册温度传感器电器特性一节
float temperate;
int Temp_Sample(void)
{
uint32_t ADC_Val;
float temperate;
ADC_Val = HAL_ADC_GetValue(&AdcBatHandle);
temperate = (float)ADC_Val*3.3/4096; //返回电压值
temperate = (1.43 - temperate)/0.0043 + 25;
return (int)temperate * 100;
//结果扩大100倍
}
实验现象
D5 D6 LED灯闪烁
HAL库代码
test.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "adc.h"
int main(void)
{
short temp;
Stm32_Clock_Init(9);//系统时钟设置
uart_init(72,115200);//串口初始化为115200
delay_init(72); //延时初始化
LED_Init(); //初始化与LED连接的硬件接口
Adc_Init();
while(1)
{
LED3 = !LED3;
temp = Get_Temperature();
printf("%hd.%hdC\r\n",temp/100,temp%100);
delay_ms(250);
}
}
adc.h
#ifndef __ADC_H
#define __ADC_H
#include "sys.h"
#define ADC_CH1 1
#define ADC_CH7 7 //通道7连接在PA7
#define ADC_CH_TEMP 16//温度传感器通道
short Get_Temperature(void);
void Adc_Init(void); //ADC通道初始化
u16 Get_Adc(u8 ch); //获得某个通道值
u16 Get_Adc_Average(u8 ch,u8 times);
//得到某个通道的采样平均值
#endif
adc,c
#include "adc.h"
#include "delay.h"
void Adc_Init(void)
{
//初始化IO口
RCC->APB2ENR|=1<<2;
//使能PORTA口时钟
GPIOA->CRL&=0X0FFFFFFF;
//PA7 输入
RCC->APB2ENR|=1<<9;
//ADC1时钟使能
RCC->APB2RSTR|=1<<9;
//ADC1复位
RCC->APB2RSTR&=~(1<<9);
//复位结束
RCC->CFGR&=~(3<<14);
//分频因子清零
//SYSCLK/DIV2=12M ADC时钟设置为12M
//ADC最大时钟不能超过14M!
//否则将导致ADC准确度下降
RCC->CFGR|=2<<14;
ADC1->CR1&=0XF0FFFF;
//工作模式清零
ADC1->CR1|=0<<16;
//独立工作模式
ADC1->CR1&=~(1<<8);
//非扫描模式
ADC1->CR2&=~(1<<1);
//单次转换模式
ADC1->CR2&=~(7<<17);
ADC1->CR2|=7<<17;
//软件控制转换
ADC1->CR2|=1<<20;
//使用用外部触发(SWSTART)!!必须使用 一个事件触发
ADC1->CR2&=~(1<<11);
//右对齐
ADC1->CR2 |= 1<<23;
//使能温度传感器
ADC1->SQR1&=~(0XF<<20);
//1个转换在规则序列中,也就是只转换规则序列1
ADC1->SQR1|=0<<20;
//设置通道1的采样时间
ADC1->SMPR2&=~(3*1);
//通过1采样时间清空
ADC1->SMPR2|=7<<(3*1);
//通道1 239.5周期,提高采样时间可以提高精确度
ADC1->SMPR1&=~(7<<3*6);
//清除通道16原来的设置
ADC1->SMPR1|=7<<(3*6);
//通道16 239.5周期,提高采样时间可以提高精确度
ADC1->CR2|=1<<0;
//开启AD转化器
ADC1->CR2|=1<<3;
//使能复位校准
while(ADC1->CR2&1<<3);
//等待校准结束
ADC1->CR2|=1<<2;
//开启AD校准
while(ADC1->CR2&1<<2);
//等待校准结束
//该位由软件设置以开始校准,并在校准结束时由硬件清除
}
//获得ADC1某个通道的值
u16 Get_Adc(u8 ch)
{
//设置转换序列
ADC1->SQR3&=0XFFFFFFE0;
//规则序列1 通道ch
ADC1->SQR3|=ch;
ADC1->CR2|=1<<22;
//启动规则转换通道
while(!(ADC1->SR&1<<1));
//等待转换结束
return ADC1->DR;
//返回adc值
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
short Get_Temperature(void)
{
u32 adcx;
short result;
double temperature;
adcx = Get_Adc_Average(ADC_CH_TEMP,20);
temperature = (float)adcx*(3.3/4096);
temperature = (1.43 - temperature)/0.0043 + 25;
result = temperature*=100;
return result;
}