14:(标准库)ADC一:ADC模数转换器

1、ADC的简介

1.1、ADC测量电压原理

在这里插入图片描述
在这里插入图片描述
  与其说ADC是模数转换器,不如说ADC是一个采样器+数据比较器。如上图:ADC的某个采样引脚去采样模拟信号的某个时间点的数据(3.3v),然后通过电容的充放电将3.3v的数据输入到数据比较器里面。然后ADC不断的向结果寄存器里面填加数据(二进制数),然后数据通过电压发生器(DAC(数模转换器))输入到数据比较器里面。在比较器里面对比进行如下操作:
  ①若ADC的结果寄存器里面的数据 < 模拟信号,则增加ADC的结果寄存器里面的数据。
  ②若ADC的结果寄存器里面的数据 > 模拟信号,则减小ADC的结果寄存器里面的数据。
  ③若ADC的结果寄存器里面的数据 = 模拟信号,则代表成功测量出模拟信号

【注】增加ADC的数值是从高位依次往低位增加。这就是逐次逼近ADC

1.2、电压分辨率

  综上:结果寄存器里面的数据 < 模拟信号,则增加ADC的结果寄存器里面的数据。那结果寄存器里面数值+1(电压分辨率),电压发生器输入到比较器里面的电压加多少喃?

答案:这与ADC的参考电压和结果寄存器的位数有关。
例如:若参考电压为3.3v,结果寄存器为2位。那结果寄存器里面数值+1(电压分辨率)是多少喃?如下图所示:
在这里插入图片描述
结果寄存器为4位/8位。那结果寄存器里面数值+1(电压分辨率)是多少喃?如下图所示:

在这里插入图片描述
总结:若结果寄存器的位数越多,则分辨率越小。则测量到的结果就越准确。而在STM32F103C8T6这款芯片中ADC的参考电压为VREF+~VREF-。因为VREF连接到VDD供电引脚,所以VREF = 3.3V。结果寄存器的位数为12位。而有些单片机的VREF没有连接到供电引脚VDD,可以通过外部供电来提供参考电压。

1.3、采样转换演示

   我们先来了解到ADC的工作原理。如图:当闭合采样开关,模拟信号输入,对电容充电。当充满电后,断开采样开关,比较器通过比较来给结果寄存器增加数据/减少数据,直到和模拟信号的值相差到规定的数值以内,然后将数据保存。

在这里插入图片描述
  如图:采样到的模拟信号为2.21v,参考电压为3.3v,结果寄存器为4位,则电压分辨率为0.22v。
  ①将结果寄存器的高位置1,进入比较器比进行比较:8 * 0.22 = 1.76 < 2.21v,保留寄存器中最高位数据1。
  ②将结果寄存器次高位置1,进入比较器比进行比较:8 * 0.22 + 4 * 0.22 = 2.64 > 2.21v,清除寄存器中次高位数据1。此位置0
  ③将结果寄存器次后一位置1,进入比较器比进行比较:8 * 0.22 + 2 * 0.22 = 2.20 < 2.21v,则保留寄存器中此位数据1。
  ④将结果寄存器次最后位置1,进入比较器比进行比较:8 * 0.22 + 2 * 0.22 + 1 * 0.22 = 2.42 > 2.21v,清除寄存器中此位数据1。此位置0

综上:最终得出结果寄存器的数据最后为1010。由于寄存器的位数不够大,导致测量的精度不准。

2、采样时间和转换时间

   采样时间:就是对电容充满电所需要的时间
   转换时间:就是通过比较,确定结果寄存器里面的数值的时间。
   STM32F103C8的ADC挂载在APB2总线上,而ADC的特性规定,ADC的时钟频率<14MHz。所以APB2时钟频率信号输出进入ADC之前要进行分频处理。

