STM32 ADC数值采样及热敏传感器使用指南

目录

一丶ADC介绍

二丶ADC工作原理及管脚分布

三丶代码部分详解

(一)库函数介绍

(二)代码部分整合


一丶ADC介绍

        ADC模块中文名为模拟/数字转换器,是12位逐次逼近型的模拟数字转换器,一般用于数值的采样  可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。学习过stm32后我们知道,stm32是数字电路,。数字电路没有多少伏,多少度的概念,而通常的传感器模块,输出的都是模拟量。

比如我要使用热敏传感器测量温度,那么需要将传感器模块的模拟量,转换成STM32可以“看懂的数字量”,所以想要读取温度的数值,就需要用到ADC模数转换器来实现了,实现过程简单来说就是ADC读取引脚上的电压,通过转换,储存到DR寄存器里。(本次使用的是STM32C8T6)

二丶ADC工作原理及管脚分布

AD转换原理如图

        

 (1)ADC的输入通道,包括IN0-IN15,共16GPIO口,和两个内部通道,内部温度传感器,内部参考电压

(2)共18个输入通道,将数据传输到数模转换器,模拟多路开关可选择我们想要的通道

(3)数模转换器使用逐次逼近法将模拟量转换为数字量 

(4)转换后分为两个组:规则组和注入组,存放准换结果。

        规则组:可以传输16位数据,但规则组寄存器只有一位,所以如果转运不及时会出现数据覆盖的问题,前面的数据还没有读,后面的数据就上来了,这时候就需要用到DMA,DMA可以将ADC寄存器的数据暂时挪到DMA寄存器中,需要的时候就会被取出(本次代码不涉及DMA,如需了解可看下篇文章)本次代码使用的是规则组

     注入组:注入组一次可以传输4位数据,则不需要考虑数据覆盖的问题

(5)我们知道,ADC可以由软件触发,也可以由硬件触发,软件触发顾名思义,在程序中手动调动代码即可实现。而硬件触发,就是框图内所包含的触发源。靠上的是规则组,靠下的是注入组

(6)EOC是规则组准换完成信号,JEOC是注入组转换完成信号


总结如图

三丶代码部分详解

(一)库函数介绍

        鉴于ADC库中函数繁多,本次只介绍现象代码会使用到的部分和少量拓展


1丶复位函数ADC_DeInit

void ADC_DeInit(ADC_TypeDef* ADCx);

该函数作用为将外设ADCx的全部寄存器重设为缺省值,说的简单点就是复位,恢复“出厂设置

2丶初始化函数ADC_Init

工作模式:本次采用单ADC模式

数据对其方式:ADC转换是12位,故存储在寄存器中是12位的数据,但数据寄存器是16位的,存储时空位补零,所以就出现了数据对其问题。一半来说,我们都是采用右对齐的模式。那么左对齐有什么用呢,加入你只想读一个大概的数据,不想要这么高的分辨率,就可以使用左对齐,取前八位,就舍弃了后面的数据,减小了分辨率。

ADC触发方式:选择内部软件触发

转换模式:单次转换模式为转换一次给到寄存器数据后停止。连续转换模式转换一次后开始下一轮转换,一直持续下去,不需要手动开始下一次转换

扫描模式:多通道进行

通道数选择:只选用一个通道

    ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;    //独立工作模式,即单ADC模式
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  //内部软件触发
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   //单次转换模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;         //非扫描模式
	ADC_InitStructure.ADC_NbrOfChannel = 1;               //只使用一个通道
	ADC_Init(ADC1, &ADC_InitStructure);                   

3丶 ADC_Cmd使能ADC

void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);

4丶 ADC_DMACmd是否使用DMA

void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);

5丶ADC_ITConfig是否使用中断

void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);

6丶根据手册规定,需要进行ADC校准

void ADC_ResetCalibration(ADC_TypeDef* ADCx);    //复位校准
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);     //复位校准状态
void ADC_StartCalibration(ADC_TypeDef* ADCx);      //开始校准
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);     //校准状态

