STM32快速复习(四)ADC模数转换器

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

ADC(Analog-Digital Converter)模拟-数字转换器
ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁


一、模数转换器是什么?模数转换器原理?模数转换器作用?

ADC(Analog-Digital Converter)模拟-数字转换器
可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。当然,也存在数字到模拟的桥梁,如DAC、PWM波等,并且由于PWM只有完全导通和完全断开两种状态,PWM电路简单且没有额外的功率损耗,所以相比DAC,使用PWM来等效模拟量更适合直流电机调速等大功率应用场景(惯性系统)。而DAC主要应用于波形生成领域,如信号发生器、音频解码芯片等。

当时学习STM32用的是C8T6,C8T6只有ADC外设,没有DAC外设。C8T6的ADC参数:
(1)12位逐次逼近型ADC,1us转换时间(信号频率较高时需要注意)。
(2)输入电压范围:0-3.3V,转换结果范围:0~4095。
(3)18个输入通道,可测量16个外部(GPIO口)和2个内部信号源(内部温度传感器和内部参考电压)。
1)内部温度传感器:可以测量芯片温度。
2)内部参考电压:是一个1.2V左右的基准电压,不随外部供电电压变化。如果外部供电电压不是3.3V,那读取GPIO口的电压就不对,此时就可以通过读取这个基准电压来校准。
(6)规则组和注入组两个转换单元,是stm32的ADC增强功能。
(7)模拟看门狗自动监测输入电压范围,当AD值高于上阈值或低于下阈值时,就会申请中断,可减轻软件负担。
STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道。
在这里插入图片描述
在这里插入图片描述
逐次逼近型ADC,用于理解ADC的原理。了解原理即可,不做要求。STM32只画了一个框图:模数转换器。没有内部结构。
原理:运用电压比较器,一端是待测电压,一端是DAC输出电压。DAC给数据,可以输出相应电压。用DAC输出电压和外部ADC输入的电压进行比较。再对DAC的电压进行增加/减小等操作。使DAC输出电压和ADC输入电压近乎相等,这样,DAC输出就是ADC输入的电压。此时,芯片就知道了输入电压的准确数值。
常用二分法,具体方法网上可以看详细过程。利用二分法。会发现比较的电压为二进制的位权,所以比较过程是对二进制从高位到地位的依次判断(1 or 0)。所以就是逐次逼近的名字由来。

在这里插入图片描述
大部分情况下,电源输入和参考电压为同一个电源,即电源输入的值就算参考电压的值,就算ADC的范围值。
输入为3.3V.电压输出范围和检测范围就为0-3.3
输入为5V.电压输出范围和检测范围就为0-5
ADCCLK来源于ADC预分频器来源于时钟RCC。APB2时钟通过ADC分频器进行分频,得到ADCCLK,具体电路看时钟树那一章。ADCCLK最高14MHz。所以ADC预分频器只能选择6分频或者8分频。

在这里插入图片描述
ADCx_IN0~ADCx_IN15、温度传感器、V REFINT:ADC的16个输入通道。
注入通道【使用不多】:最多一次性选4路通道,配合4个16位寄存器,就可以一次性转换4路模拟数据。
规则通道【常用】:最多一次性选16路通道,但只有1个16位寄存器,存在新来的数据覆盖上一个数据的问题,此时要么尽快将数据取走,要是使用DMA帮助转运数据,进而可以实现一次性转换16路模拟数据。当然,一次就选一个通道,就是普通的ADC功能。
触发转换电路:stm32中的ADC触发方式:
软件触发:在程序中手动调一句代码。
硬件触发:上图所示的触发源。主要来自于定时器TIMx,也可以外部中断引脚EXTI。正常思路是:定时器每隔1ms产生一次中断 --> 中断函数中开启触发转换信号 --> ADC完成
一次转换。缺点是需要频繁进入中断,消耗软件资源。但是得益于上图的硬件电路设计,stm32可以直接使用定时器主模式触发ADC转换,硬件全自动无需申请中断,可以极大地减轻CPU负担。
VDDA 、VSSA :ADC的供电引脚。
V REF+、V REF-:ADC的参考电压,决定了ADC的输入电压的范围。stm32内部已经和VDDA 、
VSSA连接在一起了。
ADCCLK:来自ADC的预分频器,这个ADC的预分频器则来自于“RCC时钟树”。具体可以查看时钟树的电路,默认情况就是对72MHz进行ADC预分频,由于ADCCLK最大18MHz,所以只能选择6分频/8分频。
DMA请求:触发DMA进行数据转运。
注入通道数据寄存器、规则通道数据寄存器:用于存放转换结果。
模拟看门狗:一旦高于上阈值或低于下阈值,就会申请模拟看门狗的中断,最终进入NVIC。
转换结束EOC:规则通道转换完成,会在状态寄存器置标志位。
注入转换结束JEOC:注入通道转换完成,会在状态寄存器置标志位。
NVIC:嵌套向量中断控制器,控制是否响应上面这三个中断。
在这里插入图片描述
整个ADC原理:不同电压通过输入通道进行选择,进入AD转换器。通过规则组/注入组放置在数据寄存器。通过触发控制和时钟进行操作和推动。模拟看门狗用于监测范围。