在这里插入图片描述
在这里插入图片描述

  • 转换时间 = 12.5的时钟周期。
    在这里插入图片描述
    将结果寄存器里面置1/置0进行输出比较所需的时间为1个时钟周期。而结果寄存器是12位,所以需要12个时钟周期。外加0.5个时钟周期进行等待数据的处理完毕。

  • 采样时间 = 电容充电的时间
    电容充电的时间由电路中的电阻和电容本身所决定的,电阻越大,电流越小,充电时间越长,采样时间越长。即电容充满电时间格式如下图所示:
    当电阻为400Ω时,电容充满电所需时间 = 0.11us,当电阻为10kΩ时,电容充满电所需时间 = 0.85us
    在这里插入图片描述
    而等待电容充电的时间越长,则采样到数据就越精确,误差就越小。若等待的时间超过电容充满电的时间,那么采样到的数据就是模拟电压。
    在这里插入图片描述但是ADC的精度本来就是有限的,所以只要采样的误差<1/4*ADC分辨率即可。
    在这里插入图片描述
    若ADC的时钟源频率为14MHz,则将采样时钟转换为时间周期如下图所示:
    电阻为400Ω时:采样时间t = 0.11us,而时间T = 1s/(14MHz) = 1/14(us),则t = 1.54T,即采样时间是1.54个时钟周期
    在这里插入图片描述但是在编程中采样时间被规定了8个挡位的,所以我们只能选择和采样时间相近的那个挡位。挡位>采样时间,代表等待电容充电时间>电容充满电的时间,即数据越精确。挡位<采样时间,代表等待电容充电时间<电容充满电的时间,数据不精确,都是采样时间变快

问:最理想的情况下ADC每砂最多执行多少次转换
答:最理想的情况是:采样的电阻为0Ω,ADC工作频率为14MHz。采样电阻为0,则采样时间t = 0.0776us = 1.0864T,即为1.0864个时钟周期,而挡位最接近的为1.5个时钟周期。所以总花费的时间 = 1.5T+12.5T = 14T = 14 * 1s/(14MHz) = 1us。所以:所以:理想情况下1us采样转换1次数据。而1s则采样转换10M次数据。
在这里插入图片描述

3、STM32中ADC模块

   STM32F103C8T6单片机中有2个ADC模块:ADC1、ADC2,结果寄存器都为12位,参考电压为3.3v,也就是将3.3v的电压分为了4095份。且每个ADC片上外设都有10外部测量通道和2个内部信号源。我们可以通过外部通道引脚(PA0~PA7,PB0,PB1)测量外部模块的电压
在这里插入图片描述在这里插入图片描述

这么多的通道是怎样管理的喃?
答:通过规则组(常规序列)和注入组(注入序列)进行管理

在这里插入图片描述

+如上图所示:规则组和注入组像是一个盒子(用来存放需要测量的引脚)。
①注入组:注入组里面最多能放4个采集转换通道(4个序列盒子),给一个触发信号,ADC就开始采集转换,将结果保存到对应的4个结构寄存器里面。
②规则组:规则组里面最多能放入16个采集转换通道(16个序列盒子),给触发控制一个触发信号,ADC就按照规则组里面序列盒子的存放通道顺序进行采集转换。而存放规则组采集转换的数据结果寄存器只有1个。所以使用规则组时要及时将第一个通道测量到的数据进行处理,否则第二个通道的数据会将其覆盖。所以常常搭配DMA进行使用
③ECO:为转换完成标志位,可以通过此为进行中断触发请求。
④模拟看门狗:可以监测结果寄存器里面的数值,设定一个数据阈值,当监测到结果寄存器里面的值大于阈值时,也可以触发一个中断请求。
【注意】注入组给优先级高于规则组,当有注入组里面有通道进行测量,则ADC会停止规则组的测量,去进行注入组的测量

③触发控制:让ADC开始采集转换的信号,如下图所示,有定时器启动和软件启动。而软件启动就是就是给寄存器置1
在这里插入图片描述

在这里插入图片描述

4、转换模式

规则组的转换模式:

4.1、单次转换非扫描模式

   单次:给一个触发信号,测量转换一次,测量转换完成后,停止工作。
   非扫描:序列1盒子里面有测量通道,其他的序列盒子没有。
在这里插入图片描述
在此模式下,规则组只有第一个盒子里面的通道才有效(即序列1),我们可以在序列1的盒子里面放入我们需要转换的通道,然后给规则组一个触发信号,开始采集转换,转换完成后数据保存在数据寄存器里面,同时ECO标志位置1。需要进行第二次转换,那么需要在启动一次触发信号,开启转换。

4.2、连续转换非扫描模式

   连续:给一个触发信号,ADC就会一直进行测量转换,第一次完成后,开始进行第二次测量转换,一直循环下去。
在这里插入图片描述
和单次转换非扫描模式不同的是,给规则组一个触发信号,在第一次转换完成后立马进入第二次转换,不断的进行下去。单片机需要获取数据,只需要读取数据寄存器里面的数据即可

