15:(标准库)ADC二:ADC内部通道的使用

1、内部温度的测量

   已知STM32F103C8有10个外部通道和2个内部通道,10个外部通道连接着外部引脚。而2个内部通道连接着特定功能的内部模块。如下图所示:通道16连接着温度传感,所以可以通过通道16来测量单片机内部的温度。通道17连接着一个1.2v恒定不变的电压,可以通过通道17测量出实际的参考电压。

在这里插入图片描述

  温度传感器在内部和ADC1_IN16输入通道相连接,此通道把传感器输出的电压转换成数字值。温度传感器模拟输入推荐采样时间是17.1μs,若ADC的工作频率为12MHz,则时钟周期T = (1/12)(us),则17.1us = 17.1 * 12(T) = 205.2T,所以采样时间t = 205.2个时钟周期。因此在程序设置中采样时间应 > 205.2T。这样采样到的温度才较为准确。
在这里插入图片描述
在这里插入图片描述①ADC.c文件的代码如下:

#include "ADC.h"

/**
 * ADC1初始化函数
 */
void ADC1_Init(void)
{
    /* 1、使能GPIO和ADC时钟 */
    // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 	//使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  		//使能ADC1时钟
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);							//对ADC时钟源分频

    // /* 2、对PA0(ADC1的通道0引脚)进行引脚配置 */
    // GPIO_InitTypeDef GPIO_InitStructure;
    // GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;     //PA0 作为模拟通道输入引脚
    // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
    // GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* 3、配置ADC1工作模式 */
    ADC_InitTypeDef ADC_InitStructure;
    ADC_DeInit(ADC1);                                                       //将外设ADC1的全部寄存器重设为缺省值
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;                      //ADC1和ADC2工作在独立模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;                           //非扫描(单通道)模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                      //连续模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;     //转换由软件而不是外部触发启动
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                  //ADC1数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 1;                                 //转换的ADC通道的数目,只有PA0
    ADC_Init(ADC1, &ADC_InitStructure);                                     //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
    
    /* 4、配置规则组*/
    ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_239Cycles5); //ADC1,ADC通道,盒子序列1,
    																			  //采样时间为239.5周期 > 205.2T
    																			  //此步是将通道16放入序列1的盒子里面
    ADC_Cmd(ADC1, ENABLE);                                                        //使能ADC1

    ADC_TempSensorVrefintCmd(ENABLE);               //唤醒关电模式下的温度传感器,此步非常重要,温度传感器唤醒才会工作
    
    /* 5、ADC校准 */
    ADC_ResetCalibration(ADC1);                     //使能复位校准
    while (ADC_GetResetCalibrationStatus(ADC1));    //等待复位校准结束
    ADC_StartCalibration(ADC1);                     //开启AD校准
    while (ADC_GetCalibrationStatus(ADC1));         //等待校准结束
    
    /* 6、软件触发 */
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);         //启动ADC转换
}

/**
 * 获取ADC中的结果寄存器的值
 */
uint16_t Get_ADCResult(void)
{
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);  //等待转换完成标志位置1
    return ADC_GetConversionValue(ADC1);                    //获取ADCx->DR里面的数据,ECO自动清除
                                                            //【注意】返回的数据是12位的二进制数
}

②主函数main.c文件的代码如下:

#include "UART.h"
#include "ADC.h"

int main(void)
{
    uint16_t Data = 0;
    float ADC_VSENSE = 0;
    float Temp = 0;
    UART1_Init();
    ADC1_Init();
    printf("ADC温度测量\r\n");
    
	while(1)
	{
        Data = Get_ADCResult();
        printf("结果寄存器数值为:%d\r\n",Data);
        
        ADC_VSENSE = Data * 3.3 / 4095;              //将二进制计数为电压电压值
        printf("内部电压为:%0.2fV\r\n",ADC_VSENSE);
        
        Temp = (1.43 - ADC_VSENSE)/0.0043 + 25;      //将电压转换为温度
        printf("内部温度为:%0.2f度\r\n",Temp);
        Delay_ms(1000);
	}
}

在这里插入图片描述

2、参考的电压的校准

   内部通道17是用于确定参考电压的实际数值。在之前的代码中参考电压都是使用的是3.3v,因为参考电压引脚VREF连接着内部供电电压VDDA,而VDDA连接着VDD,而VDD是3.3v。但是在实际的使用中,VDDA的会随着使用的片上外设增加而减小,所以参考电压也会改变,而不一直是3.3v。
   可以通过内部通道17测量出实际的参考电压的值。内部通道17连接着内部参照电压VREFINT(1.2v),VREFINT(1.2v)是恒定不变的,不会随着单片机的使用而改变。所以可以通过以下公式得出实际参考电压VREF的值。
            在这里插入图片描述

①ADC.c文件的代码如下:

#include "ADC.h"

/**
 * ADC1初始化函数
 */
void ADC1_Init(void)
{
    /* 1、使能GPIO和ADC时钟 */
    // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 	//使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  		//使能ADC1时钟
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);							//对ADC时钟源分频
    
    /* 使用内部通道,无需引脚配置 */
    // /* 2、对PA0(ADC1的通道0引脚)进行引脚配置 */
    // GPIO_InitTypeDef GPIO_InitStructure;
    // GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;     //PA0 作为模拟通道输入引脚
    // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
    // GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* 3、配置ADC1工作模式 */
    ADC_InitTypeDef ADC_InitStructure;
    ADC_DeInit(ADC1);                                                       //将外设ADC1的全部寄存器重设为缺省值
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;                      //ADC1和ADC2工作在独立模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;                           //非扫描(单通道)模式
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;                     //非连续模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;     //转换由软件而不是外部触发启动
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                  //ADC1数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 1;                                 //转换的ADC通道的数目
    ADC_Init(ADC1, &ADC_InitStructure);                                     //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
    
    ADC_Cmd(ADC1, ENABLE);                                                  //使能ADC1

    /* 5、ADC校准 */
    ADC_ResetCalibration(ADC1);                     //使能复位校准
    while (ADC_GetResetCalibrationStatus(ADC1));    //等待复位校准结束
    ADC_StartCalibration(ADC1);                     //开启AD校准
    while (ADC_GetCalibrationStatus(ADC1));         //等待校准结束 
}