二、模数转换器规则和特殊需要注意的部分

1.引脚复用关系

在这里插入图片描述
对于C8T6这个型号来说;
ADC1和ADC2共用引脚,不仅可以单独使用,可以组成更加复杂的双ADC模式。双ADC模式通过配合可以组成同步模式、交叉模式(ADC1和ADC2交叉对同一个通道进行采样,以提高采样率)等。

2.规则组的转换模式

stm32的ADC最多同时支持16个通道,那么ADC每次扫描1个通道还是多个通道,便是选择 非扫描模式/扫描模式;而对于单个通道的ADC转换来说,触发一次ADC是只转换一次,还是自动的进行连续转换,便是选择 单次转换/连续转换。上面这两种选择进行组合,便产生了 规则组的4种转换模式:
单次转换,非扫描模式
连续转换,非扫描模式
单次转换,扫描模式
连续转换,扫描模式
也就是两个ADC的组合使用。
在这里插入图片描述

在这里插入图片描述

规则组只有一个数据寄存器,只能保留最后一个输入电压的检测过程,当下一个数据检测完后,会覆盖上一个数据。一般利用DMA,当前面电压的检测完成之后,利用DMA将数据转移到其他寄存器。防止被覆盖。

3.注入组的转换模式

可以一次性检测四个电压,并且有四个数据寄存器,可以防止数据被覆盖。避免规则组的情况。

4.触发转换信号

在这里插入图片描述
要想ADC进行转换,还需要完成触发 这个操作。触发信号可以是 软件触发、硬件触发。软件触发可以由ADC的库函数完成;硬件触发见上图(利用定时器或者外部引脚,申请中断进行触发)。

5.数据对齐

在这里插入图片描述
因为ADC是12位的,而寄存器宽度为16位,所有便有了数据对齐方式的选择。

  1. 右对齐【常用】:读出的值就是实际值。
  2. 左对齐:有时候不需要太大的分辨率,便将12位ADC的转换数据左对齐,然后只取高8位。

6.转换时间

AD转换的步骤:采样,保持,量化,编码
采样:防止电压乱跳,乱跳的电压无法测量。

STM32 ADC的总转换时间为:
TCONV = 采样时间 + 12.5个ADC周期
采样时间:程序中配置,越长越好
ADC周期:RCC分频的ADCCLK

例如:当ADCCLK=14MHz,采样时间为1.5个ADC周期
TCONV = 1.5 + 12.5 = 14个ADC周期 = 1μs

7.校准

ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差

建议在每次上电后执行一次校准

启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期

ADC初始化的最后加几条代码即可,知道有这个过程就行。

三、代码理解

1.引入库

ADC初始化/一般性配置:
RCC_ADCCLKConfig 【必需】:配置 ADCCLK 的分频器(rcc库函数)。可以对APB2的72MHz时钟选择2、4、6、8分频,输入到ADCCLK。但由于ADCCLK规定不能超过14MHz,
所以一般选择分频系数为6。
ADC_DeInit :将ADCx的配置恢复成默认值。
ADC_Init 【必需】:ADC的初始化函数。
ADC_StructInit :给ADC结构体赋一个默认值。
ADC_Cmd 【必需】:ADC外设的开关控制,用于给ADC上电。
间断模式:
ADC_DiscModeCmd :是否启用间断模式。
ADC_DiscModeChannelCountConfig :配置间断模式,每隔几个通道间断一次。
触发转换:
ADC_SoftwareStartConvCmd :软件触发。用于软件触发ADC进行转换。
ADC_ExternalTrigConvCmd :ADC外部触发转换控制。是否允许外部触发转换。默认关闭。
ADC_GetSoftwareStartConvStatus 【不常用】:获取软件开始转换状态,看看规则通道是否已经开始转换,但并不能用于指示转换的结束。
配置通道:
ADC_TempSensorVrefintCmd :开启内部的温度传感器、参考电压通道作为输入源。
ADC_RegularChannelConfig 【必需】:ADC规则组通道配置。给序列的每个位置填写指定的通道。
获取结果:
ADC_GetFlagStatus :获取ADC内任意指定标志位状态(比如转换结束标志位EOC)。
ADC_ClearFlag :清除所选定的标志位。
ADC_GetConversionValue :获取ADC转换值,就是用于读取转换之后的结果。
ADC_GetDualModeConversionValue :获取ADC双模式转换值。
ADC校准【必需】:ADC初始化完成后,依次调用即可。
ADC_ResetCalibration :复位校准。
ADC_GetResetCalibrationStatus :获取校准状态。
ADC_StartCalibration :开始校准。
ADC_GetCalibrationStatus :获取开始校准状态。
注入组的配置:
ADC_AutoInjectedConvCmd
ADC_InjectedDiscModeCmd
ADC_ExternalTrigInjectedConvConfig
ADC_ExternalTrigInjectedConvCmd
ADC_SoftwareStartInjectedConvCmd
ADC_GetSoftwareStartInjectedConvCmdStatus
ADC_InjectedChannelConfig
ADC_InjectedSequencerLengthConfig
ADC_SetInjectedOffset
ADC_GetInjectedConversionValue
DMA相关:
ADC_DMACmd :开启DMA,使其可以转运数据。
中断相关:
ADC_AnalogWatchdogCmd :软件看门狗使能。
ADC_AnalogWatchdogThresholdsConfig :软件看门狗的上下阈值配置。
ADC_AnalogWatchdogSingleChannelConfig :软件看门狗的通道配置。
ADC_ITConfig :ADC的中断输出控制。用于控制某个中断能否通向NVIC。
ADC_GetITStatus :获取中断标志位。
ADC_ClearITPendingBit :清除中断标志位。

