ADC

ADC(Analog-to-Digital Converter) 指模数转换器。是指将连续变化的模拟信号转换为离散的数字信号的器件。

STM32拥有 1~3 个 ADC(STM32F101/102 系列只有 1 个 ADC),这些 ADC 可以独立使用,也可以使用双重模式(提高采样率)。STM32 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。

STM32F103 系列最少都拥有 2 个 ADC,我们选择的 STM32F103ZET 包含有 3 个 ADC。STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样周期为 1.5 个 ADC 时钟下得到),不要让 ADC 的时钟超过 14M,否则将导致结果准确度下降。STM32 将 ADC 的转换分为 2 个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。

ADC相关参数说明

  • 分辨率:分辨率以二进制(或十进制)数的位数来表示,一般有8 位10 位12位、16位等,它说明模数转换器对输入信号的分辨能力,位数越多,表示分辨率越高,恢复模拟信号时会更精确。
  • 精度:精度表示 ADC 器件在所有的数值点上对应的模拟值和真实值之间的最大误差值,也就是输出数值偏离线性最大的距离。
  • 转换速率:转换速率是指A/D 转换器完成一次从模拟到数字的 AD 转换所需时间的倒数。例如,某 A/D 转换器的转换速率为 1MHz,则表示完成一次 AD 转换时间为 1 微秒。

1.1 STM32ADC介绍

STM32的ADC是12bit逐次逼近型模拟数字转换器,有多达18个通道,可测量16个外部和2个内部信号源,各通道的A/D转换可以单次、连续、扫描或间断模式执行,ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。

STM32 的 ADC 在单次转换模式下,只执行一次转换,该模式可以通过 ADC_CR2 寄存器的 ADON 位(只适用于规则通道)启动,也可以通过外部触发启动(适用于规则通道和注入通道),这是 CONT 位为 0。以规则通道为例,一旦所选择的通道转换完成,转换结果将被存在 ADC_DR 寄存器中,EOC(转换结束)标志将被置位,如果设置了 EOCIE,则会产生中断。然后 ADC 将停止,直到下次启动。模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。

  • ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。
  • ADC供电要求:2.4V到3.6V

  • ADC 输入范围为:VREF- ≤ VIN ≤ VREF+。由 VREF-、VREF+ 、VDDA 、VSSA、这四个外部引脚决定。

注意: 如果我们想让输入的电压范围变宽,去到可以测试负电压或者更高的正电压,我们可以在外部加一个电压调理电路,把需要转换的电压抬升或者降压到 0~3.3V,这样 ADC 就可以测量。

1.2 ADC框架

1.2.1 ADC引脚

对于stm32f103系列单片机来讲,能测量的转化的电压范围0-3.3V。

注:超出转换测量范围需要额外增加外围电路,使其电压范围纠正到0-3.3V内,然后再进行转换测量。

1.2.2 ADC输入通道

