STM32F103 内部温度传感器实验

实验目的

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;
	
	
	
}
	 










  • 10
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值