4.3、单次转换扫描模式

   扫描模式:除了序列1盒子里面有测量通道,其他的盒子里面也有。
在这里插入图片描述
在规则组里面的多个序列盒子里面放入我们需要转换的通道,然后告诉ADC有多少个盒子被使用。然后给规则组一个触发信号,ADC就按照盒子序列的顺序开始转换,序列1的通道转换完成后数据放入结果寄存器里面,然后开始序列2的通道转换。当全部的通道转换完成时ECO标志位置1,需要第二次转换则需要启动触发信号。
【注意】一般情况下扫描模式搭配DMA使用,这样才会避免数据的覆盖丢失。

4.4、连续转换扫描模式

在这里插入图片描述

数据对齐:
   ADC转换后的数据是12位的,而数据寄存器是16位的,所以数据寄存器有4个空出来的位置补零。一般的情况下使用右对齐即可
在这里插入图片描述

数据校准
   校准都是固定的,只需要在初始化后加上校准代码即可,不用理解。
在这里插入图片描述

5、编程案列

与之相关的标准库编程接口:
在这里插入图片描述

5.1、AD单通道连续转换

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

/*
	ADC单通道实验,通过通道1(PA0)对自身输出电压进行采集,通过电位器改变电压变化,最终通过串口打印出来
*/

#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_0, 1, ADC_SampleTime_55Cycles5); //ADC1,ADC通道0(对应PA0)
    																			//盒子序列1,采样时间为55.5周期
    																			//此步就是将通道0放入盒子序列1中
    ADC_Cmd(ADC1, ENABLE);                                                      //使能ADC1

    /* 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 "stm32f10x.h"                 
#include "Delay.h"
#include "UART.h"
#include "ADC.h"

int main(void)
{
    uint16_t Data = 0;
    float ADC_Value = 0;
    UART1_Init();
    ADC1_Init();
    printf("ADC电压测量\r\n");
    
	while(1)
	{
        Data = Get_ADCResult();
        printf("结果寄存器数值为:%d\r\n",Data);
        ADC_Value = Data * 3.3 / 4095;              //将二进制计数为电压电压值
        printf("测量到的电压为:%0.2f\r\n",ADC_Value);
        Delay_ms(1000);
	}
}

5.2、AD单通道模拟多通道

   我们还没有学习DMA,如果不使用DMA对多通道转换的数据进行挪动的话,那么会出现数据的覆盖我们通过单次转换非扫描模式模拟单次扫描模式。就是在第一次转换完成后,在获取数据的同时将第1序列的盒子里面的通道号通过手动修改。
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);
    
    /* 2.对通道0(PA0),通道1(PA1),通道2(PA2),通道3(PA3)进行配置 */
	GPIO_InitTypeDef GPIOInitStruct;
	GPIOInitStruct.GPIO_Mode = GPIO_Mode_AIN;//模拟输入模式,ADC的专属模式
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
	GPIO_Init(GPIOA,&GPIOInitStruct);

    /* 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_ContinuousConvMode = DISABLE;                     //非连续模式
    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_0, 1, ADC_SampleTime_55Cycles5); //ADC1,ADC通道,盒子序列1,采样时间为55.5周期
    ADC_Cmd(ADC1, ENABLE);                                                      //使能ADC1

    /* 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(uint8_t ADC_Channel)
{
    ADC_RegularChannelConfig(ADC1, ADC_Channel, 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位的二进制数
}

主程序文件的代码如下:

/*
	ADC单通道模拟多通道的简单使用
*/

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

int main(void)
{
    uint16_t Data0,Data1,Data2,Data3;
    float ADC_Value = 0;
    UART1_Init();
    ADC1_Init();
    printf("ADC电压测量\r\n");
    
	while(1)
	{
        /* 获取通道的测量值 */
        Data0 = Get_ADCResult(ADC_Channel_0);//修改通道并获取数据
        Data1 = Get_ADCResult(ADC_Channel_1);
        Data2 = Get_ADCResult(ADC_Channel_2);
        Data3 = Get_ADCResult(ADC_Channel_3);
        
        /* 将测量值计数处理 */
        
        printf("通道0的结果寄存器数值为:%d\r\n",Data0);
        ADC_Value = Data0 * 3.3 / 4095;                //将二进制计数为电压电压值
        printf("测量到的电压为:%0.2f\r\n",ADC_Value);
        Delay_ms(100);
        
        printf("通道1的结果寄存器数值为:%d\r\n",Data1);
        ADC_Value = Data1 * 3.3 / 4095;                //将二进制计数为电压电压值
        printf("测量到的电压为:%0.2f\r\n",ADC_Value);
        Delay_ms(100);
        
        printf("通道2的结果寄存器数值为:%d\r\n",Data2);
        ADC_Value = Data2 * 3.3 / 4095;                //将二进制计数为电压电压值
        printf("测量到的电压为:%0.2f\r\n",ADC_Value);
        Delay_ms(100);
        
        printf("通道3的结果寄存器数值为:%d\r\n",Data3);
        ADC_Value = Data3 * 3.3 / 4095;                //将二进制计数为电压电压值
        printf("测量到的电压为:%0.2f\r\n",ADC_Value);
        Delay_ms(100);
	}
}