7丶ADC_SoftwareStartConvCmd软件触发ADC

void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

8丶时钟分频

手册规定ADC最大14mhz,所以选择6分频或8分频

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

结合上文总结一下ADC初始化流程

(1)开启APB2时钟和ADC时钟

(2)ADC时钟分频,选择六分频或八分频

(3)初始化GPIO  注:这里GPIO模式要使用GPIO_Mode_AIN,ADC专用模式

(4)ADC及ADC通道选择

(5)初始化ADC

(6)使能ADC,给ADC上电

(7)校准ADC

(二)丶代码部分整合

ADC部分

#include "stm32f10x.h"                  // Device header

void AD_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
	
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;
	ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;
	ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;
	ADC_InitStructure.ADC_NbrOfChannel=1;
	ADC_InitStructure.ADC_ScanConvMode=DISABLE;
	ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
	ADC_Init(ADC1,&ADC_InitStructure);
	
	ADC_Cmd(ADC1,ENABLE);
		
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1)==SET);
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1)==SET);
		


	
}

uint16_t AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);
	
	return ADC_GetConversionValue(ADC1);

}

主函数部分

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "ADC.h"
#include "LED.h"

uint16_t Temp;
#define T25 298.15    
#define B   3380
float  Vlue;

double myLn(double a)
{
	int  N = 15;
	int k=0,nk=0;
	double x=0.0,xx=0.0,y=0.0;
	x=(a-1)/(a+1);
	xx = x*x;
	nk = 2*N+1;
	y = 1.0/nk;
	
	for(k = N;k>0;k--)
	{
		nk = nk -2;
		y = 1.0/nk+xx*y;
	}
	return 2.0*x*y;
}

float Get_Temperaturn(void )
{
	float r_f = 0.0,temp_f = 0.0;
	
	Temp = AD_GetValue();
	Vlue = (float)Temp/4095*3.3;
	
	r_f = (Vlue*10000)/(3.3-Vlue); 
	temp_f = 1/((myLn(r_f/10000))/B + 1/T25 ) - 273.15; 	
	return temp_f;
}

int main(void)
{
	OLED_Init();
	AD_Init();
	OLED_ShowString(1, 1, "Temperature:");
	LED1_ON();
	while (1)
	{
		Temp = AD_GetValue();
		
		OLED_ShowNum(2, 3, Get_Temperaturn(), 2);
		OLED_ShowString(2,6,"C");

		
		Delay_ms(100);
		
		if(Get_Temperaturn()>28)
		{
			OLED_ShowString(3,1,"warn");
		
		}
			if(Get_Temperaturn()<28)
		{
		
			OLED_ShowString(3,1,"safe");
		}
	}
}

关于温度的转换

Rt = R 乘 EXP(B 乘 (1/T1-1/T2))
对上面的公式解释如下:

Rt 是热敏电阻在T1温度下的阻值;
R是热敏电阻在T2常温下的标称阻值;
B值是热敏电阻的重要参数;
EXP是e的n次方;
这里T1和T2指的是K度即开尔文温度,K度=273.15(绝对温度)+摄氏度;

根据串联分压,知道总电压VCC 3.3v,热敏电阻的电压V2是adc采集后经过转换得到的,也是已知,
所以R1的电压就是VCC – V2 ,然后根据R1电阻10K,可以求得电路的电流 I ,所以热敏电阻的
电阻 就可以用电流电压比值,于是得到Rt。
参数R 和 B值都是热敏电阻的参数,根据自己买的器件决定,我的就是10k,3380。可以问卖家,
也可以自己网上查型号。
这里还要注意,T2的单位是开尔文,所以室温25摄氏度的开尔文是273.15+25=298.15.
就只剩下T1是未知数,一元一次方程,带进去一算就欧克。

AD视频

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 ADC数值采样及热敏传感器使用指南

发表评论