/**
 * 获取ADC中的结果寄存器的值
 */
uint16_t Get_ADCResult(void)
{
    /* 配置规则组 */
    ADC_RegularChannelConfig(ADC1, ADC_Channel_17, 1, ADC_SampleTime_55Cycles5); //ADC1,ADC通道,盒子序列1,采样时间为55.5周期
    /* 软件触发 */
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);                 //启动ADC转换
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);  //等待转换完成标志位置1
    return ADC_GetConversionValue(ADC1);                    //获取ADCx->DR里面的数据,ECO自动清除
                                                            //【注意】返回的数据是12位的二进制数
}

②主函数main.c文件的代码如下:

#include "stm32f10x.h"                 
#include "Delay.h"
#include "UART.h"
#include "ADC.h"

int main(void)
{
    uint16_t Data = 0;
    float ADC_VREF = 0;
    UART1_Init();
    ADC1_Init();
    printf("ADC实际参考电压\r\n");
    
	while(1)
	{
        Data = Get_ADCResult();
        printf("结果寄存器数值为:%d\r\n",Data);
        
        ADC_VREF = 1.2 * 4095 / Data;                //求出参考电压实际值
        printf("参考电压实际值:%0.2f\r\n",ADC_VREF);
        Delay_ms(1000);
	}
}

在这里插入图片描述
如图所示:此时的参考电压是3.18v,而不是3.3v。

3、使用校准后的参考电压测量内部温度值

①ADC.c文件的代码如下:

#include "ADC.h"

/**
 * ADC1初始化函数
 */
void ADC1_Init(void)
{
    /* 1、使能GPIO和ADC时钟 */
    // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 	//使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  		//使能ADC1时钟
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);							//对ADC时钟源分频
    
    /* 使用内部通道,无需引脚配置 */
    // /* 2、对PA0(ADC1的通道0引脚)进行引脚配置 */
    // GPIO_InitTypeDef GPIO_InitStructure;
    // GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;     //PA0 作为模拟通道输入引脚
    // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
    // GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* 3、配置ADC1工作模式 */
    ADC_InitTypeDef ADC_InitStructure;
    ADC_DeInit(ADC1);                                                       //将外设ADC1的全部寄存器重设为缺省值
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;                      //ADC1和ADC2工作在独立模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;                           //非扫描(单通道)模式
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;                     //非连续模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;     //转换由软件而不是外部触发启动
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                  //ADC1数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 1;                                 //转换的ADC通道的数目
    ADC_Init(ADC1, &ADC_InitStructure);                                     //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
       
    ADC_Cmd(ADC1, ENABLE);                                                  //使能ADC1
    
    ADC_TempSensorVrefintCmd(ENABLE);               //唤醒关电模式下的温度传感器,这一步很重要,唤醒传感器才会工作。 

    /* 5、ADC校准 */
    ADC_ResetCalibration(ADC1);                     //使能复位校准
    while (ADC_GetResetCalibrationStatus(ADC1));    //等待复位校准结束
    ADC_StartCalibration(ADC1);                     //开启AD校准
    while (ADC_GetCalibrationStatus(ADC1));         //等待校准结束 
}

/**
 * 获取ADC中的结果寄存器的值
 */
uint16_t Get_ADCResult(uint8_t ADC_Channel)
{
    /* 配置规则组 */
    ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_239Cycles5); //ADC1,ADC通道,盒子序列1,采样时间为239.5周期
    /* 软件触发 */
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);                 //启动ADC转换
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);  //等待转换完成标志位置1
    return ADC_GetConversionValue(ADC1);                    //获取ADCx->DR里面的数据,ECO自动清除
                                                            //【注意】返回的数据是12位的二进制数
}

②主函数main.c文件的代码如下:

#include "stm32f10x.h"                 
#include "Delay.h"
#include "UART.h"
#include "ADC.h"

int main(void)
{
    uint16_t Data = 0;
    float ADC_VREF = 0;
    float ADC_VSENSE  = 0;
    float Temp = 0;
    UART1_Init();
    ADC1_Init();
    
	while(1)
	{
        Data = Get_ADCResult(ADC_Channel_17);
        ADC_VREF = 1.2 * 4095 / Data;                //求出参考电压实际值
        printf("参考电压实际值:%0.2fv\r\n",ADC_VREF);
        
        Data = Get_ADCResult(ADC_Channel_16);
        ADC_VSENSE = ADC_VREF / 4095 * Data;         //使用校准的参考电压求温度传感器电压
        printf("内部温度电压为:%0.2fv\r\n",ADC_VSENSE);
        
        Temp = (1.43 - ADC_VSENSE)/0.0043 + 25;      //将电压转换为温度
        printf("内部温度为:%0.2f度\r\n",Temp);
        Delay_ms(1000);
	}
}

在这里插入图片描述

综上:若没有使用校准的参考电压时:即3.3v,测量出的温度传感器的内部电压为1.45v,计算出的温度为21度。而使用了校准的参考电压,即3.25v,测量出的温度传感器的内部电压为1.43v,算出的温度为25度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值