2.示例代码

代码如下(示例):

 #include "stm32f10x.h"                  // Device header
 #include "OLED.h"
 #include "ADC_User.h"
 int main(void){
   
   //OLED显示屏初始化
   OLED_Init();
   OLED_ShowString(1,1,"Voltage-PA0:");
   OLED_ShowString(2,1,"+00.00 V");    
   
   //ADC初始化
   ADC_User_Init();
   ADC_User_Start();
   
   while(1){
       OLED_ShowFloat(2,1,(float)ADC_User_Get()*3.3/4095,2,2);
   };
 }

ADC_User.c

 #include "stm32f10x.h"                  // Device header
 //ADC初始化-规则组PA0
 void ADC_User_Init(void){
   //1.开启外设时钟
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
   RCC_ADCCLKConfig(RCC_PCLK2_Div6);//6分频使得ADC时钟为12MHz
   //2.配置GPIO
   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);
   //3.配置多路开关,选择通道进入规则组
   ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_1Cycles5);
   //4.配置ADC转换器
   ADC_InitTypeDef ADC_InitStructure;
   ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换
   ADC_InitStructure.ADC_DataAlign          = ADC_DataAlign_Right;//数据右对齐
   ADC_InitStructure.ADC_ExternalTrigConv   = ADC_ExternalTrigConv_None;//不使用外部触发(软件触发)
   ADC_InitStructure.ADC_Mode               = ADC_Mode_Independent;//独立模式
   ADC_InitStructure.ADC_NbrOfChannel       = 1;//只有1个通道(非扫描模式,参数不起作用)
   ADC_InitStructure.ADC_ScanConvMode       = DISABLE;//非扫描模式(因为是单通道)
   ADC_Init(ADC1, &ADC_InitStructure);
   //5.配置开关控制
   ADC_Cmd(ADC1, ENABLE);
   //6.进行ADC校准
   ADC_ResetCalibration(ADC1);
   while(ADC_GetResetCalibrationStatus(ADC1)==SET);
   ADC_StartCalibration(ADC1);
   while(ADC_GetCalibrationStatus(ADC1)==SET);
 }
 //对ADC进行一次软件触发
void ADC_User_Start(void){
   ADC_SoftwareStartConvCmd(ADC1, ENABLE);
 }
 //获取ADC转换结果
uint16_t ADC_User_Get(void){
   //等待转换完成并读取
   while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
   return ADC_GetConversionValue(ADC1);//硬件会自动清除EOC标志位
}

  1. stm32库函数已经有了ADC打头的库函数了,如ADC_Init(),所以命名的时候不要再使用ADC,而可以使用ADC_User。
  2. GPIO配置成模拟输入AIN模式。AIN模式下,GPIO口无效,可以防止GPIO的输入输出对模拟电压造成干扰。AIN模式是ADC的专属模式。实际测试中,浮空输入、上拉输入、模拟输入的展示效果几乎没有区别(但是硬件原理完全不同)。
  3. 函数提示设置:找到扳手图标—->Text Completion栏—->把Show Code Completion List For下面的框全部勾上。
    4. 读取规则组数据后,无需软件清除EOC标志位。参考手册中说明,读取ADCC_DR就会自动清除EOC标志位。所以参考手册还是非常重要!!
  4. 关于数据抖动。实测发现ADC转换后的结果会抖动,若想消除这种现象,可以有以下几种方法:
    常用滤波:
    如均值滤波(LPF)。
    裁剪分辨率:去除转换结果的最后抖动的几位。

总结

扫描模式+DMA转运数据:DMA是转运多通道数据的最优解
下章复习DMA。
ADC原理不复杂,但有部分还需要多和前几章一块理解下,例如时钟树等。

  • 26
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值