5.3、ADC单通道中断

   规则组和注入组转换结束时能产生中断,当模拟看门狗状态位被设置时也能产生中断。它们都有独立的中断使能位。
如图:规则组转换完成会将EOC置1,注入组转换完成会将JEOC置1。
在这里插入图片描述
【注意】ADC1和ADC2的中断映射在同一个中断向量上,而ADC3的中断有自己的中断向量。

上面的代码是通过阻塞式获取规则组转换完成的数据,接下来是通过中断的方式获取规则组转换完成的数据。
①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_0, 1, ADC_SampleTime_55Cycles5); //ADC1,ADC通道,盒子序列1,采样时间为55.5周期
    ADC_Cmd(ADC1, ENABLE);                                                      //使能ADC1
    
    /* 5、使能EOC中断,NVIC的配置 */
    ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = ADC1_2_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    
    /* 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位的二进制数
//}

/**
 * ADC单通道转换完成的中断范围函数
 */
uint16_t Data = 0;
void ADC1_2_IRQHandler (void)
{
    if(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == SET)//转换完成标志位EOC置1
    {
        ADC_ClearFlag(ADC1, ADC_FLAG_EOC);//清除标志位EOC
        Data = ADC_GetConversionValue(ADC1);//获取结果寄存器中的数据
    }
}

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

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

int main(void)
{
    float ADC_Value = 0;
    UART1_Init();
    ADC1_Init();
    printf("ADC电压测量\r\n");
    
	while(1)
	{
        printf("结果寄存器数值为:%d\r\n",Data);
        ADC_Value = Data * 3.3 / 4095;              //将二进制计数为电压电压值
        printf("测量到的电压为:%0.2f\r\n",ADC_Value);
        Delay_ms(1000);
	}
}

【注意】使用的是单通道连续转换模式,即给个触发信号后会不断的进行转换下去,完成一次转换就会进入一次中断。这样频繁的进入ADC中断可能会影响主函数的运行。所以可以通过定时器中断来解决这个问题。定一段时间,在定时中断里面使能触发信号(ADC配置为非连续模式),ADC开始转换,当转换完成后进入ADC中断,在ADC中断获取数据。注:ADC的中断优先级配置要大于定时器中断优先级。

### STM32 ADC 使用标准库 示例代码及教程 #### 初始化与配置 为了使用STM32ADC功能,首先需要初始化ADC模块。这涉及到设置ADC的工作模式、分辨率以及选择用于触发转换的方式。 ```c // 定义ADC结构体并初始化参数 ADC_InitTypeDef ADC_InitStructure; // RCC使能ADC时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // 配置ADC工作于连续扫描模式,右对齐数据,单次转换模式关闭扫描 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); ``` 上述代码展示了如何通过`ADC_InitTypeDef`来定义和初始化ADC的具体属性[^2]。 #### 启动转换 旦完成了初始化操作之后,则可以通过调用相应的API接口开启次性的模拟量到数字量之间的变换流程: ```c // 开启指定通道 ADC_Cmd(ADC1, ENABLE); // 转换前校准 ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); // 添加待测通道至序列里边去 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); // 执行AD采样动作 ADC_SoftwareStartConvCmd(ADC1, ENABLE); ``` 此部分实现了对于特定输入端口的选择及其对应的采样时间设定,并最终执行软件控制下的转换命令。 #### 获取结果 当完成了轮完整的A/D转换周期以后就可以读取得到的结果值了: ```c uint16_t result; result = ADC_GetConversionValue(ADC1); ``` 这段简单的语句即可以实现从外设寄存器中取出最近次有效测量所得的数据。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值