ADC有16个外部输入通道和2路内部通道(温度传感器、内部参考电压)。
16个通道对应两种转换组:规则组和注入组,经由规则通道和注入通道转换,转换后的数据写入对应的规则通道数据寄存器和注入通道数据寄存器*4

  • 规则组:由多达16个转换组成
  • `注入组``:由多达4个转换组成

一般情况下我们都使用的是规则通道进行转换,转化顺序和转化总数由ADC_SQRx寄存器进行设置;注入通道是在规则通道转换的时候强行插入的转换通道,转化顺序和转化总数由ADC_JSQR寄存器进行设置。

利用外部触发或通过设置ADC_CR2寄存器的ADON位,启动一组规则通道的转换后。如果在规则通道转换期间产生一外部注入触发,当前转换被复位,注入通道序列被以单次扫描方式进行转换,然后恢复上次被中断的规则组通道转换;如果在注入转换期间产生一规则事件,注入转换不会被中断,但是规则序列将在注入序列结束后被执行。

转换模式包括:单次转换模式、连续转换模式、间断模式和扫描模式 。

对齐方式:由于ADC为12bit精度,寄存器有效位16位,所有会涉及对齐方式:左对齐和右对齐。

DMA请求 :因为规则通道转换的值储存在一个仅有的数据寄存器中,所以当转换多个规则通道时需要使用DMA,这可以避免丢失已经存储在ADC_DR寄存器中的数据

只有在规则通道的转换结束时才产生DMA请求,并将转换的数据从ADC_DR寄存器传输到用户指定的目的地址。

1.2.3、ADC时钟

可编程的通道采样时间 :ADC使用若干个ADC_CLK周期对输入电压采样,采样周期数目可以通过ADC_SMPR1和ADC_SMPR2寄存器中的SMP[2:0]位更改;每个通道可以分别用不同的时间采样。

==ADC 控制寄存器(ADC_CR1 和 ADC_CR2)==

ADC_CR1 的 SCAN 位,该位用于设置扫描模式,由软件设置和清除,如果设置为 1,则使用扫描模式,如果为 0,则关闭扫描模式。在扫描模式下,由 ADC_SQRx 或 ADC_JSQRx 寄存器选中的通道被转换。如果设置了 EOCIE 或 JEOCIE,只在最后一个通道转换完毕后才会产生 EOC 或 JEOC 中断。

==ADC_CR2该寄存器==

该寄存器我们也只针对性的介绍一些位:ADON 位用于开关 AD 转换器。而 CONT 位用于设置是否进行连续转换,我们使用单次转换,所以 CONT 位必须为 0。CAL 和 RSTCAL 用于AD 校准。ALIGN 用于设置数据对齐,我们使用右对齐,该位设置为 0。EXTSEL[2:0]用于选择启动规则转换组转换的外部事件,详细的设置关系。

ADC 采样事件寄存器(ADC_SMPR1 和 ADC_SMPR2),这两个寄存器用于设置通道 0~17 的采样时间,每个通道占用 3 个位。

==ADC_SMPR2 的各位描述==

总转换时间如下计算:

TCONV = 采样时间+ 12.5个周期

例如:采样周期设置为1.5周期

TCONV = 1.5+12.5 = 14个周期

频率为14MHz时,转换一次的时间为1us

1.2.4 触发转换方式

转换可以由外部事件触发(例如定时器捕获,EXTI线)。如果设置了EXTTRIG控制位,则外部事

件就能够触发转换。EXTSEL[2:0]和JEXTSEL2:0]控制位允许应用程序选择8个可能的事件中的某一个,可以触发规则和注入组的采样。

1.2.5 ADC中断

规则和注入组转换结束时能产生中断,当模拟看门狗状态位被设置时也能产生中断。它们都有独立的中断使能位。

1.2.6 双ADC模式

在有2个或以上ADC模块的产品中,可以使用双ADC模式,在双ADC模式里,根据ADC1_CR1寄存器中DUALMOD[2:0]位所选的模式,转换的启动可以是ADC1主和ADC2从的交替触发或同步触发。
共有6种可能的模式:

  • 同步注入模式
  • 同步规则模式
  • 快速交叉模式
  • 慢速交叉模式
  • 交替触发模式
  • 独立模式

还有可以用下列方式组合使用上面的模式:

  • 同步注入模式 + 同步规则模式
  • 同步规则模式 + 交替触发模式
  • 同步注入模式 + 交叉模式

1.3 STM32ADC固件库函数介绍

  • 初始 ADC 用到的 GPIO;
  • 设置 ADC 的工作参数并初始化;
  • 设置 ADC 工作时钟;
  • 设置 ADC 转换通道顺序及采样时间;
  • 配置使能 ADC 转换完成中断,在中断内读取转换完数据;
  • 使能 ADC;
  • 使能软件触发 ADC 转换。
  • ADC 转换结果数据使用中断方式读取,这里没有使用 DMA 进行数据传输。

1.3.1 ADC初始化

void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct)

==ADC初始化结构体==

typedef struct
{
    uint32_t ADC_Mode; //工作模式
    FunctionalState ADC_ScanConvMode; //ADC扫描(多通道)使能/失能
    FunctionalState ADC_ContinuousConvMode; //连续转换使能/失能
    uint32_t ADC_ExternalTrigConv; //ADC触发信号选择
    uint32_t ADC_DataAlign; //ADC数据对齐模式
    uint8_t ADC_NbrOfChannel; //ADC采集通道
}
  • ADC_Mode:配置 ADC 的模式,当使用一个 ADC 时是独立模式,使用两个 ADC 时是双模式,在双模式下还有很多细分模式可选,具体配置 ADC_CR1:DUALMOD 位。
  • ADC_ScanConvMode: 用来设置是否开启扫描模式,因为是单次转换,这里我们选择不开启值 DISABLE 即可。
  • ADC_ContinuousConvMode: 用来设置是否开启连续转换模式,因为是单次转换模式,所以我们选择不开启连续转换模式,DISABLE 即可。
  • ADC_ExternalTrigConv: 是用来设置启动规则转换组转换的外部事件,这里我们选择软件触发,选择值为 ADC_ExternalTrigConv_None 即可。
  • DataAlign: 用来设置 ADC 数据对齐方式是左对齐还是右对齐,这里我们选择右对齐方式ADC_DataAlign_Right。
  • ADC_NbrOfChannel:用来设置规则序列的长度,这里我们是单次转换,所以值为 1 即可。
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 工作模式:独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //AD 单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //AD 单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
//转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC 数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的 ADC 通道的数目 1
ADC_Init(ADC1, &ADC_InitStructure); //根据指定的参数初始化外设 ADCx

==ADC_Mode==

ADC_Mode_Independent //ADC1 和 ADC2 工作在独立模式
ADC_Mode_RegInjecSimult //ADC1 和 ADC2 工作在同步规则和同步注入模式
ADC_Mode_RegSimult_AlterTrig //ADC1和ADC2工作在同步规则模式和交替触发模式
ADC_Mode_InjecSimult_FastInterl //ADC1和ADC2工作在同步规则模式和快速交替模式
ADC_Mode_InjecSimult_SlowInterl //ADC1和ADC2工作在同步注入模式和慢速交替模式
ADC_Mode_InjecSimult //ADC1 和 ADC2 工作在同步注入模式
ADC_Mode_RegSimult //ADC1 和 ADC2 工作在同步规则模式
ADC_Mode_FastInterl //ADC1 和 ADC2 工作在快速交替模式
ADC_Mode_SlowInterl //ADC1 和 ADC2 工作在慢速交替模式
ADC_Mode_AlterTrig //ADC1 和 ADC2 工作在交替触发模式

==ADC_ScanConvMode==

//ADC的扫描模式,不断扫描ADC1,2,3,扫描多用在多通道上
ENABLE
DISABLE

==ADC_ContinuousConvMode==

//连续转换模式,ADC通道连续采集,一次采集转化完继续采集
 ENABLE
 DISABLE

==ADC_ExternalTrigConv==

//外部触发转换选择
ADC_ExternalTrigConv_T1_CC1 //选择定时器 1 的捕获比较 1 作为转换外部触发
ADC_ExternalTrigConv_T1_CC2 //选择定时器 1 的捕获比较 2 作为转换外部触发
ADC_ExternalTrigConv_T1_CC3 //选择定时器 1 的捕获比较 3 作为转换外部触发
ADC_ExternalTrigConv_T2_CC2 //选择定时器 2 的捕获比较 2 作为转换外部触发
ADC_ExternalTrigConv_T3_TRGO //选择定时器 3 的 TRGO作为转换外部触发
ADC_ExternalTrigConv_T4_CC4 //选择定时器 4 的捕获比较 4 作为转换外部触发
ADC_ExternalTrigConv_Ext_IT11 //选择外部中断线 11 事件作为转换外部触发
ADC_ExternalTrigConv_None //转换由软件而不是外部触发启动

==ADC_DataAlign==

#define ADC_DataAlign_Right ((uint32_t)0x00000000)
#define ADC_DataAlign_Left ((uint32_t)0x00000800)

==ADC_NbrOfChannel==

ADC要转化的通道数目,可以设置为1‐16

1.3.2 使能/失能ADC外设

void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState)

//使能指定的 ADC1
ADC_Cmd(ADC1, ENABLE);

1.3.3 使能/失能ADC的DMA功能

void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState)

1.3.4 ADC中断功能配置

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

1.3.5 ADC获取转换值

在上面的校准完成之后,ADC 就算准备好了。接下来我们要做的就是设置规则序列 1 里面的通道,采样顺序,以及通道的采样周期,然后启动 ADC 转换。在转换结束后,读取 ADC 转换结果值就是了。

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel,uint8_t Rank, uint8_t ADC_SampleTime);

我们这里是规则序列中的第 1 个转换,同时采样周期为 239.5,所以设置为:

ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );

==软件开启 ADC 转换的方法==

ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能指定的 ADC1 的软件转换启动功能

==开启转换之后,就可以获取转换 ADC 转换结果数据==

ADC_GetConversionValue(ADC1);

同时在 AD 转换中,我们还要根据状态寄存器的标志位来获取 AD 转换的各个状态信息。库函数获取 AD 转换的状态信息的函数:

FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)

==判断 ADC1d 的转换是否结束==

while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

注意:芯片有外部参考电压:Vref-和 Vref+,其中 Vref-必须和 VSSA 连接在一起,而 Vref+的输入范围为:2.4~VDDA。精英 STM23 开发板通过 P7 端口,设置 Vref-和 Vref+设置参考电压,默认的我们是通过跳线帽将 Vref-接到 GND,Vref+接到 VDDA,参考电压就是 3.3V。

1.3.6 ADC获取和清除中断标志

ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT);
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT);

1.3.7 ADC预分频配置

void RCC_ADCCLKConfi

1.4 ADC Code

//初始化 ADC
//这里我们仅以规则通道为例
//我们默认将开启通道 0~3
void Adc_Init(void)
{ 
    ADC_InitTypeDef ADC_InitStructure; 
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |
                           RCC_APB2Periph_ADC1 , ENABLE ); //使能 ADC1 通道时钟
    RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置 ADC 分频因子 6 
    //72M/6=12,ADC 最大时间不能超过 14M
    //PA1 作为模拟通道输入引脚 
    GPIO_InitStructure.GPIO_Pin =GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
    GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.1
    ADC_DeInit(ADC1); //复位 ADC1,将外设 ADC1 的全部寄存器重设为缺省值
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 独立模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE; //单通道模式
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //单次转换模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//转换由
    //软件而不是外部触发启动
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC 数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC 通道的数目
    ADC_Init(ADC1, &ADC_InitStructure); //根据指定的参数初始化外设 ADCx 
    ADC_Cmd(ADC1, ENABLE); //使能指定的 ADC1
    ADC_ResetCalibration(ADC1); //开启复位校准 
    while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
    ADC_StartCalibration(ADC1); //开启 AD 校准
    while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
} 
//获得 ADC 值
//ch:通道值 0~3
u16 Get_Adc(u8 ch) 
{
    //设置指定 ADC 的规则组通道,设置它们的转化顺序和采样时间
    ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
    //通道 1,规则采样顺序值为 1,采样时间为 239.5 周期 
    ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能软件转换功能
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
    return ADC_GetConversionValue(ADC1); //返回最近一次 ADC1 规则组的转换结果
}
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;
}