ADC

ADC代表模数转换器(Analog-to-Digital Converter),是一种电子设备或电路,用于将模拟信号转换为数字信号。模拟信号是连续变化的信号,如电压、电流或温度,而数字信号是离散的信号,由一系列数字值表示。ADC的主要作用是将模拟信号转化为数字形式,以便数字电子设备能够处理、存储和分析这些信号。

独立模式单通道采集

CKS32F107xx系列产品提供2个12位的模拟/数字转换器(ADC),每个ADC共用多达16个外部通道,各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。

A/D转换器的供电和参考电压

为了提高转换的精确度,ADC使用一个独立的电源供电,其电源引脚为VDDA和VSSA,从而过滤和屏蔽来自印刷电路板上的毛刺干扰。在进行硬件设计的时候,VDDA和VSSA必须分别连接到VDD和VSS。对于100脚封装的,为了确保输入为低压时获得更好精度,用户可以连接一个独立的外部参考电压ADC到VREF+和VREF-脚上,其中,VREF-引脚必须连接到VSSA,而VREF+的电压范围为2.4V~VDDA。对于64引脚封装的,没有VREF+和VREF-引脚,他们在芯片内部与ADC的电源(VDDA)和地(VSSA)相联。

ADC转换时间

ADC输入时钟ADC_CLK由PCLK2 经过分频产生,最大是14M,分频因子由RCC时钟配置寄存器RCC_CFGR的位15:14 ADCPRE[1:0]设置,可以是2/4/6/8分频,注意这里没有1分频。一般我们设置PCLK2=HCLK=72M。ADC使用若干个ADC_CLK周期对输入电压采样,采样周期数目可以通过ADC_SMPR1和 ADC_SMPR2寄存器中的SMP[2:0]位更改。每个通道可以分别用不同的时间采样。 总转换时间如下计算:TCONV=采样时间+12.5个周期。例如当ADCCLK=14MHz,采样时间为1.5周期,则总的转换时间TCONV=1.5+12.5=14周期=1us。

ADC数据寄存器

ADC转换完成后的数据输出寄存器。根据转换组的不同,规则组的完成转换的数据输出到ADC_DR寄存器,注入组的完成转换的数据输出到ADC_JDRx寄存器。假如是使用双重模式,规则组的数据也是存放在ADC_DR寄存器。ADC规则组数据寄存器ADC_DR是一个32位的寄存器,独立模式时只使用到该寄存器低16位保存ADC1/2的规则转换数据。在双ADC模式下,高16位用于保存ADC2转换的数据,低16位用于保存ADC1转换的数据。因为ADC的精度是12位的,ADC_DR寄存器无论高16位还是低16位,存放数据的位宽都是16 位的,所以允许选择数据对齐方式。由ADC_CR2寄存器的ALIGN位设置数据对齐方式,可选择:右对齐或者左对齐。如果使用多通道转换,那么这些通道的数据 也会存放在ADC_DR里面,按照规则组的顺序,上一个通道转换的数据,会被下一个通道转换的数据覆盖掉,所以当通道转换完成后要及时把数据取走。比较常用的方法是使用DMA模式。当规则组的通道转换结束时,就会产生DMA请求,这样就可以及时把转换的数据搬运到用户指定的目的地址存放。注意:只有ADC1可以产生DAM请求,而由ADC2转换的数据可以通过双ADC模式,利用ADC1的 DMA功能传输。

ADC中断

ADC中断可分为三种:规则组转换结束中断、注入组转换结束中断、设置了模拟看门狗状态位中断。它们都有独立的中断使能位,分别由ADC_CR 寄存器的EOCIE、JEOCIE、AWDIE位设置,对应的标志位分别是EOC、JEOC、AWD。

ADC初始化结构体详解

ADC_InitTypeDef结构体用于设置ADC的工作参数,并由标准库函数ADC_Init()调用这些设定参数进入设置外设相应的寄存器,达到配置外设工作环境的目的。其具体的定义如下:

typedef struct
{  
    uint32_t ADC_Mode;                       
    FunctionalState ADC_ScanConvMode;       
    FunctionalState ADC_ContinuousConvMode;  
    uint32_t ADC_ExternalTrigConv;            
    uint32_t ADC_DataAlign;                  
    uint8_t ADC_NbrOfChannel;              
}ADC_InitTypeDef;

ADC_Mode:配置ADC的模式,当使用一个ADC时是独立模式,使用两个ADC 时是双模式,在双模式下还有很多细分模式可选,具体配置ADC_CR1:DUALMOD位。

ScanConvMode:可选参数为ENABLE和DISABLE,配置是否使用扫描。如果是单通道AD转换使用DISABLE,如果是多通道AD转换使用ENABLE,具体配置 ADC_CR1:SCAN位。

ADC_ContinuousConvMode:可选参数为 ENABLE 和 DISABLE,配置是启动自动连续转换还是单次转换。使用ENABLE配置为使能自动连续转换;使用 DISABLE 配置为单次转换,转换一次后停止需要手动控制才重新启动转换,具体配置 ADC_CR2:CON位。

ADC_ExternalTrigConv:外部触发选择,ADC有很多外部触发条件,可根据项目需求配置触发来源。实际上,我们一般使用软件自动触发。

ADC_DataAlign:转换结果数据对齐模式,可选右对齐ADC_DataAlign_Right 或者左对齐ADC_DataAlign_Left。

ADC_NbrOfChannel:AD转换通道数目,根据实际设置即可。具体的通道数和通道的转换顺序是配置规则序列或注入序列寄存器。

CKS32F103XX ADC单通道采集实验

本实验使用规则组单通道的单次转换模式,并且通过软件触发,即由ADC_CR2寄存器的SWSTART位启动。下面讲解其详细设置步骤:

1) 开启PA口时钟和ADC1时钟,设置PA1为模拟输入。

CKS32F107xx的ADC通道1在PA1上,所以,我们先要使能PORTA的时钟和ADC1时钟,然后设置PA1为模拟输入。使能GPIOA和ADC时钟用RCC_APB2PeriphClockCmd函数,设置 PA1的输入方式,使用GPIO_Init函数即可。

2) 复位 ADC1,同时设置 ADC1 分频因子。 

开启ADC1时钟之后,我们要复位ADC1,将ADC1的全部寄存器重设为缺省值之后我们 就可以通过RCC_CFGR设置ADC1的分频因子。分频因子要确保ADC1的时钟(ADCCLK)不要超过14Mhz。这个我们设置分频因子位6,时钟为72/6=12MHz,库函数的实现方法是: 

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

ADC 时钟复位的方法是:

ADC_DeInit(ADC1);

3) 初始化ADC1参数,设置ADC1的工作模式以及规则序列的相关信息。 

在设置完分频因子之后,我们就可以开始 ADC1 的模式配置了,设置单次转换模式、触发方式选择、数据对齐方式等都在这一步实现。同时,我们还要设置ADC1规则序列的相关信息,我们这里只有一个通道,并且是单次转换的,所以设置规则序列中通道数为1。这些在库函数中是通过函数ADC_Init实现的。

4) 使能ADC并校准。 

在设置完了以上信息后,我们就使能AD转换器,执行复位校准和AD校准,注意这两步 是必须的!不校准将导致结果很不准确。 使能指定ADC的方法是:

ADC_Cmd(ADC1, ENABLE);

执行复位校准的方法是:

ADC_ResetCalibration(ADC1);

执行ADC校准的方法是:

ADC_StartCalibration(ADC1);

每次进行校准之后要等待校准结束。这里是通过获取校准状态来判断是否校准是否结束。等待复位校准结束函数为:

while(ADC_GetResetCalibrationStatus(ADC1));

等待AD校准结束函数为:

while(ADC_GetCalibrationStatus(ADC1));

5)读取ADC值。 

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

ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );

软件开启ADC转换的方法是:

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

开启转换之后,就可以获取转换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 ));

通过以上几个步骤的设置,我们就能正常的使用CKS32F107xx的ADC1来执行AD转换操作了。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 14

AT32 的 ADC 是一个将模拟输入信号转换为设定分辨率数位数字信号的外设。采样率最高可达 5.33MSPS。多达 26 个通道源可进行采样及转换。具备多种功能强大的模式,本文主要以 ADC 的特 色功能进行讲解和案列解析。

ADC简介

ADC控制器的功能极其强大。其包含但不限于以下内容
  • 时钟及状态,由数字和模拟时钟两个部分组成
  • 分辨率及采样转换,可配置分辨率为12/10/8/6位的转换,采样周期支持广范围的配置
  • 自校准,自带校准功能以纠正数据偏移
  • 基本模式,支持多种模式,不同模式可组合使用满足多种应用
  • 不同优先权的通道,普通通道与抢占通道具备不同的优先权
  • 多种独立的触发源,包括TMR、EXINT、软触发等多种触发选择
  • 数据后级处理,包括数据的对齐,抢占通道偏移量等多种处理
  • 转换中止,可软件控制在ADC不掉电状态下实现转换中止
  • 过采样器,普通及抢占通道均支持过采样
  • 电压监测,通过对转换结果的判定来实现电压监测
  • 中断及状态事件,具备多种标志指示ADC状态,且某些标志还具备中断功能
  • 多种转换数据的获取方式,包括DMA获取、CPU获取两种方式实现转换数据的读取
  • 多达26个通道:IN0~IN17,IN20~IN27

《雅特力AT32F423 ADC使用指南》全文。

来源:AT32 MCU 雅特力科技

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 38

01、引言

很多 STM32 系列中的 ADC 都带有自校准的功能。它提供了一个自动校准的过程,用于驱动包括 ADC 上电/掉电序列在内的所有校准动作。在这个过程中,ADC 计算出一个校准因子,并在内部应用到此 ADC 模块,直到下一次 ADC 掉电。在执行任何 ADC 操作之前必须校准,以消除芯片之间 ADC 结果的偏差。

02、问题

2.1. 问题详情

客户使用 STM32U575ZIT6Q 验证 ADC4 时,使用 STM32CubeMX 配置后生成工程项目。因为使用 ADC 进行采样转换前,必须要做 ADC 的自校准。于是在 main 函数中加入自校准代码,如图 1 所示。

1.png

图1.代码:ADC 自校准

然而,在运行代码的情况下,发现 PC 指针最后跑到这个 Calibration Error 的Error_Handler()里。也就是说,执行自校准失败了! 

再检查进入 Error_Handler()发生的问题,发现在执行HAL_ADCEx_Calibration_Start()时陷在了下面这个 Loop 当中,如图 2 所示。

2.png

图2.代码:等待 ADC 自校准完成

也就是说,ADCAL 位被置 1 后,始终没有被硬件清 0,代表自校准始终不成功。

2.2. 问题分析

查看 STM32U5 的参考手册 RM0456 中关于自校准的描述。考虑到校准没有成功,那么应该看看是不是自校准需要哪些条件,而这些条件并没有成立。 

于是,在 ADC 章节中的 Calibration 小节找到了这么一段话,如图 3 所示。

3.png

图3.ADC 自校准要求条件

也就是说,在初始化自校准之前,需要保证 3 个条件: 

1) ADC 的电压调整器已经使能并正常工作(ADVREGEN = 1 且 LDORDY =1) 

2) ADC 没有打开(ADEN=0) 

3) 自动掉电模式没有使能(AUTOFF = 0) 

回到刚才等待 ADC 自校准完成的代码,当指针停留到这边时,在线调试检查各个标志位情况,发现 ADVREGEN=1,ADEN=0,AUTOFF=0,LDORDY=0。所以,可以肯定的是就是 LDORDY 不为 1,也就是说 ADC 的电压调整器还没有准备好,导致了自校准无法成功并退出。 

在 STM32U5 中,引入了一个新的 ADC 特性,叫 ADC 电压调整器(ADC voltageregulator)。在使用 ADC 之前,这个电压调整器必须被使能并且能够稳定工作。可以通过将 ADC_CR 寄存器中的 ADVREGEN 位置 1 来使能它,然后必须要等这个电压调整器的启动时间之后,才可以正常启动自校准或者使用 ADC。这个 LDO 有没有准备好,可以通过 ADC_ISR 寄存器中的 LDORDY 这个位来判断。LDORDY=1 才代表了 LDO 已经准备好了。 

回到客户的问题,可以知道即使 ADVREGEN 置 1 了,LDORDY 始终没有置起来,电压调整器没有启动工作,难道是 ADC 的这个电压调整器坏掉了? 

别急,想到电源的问题,还不能忘了参考手册的另一个章节 :电源控制 PWR。翻到PWR 这一章,先要考虑到,与 ADC 相关的电源为 VDDA,所以要重点查看 VDDA 的内容。找到 Independent analog peripherals supply 这一小节,可以看以下关键的句子,如图 4。

4.png

图4.独立的模拟外设供电

从图中黄色高亮的文字,我们可以了解到,STM32U5 的 VDDA 在控制上还跟其他系列不一样,它有一个开关来选择是否隔离。芯片复位后,VDDA 提供的 ADC 和模拟开关控制在逻辑和电气上是隔离的,因此不可用。一旦 VDDA 电源存在,就必须在使用模拟外设之前,通过在 PWR_SVMCR 寄存器中将 ASV 置位来消除隔离。也就是说,要想使用ADC,必须先将 ASV 置位来消除隔离。 

在 PWR_SVMCR 寄存器的解释中,也可以看到 ASV 位的描述中要求将此位强制置 1才可以使用模拟外设,如图 5。

5.png

图5.ASV 位描述

ASV 的配置是没有在 STM32CubeMX 中进行配置的,所以需要在后期添加。所以,很可能就是因为没有将 ASV 置位以使能 VDDA,导致 ADC 外设被隔离,并没有得到供电,所以 ADC 的电压调整器不可能正常工作,LDORDY 也不可能为 1。

03、问题解决

在工程项目中搜索 ASV,可以找到两个 API,如图 6。

6.png

图6.VDDA 使能或禁用 API

也就是说,我们需要先将 HAL_PWREx_EnableVddA()添加到代码中。因为其属于MSP 硬件配置,所以将其加到 HAL_MspInit()函数,如图 7 所示。

7.png

图7.修改过的 HAL_MspInit()函数

再重新编译,然后执行指令代码,就可以看到 ADC 的自校准可以正常完成,PC 指针已经可以正常跑到自校准后面的代码了。

04、小结

在 STM32U5 中,为了更好地控制功耗,ADC 中加入电压调整器和 VDDA 隔离功能。这与以往的 STM32 有所不同,需要注意一下。使用 ADC 等模拟外设前,需要注意将PWR_SVMCR 寄存器中将 ASV 置位来消除隔离。 

其实不仅仅是 VDDA 可以隔离,VDDIO2 也是可以的,通过 PWR_SVMCR 寄存器的IO2SV 位进行控制。

如果仔细看过 STM32U5 的 ADC 例程,可以看到 HAL_MspInit()的内容是这么写的,如图 8 所示。

8.png

图8.例程的 HAL_MspInit()函数

可以看到,在 MSP 初始化里,不仅使能了 VDDA,还使能了 VDDIO2。所以要使用VDDIO2 相关 I/O 的,也需要注意一下。

来源:STM32单片机

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 59

众所周知,模数转换,即Analog-to-Digital Converter,常称ADC,是指将连续变量的模拟信号转换为离散的数字信号的器件。主要用于对模拟信号进行数字采集,以进行数据处理。通常情况下,在转换完成时读取转换结果。而ADC中的窗口比较功能,会检测ADC结果是否低于或高于特定阈值,当转换结果在某一个范围时可以触发中断,几乎无需软件干预。该配置非常实用,它可以监视信号是否按照要求保持在特定范围内,或者在电池电量不足/过充时发出信号。

本文以RA4M2为例介绍如何设置ADC窗口比较功能。

RA MCU中一般都会包括12位逐次逼近型A/D转换器(ADC12)单元,最多可选择13路模拟量输入通道、温度传感器输出、内部参考电压进行转换。

同时,ADC12还提供比较功能(窗口A和窗口B)。比较功能分别指定窗口A和窗口B的参考值上限和的参考值下限,当所选通道的A/D转换值满足比较条件时输出中断。窗口A和窗口B的主要区别在于它们的中断输出信号不同以及对窗口B只能选择一个通道的限制。

这里以窗口A为例进行介绍。窗口A有以下4种比较模式。

• 结果低于阈值

• 结果高于阈值

• 结果在窗口内

• 结果在窗口外

窗口A比较功能相关的寄存器有ADCMPCR、ADCMPLR0/1和ADCMPDR0/1寄存器,详情请参考RA硬件手册。

1.png

2.png

3.png

4.png

5.png

ADC数据与可设置的高低阈值进行比较,并可为AD数据在设定的门限值内、外、高或低自动生成比较中断。需要设置三个选项位置,接下来我们将逐个介绍各种情况下属性设置和用户代码。

1、A/D转换结果高于门限值时产生窗口比较A中断(ADC120_CMPAI)

6.png

若想实现上面的比较功能,请按照下图中所示,在FSP中设置ADC属性。

(1)Input → Window Compare → Window A

Enable:选择Enabled

Lower Reference:请在这里设定阈值

(2) Input → Window Compare

Window Mode:选择Disabled

7.png

当AD转换结果 > ADCMPDR0(Lower Reference 设定值时)时,产生Compare中断。

2、A/D转换结果低于门限值时产生窗口比较A中断(ADC120_CMPAI)

8.png

若想实现上图的比较功能,请按照下图中所示,在FSP中设置ADC属性。

(1)Input → Window Compare → Window A

Enable:选择Enabled

Lower Reference:请在这里设定阈值

(2) Input → Window Compare

Window Mode:选择Disabled

(3)代码中将CMPLCHA0位置为“0”

R_ADC0->ADCMPLR_b->CMPLCHA0 = 0;

9.png

当AD转换结果 < ADCMPDR0(Lower Reference 设定值时),产生Compare中断。

3、A/D转换结果在门限值内

10.png

若想实现上图的比较功能,请按照下图中所示,在FSP中设置ADC属性。

(1)Input → Window Compare → Window A

Enable:选择Enabled

Lower Reference、Upper Reference:请在这里设定阈值

(2) Input → Window Compare

Window Mode:选择Enabled

11.png

当ADCMPDR0(Lower Reference设定值时)< AD转换结果< ADCMPDR1(Upper Reference设定值时)时,产生Compare中断。

4、A/D转换结果在门限值外

12.png

若想实现上图的比较功能,请按照下图中所示,在FSP中设置ADC属性。

(1)Input → Window Compare → Window A

Enable:选择Enabled

Lower Reference、Upper Reference:请在这里设定阈值

(2) Input → Window Compare

Window Mode:选择Enabled

(3)代码中将CMPLCHA0位置为“0”

R_ADC0->ADCMPLR_b->CMPLCHA0 = 0;

13.png

当AD转换结果<ADCMPDR0(Lower Reference 设定值时)或者>ADCMPDR1(Upper Reference设定值时)时,产生Compare中断。

另外,使用窗口比较功能时请注意以下限制:

● 比较功能不能与自诊断功能或双触发模式一起使用。(比较功能不适用于ADRD、ADDBLDR、ADDBLDRA和ADDBLDRB。)

● 使用匹配/不匹配事件输出时请使用单次扫描模式。

● 当温度传感器输出时,窗口A选择内部参考电压,窗口B操作被禁用。

● 当温度传感器输出时,窗口B选择内部参考电压,窗口A操作被禁用。

● 禁止为窗口A和窗口B设置相同的通道。

● 设置参考电压值时,请将高电位参考电压值设置为大于或等于低电位参考电压值。

更多内容,您可点击下方链接进入瑞萨中文论坛查看:

https://community-ja.renesas.com/zh/forums-groups/mcu-mpu/

来源:瑞萨MCU小百科

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 74

引言

MindSDK为MM32主流的微控制器,实现了一组ADC样例工程。本文通过讲解ADC模块的样例工程,介绍ADC模块的功能和用法。关于ADC模块对应的驱动程序,以及ADC外设模块硬件的实现细节,可具体查阅MindSDK工程的源文件,以及MM32微控制器(例如MM32F5270)的用户手册。

样例工程

MindSDK中为ADC驱动设计的样例工程包括:

  • adc_basic

  • adc_interrupt

  • adc_dma

  • adc_seq

  • adc_fixed_seq

  • adc_ext_seq

  • adc_awdg

  • adc_oversample

  • adc_sw_write_calib

其中, adc_baic 、 adc_interrupt 、 adc_dma 分别演示了ADC最典型的功能,包括通过轮询、中断、DMA来获取一个ADC转换通道的转换结果。adc_awdg 来展现ADC模拟看门狗的功能, adc_seq 、 adc_fixed_seq 、 adc_ext_seq 则展现ADC按照不同的转换序列来进行转换。还有一些用于特殊场景的功能,例如通过软件写入校准值,以及硬件实现的多次采样取平均值、基本滤波等。

adc_basic

adc_basic 描述了使用ADC模块最基本的方式,通过轮询方式对一个通道进行转换,并获取通道的转换值。在样例工程中,通过 ADC_Init() 函数,配置ADC数据有效数据位 ADC_Resolution_Alt0 ,ADC的转换模式为单通道单次转换模式 ADC_ConvMode_SingleSlot 、转换数据对其方式为右对齐 ADC_Align_Right 、通道转化结果通过单端转换方式,将通道与ADC内部参考电压VREF的差数字量化后输出 ADC_SingleDiffConvMode_SingleEnd 、配置单端转换方式的参考电压为内部参考电压 ADC_SingleConvVref_Internal ,配置待转换通道的序列和每个转换通道的采样周期。之后,每次通过 ADC_DoSwTrigger() 函数,使用软件触发的方式启动ADC转换。

实际运行程序时,用户在串口调试终端中每次输入任意字符,改变转换通道的输入电压,会看到串口终端界面对应数值的变化。

adc_interrupt

adc_interrupt 相对于 adc_basic 样例工程,实现了ADC转换完成后触发中断服务,在中断服务中获取转换值的用法。

其实现原理,是在 adc_basic 的配置的基础上,通过 ADC_EnableInterrupts() 函数和 NVIC_EnableIRQ()函数 开启ADC的中断。当ADC转换通道完成转换后,就进入中断服务函数。

实际运行程序时,用户在串口调试终端每次输入任意字符,改变转换通道的输入电压,会看到串口终端界面对应数值的变化。

adc_dma

adc_dma 在 adc_basic 的基础上增加对DMA外设模块的使用。在应用层 app_adc_init() 函数中添加对DMA通道的配置,通过 DMA_InitChannel() 函数, DMA_XferMode_PeriphToMemory 配置传输方向为外设到内存、 MemAddr 和 PeriphAddr 配置内存和外设地址、 MemAddrIncMode 和 PeriphAddrIncMode 配置内存和外设的增量模式、 XferCount 传输的数据量、 DMA_ReloadMode_AutoReloadContinuous 自动重载传输量、 DMA_XferWidth_32b 以32位的数据进行传输。外设映射的DMA通道初始配置完成后,通过 DMA_EnableChannel() 函数使能DMA通道,通过 NVIC_EnableIRQ() 和 DMA_EnableChannelInterrupts() 函数开启DMA中断。之后,每次通过 ADC_DoSwTrigger() 函数触发ADC开始转换,当转换完成后DMA开始搬运,当DMA搬运完成后进入中断服务函数,以此循环往复。

实际运行程序时,用户在串口调试终端中每次输入任意字符,改变转换通道的输入电压,会看到串口终端界面对应数值的变化。

adc_fixed_seq

adc_fixed_seq 在 adc_dma 样例的基础上,将转换的ADC通道由一个通道,设置为多个通道形成一个序列来进行转换,当序列转换完成后,就停止转换。通过 ADC_EnableSeqSlotFixed() 函数来配置转换序列的顺序为有低到高的顺序进行转换。DMA将ADC每个通道数据搬运到指定内存。通过在循环中调用 ADC_DoSwTrigger() 函数来进行一个序列的多次转换。或者通过 ADC_ConvMode_SeqContinues 启用序列的连续转换模式。通过 ADC_DoSwTrigger() 函数可以停止序列转换。

实际运行程序时,用户在串口调试终端中每次输入任意字符,改变配置的转换序列中每个通道的输入电压值,会看到串口调试终端界面对应数值变化。

adc_seq

adc_seq 与 adc_fixed_seq 样例的不同之处在于, adc_fixed_seq 样例只能实现一个由高到低或者由低到的通道转换序列进行转换,例如ch0-ch1-ch3或者ch3-ch1-ch0的转换顺序。adc_seq 样例则实现任意通道序列的转换,例如ch3-ch0-ch5,任意的序列。通过ADC_EnableSeqSlot () 函数配置具体的转换序列。ADC根据配置的转换序列开始进行转换。当序列转换完成后,停止转换。可以重复的调用 ADC_DoSwTrigger() 开启下一个序列的转换,一次循环往复。或者通过 ADC_ConvMode_SeqContinues 启用序列的连续转换模式。通过 ADC_DoSwTrigger() 函数可以停止序列转换。

实际运行程序时,用户在串口调试终端中每次输入任意字符,改变转换通道的输入电压,会看到串口终端界面对应数值的变化。

adc_ext_seq

adc_ext_seq 实现在当前任意序列转换过程中插入额外的序列,当插入额外的序列时,当前任意序列中正在转换的通道完成,将切换到插入的序列中进行转换,当转换完成后在继续之前任意序列中剩余通道的转换。通过 ADC_EnableExtSeqSlot() 函数配置额外的序列。例如当前任意序列为ch3-ch0-ch5,额外的序列为ch1-ch4。ADC_EnableAutoExtSeqSlot() 函数开启自动注入模式,即任意序列转换完成后,开启注入通道的转换,转换额外的序列。这种注入模式增加了整个序列的长度。当一个完整序列转换完成后,停止转换,调用 ADC_DoSwTrigger() 函数来进行一个序列的多次转换。或者通过 ADC_ConvMode_SeqContinues 启用序列的连续转换模式。通过 ADC_DoSwTrigger() 函数可以停止序列转换。

实际运行时,用户在串口调试终端中每次输入任意字符,改变转换通道的输入电压,会看到串口终端界面对应数值的变化。

adc_awdg

adc_awdg 在 adc_basic 的基础上增加对转换结果进行监控,通过 ADC_EnableHwComp() 函数配置需要监控的转换通道 ChnNum 、设置的上限值 HighLimit 和下限值 LowLimit ,可以根据上限值和下限值的大小关系设置监控范围,当上限值大于或等于下限值时,监控通道的转换值在两个阈值之外的数值,若开启中断则会进入中断服务函数;同理,当上限值小于下限值时,监控通道转换值在两个阈值之间的数值,若开启中断则会进入中断服务函数。之后,每次通过 ADC_DoSwTrigger() 函数,使用软件触发的方式启动ADC转换。

实际运行时,用户在串口调试终端中每次输入任意字符,改变转换通道的输入电压,当电压值满足监控区间时,会看到串口终端界面当前转换数据。

adc_oversample

adc_oversample 在 adc_seq 的基础上通过 ADC_SetOverSample()`函数启动硬件的过采样模式, ADC_OverSampleRatio_256 配置过采样率为256, ADC_OverSampleDataDiv_8 配置数据右移8位, ADC_OverSampleTriggerMode_Multi 配置为所有通道的过采样转换只需要进行一次触发。injectOverSampleMode 配置是否开启注入通道的过采样模式。之后,每次通过 ADC_DoSwTrigger() 函数,使用软件触发的方式启动ADC转换。

adc_sw_write_calib

adc_sw_write_calib 在 adc_basic 的基础上实现使用软件保存ADC的校准值,然后通过软件将校准值写入寄存器中。通过 ADC_GetCalibFactor() 函数获取校准值,通过 ADC_SetCalibFactor() 来写入校准值。

来源:灵动MM32MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 17

在上一讲单通道ADC电压采集的基础上,本节主要介绍CKS32F4xx系列产品基于DMA传输的ADC多通道电压采集转换实现。

DMA传输在ADC中的应用

DMA是直接存储器存取,通常在使用ADC时,需要通过MCU内核不停的读取数据,如果使用DMA,那么读取的过程会绕过MCU,减轻MCU内核的处理压力,这样有利于资源的充分利用,提高ADC数据的处理效率。由于ADC规则通道组只有一个数据寄存器中,当转换多个通道时,使用DMA还可以避免丢失已经存储在ADC_DR寄存器中的数据。在使能DMA模式的情况下,每完成规则通道组中的一个通道转换后,都会生成一个DMA请求,便可将转换的数据从ADC_DR寄存器传输到用指定的目标内存位置。这样取代单通道实验使用中断服务的读取方法,可以实现多通道ADC应用中高速高效的采集。

软件设计要点

跟单通道例程一样,编写两个ADC驱动文件,bsp_adc.h和bsp_adc.c,用来存放ADC所用IO引脚的初始化函数以及ADC和DMA相关配置函数,主要流程为:

(1)初始化配置ADC目标引脚为模拟输入模式;

(2)使能ADC时钟和DMA时钟;

(3)配置DMA从ADC数据寄存器传输数据到指定的存储区;

(4)配置通用ADC为独立模式;

(5)设置ADC为12位分辨率,启动扫描,连续转换,不需要外部触发;

(6)设置ADC转换通道顺序及采样时间;

(7)使能DMA请求,DMA在AD转换完自动传输数据到指定的存储区;

(8)启动ADC模块;

(9)软件使能触发ADC转换。

这里需要注意的是,在使用ADC+DMA功能时,如果在启动ADC转换之后使能DMA,ADC采样数据可能会出现异常。因此建议先配置ADC及DMA相关参数,最后启动ADC转换。

代码实现

受篇幅限制,这里只介绍核心的部分代码,有些变量的设置,头文件的包含等并没有涉及到,完整的代码请参考本课程配套的例程。相关核心代码实现如下:

(1)ADC宏定义

#define TEMP_NOFCHANEL      3
/*=====================通道1 IO======================*/
// PB0 ADC IO宏定义,可用杜邦线接 3V3 或者 GND 来实验
#define TEMP_ADC_GPIO_PORT1    GPIOB
#define TEMP_ADC_GPIO_PIN1     GPIO_Pin_0
#define TEMP_ADC_GPIO_CLK1     RCC_AHB1Periph_GPIOB
#define TEMP_ADC_CHANNEL1      ADC_Channel_8
/*=====================通道2 IO ======================*/
// PB1 ADC IO宏定义,可用杜邦线接 3V3 或者 GND 来实验
#define TEMP_ADC_GPIO_PORT2    GPIOB
#define TEMP_ADC_GPIO_PIN2     GPIO_Pin_1
#define TEMP_ADC_GPIO_CLK2     RCC_AHB1Periph_GPIOB
#define TEMP_ADC_CHANNEL2      ADC_Channel_9
/*=====================通道3 IO ======================*/
// PA6 ADC IO宏定义,可用杜邦线接 3V3 或者 GND 来实验
#define TEMP_ADC_GPIO_PORT3    GPIOA
#define TEMP_ADC_GPIO_PIN3     GPIO_Pin_6
#define TEMP_ADC_GPIO_CLK3     RCC_AHB1Periph_GPIOA
#define TEMP_ADC_CHANNEL3     ADC_Channel_6
// ADC 序号宏定义
#define TEMP_ADC              ADC1
#define TEMP_ADC_CLK          RCC_APB2Periph_ADC1
// ADC DR寄存器宏定义,ADC转换后的数字值则存放在这里
#define TEMP_ADC_DR_ADDR    ((u32)ADC1+0x4c)
// ADC DMA 通道宏定义,使用DMA传输
#define TEMP_ADC_DMA_CLK      RCC_AHB1Periph_DMA2
#define TEMP_ADC_DMA_CHANNEL  DMA_Channel_0
#define TEMP_ADC_DMA_STREAM   DMA2_Stream0

定义多个通道进行多通道ADC实验,并且定义DMA相关配置。

(2)ADC GPIO初始化

static void Temp_ADC_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    /*=====================通道1======================*/
    RCC_AHB1PeriphClockCmd(TEMP_ADC_GPIO_CLK1,ENABLE); // 使能 GPIO 时钟
    GPIO_InitStructure.GPIO_Pin = TEMP_ADC_GPIO_PIN1; // 配置 IO
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;     //不上拉不下拉
    GPIO_Init(TEMP_ADC_GPIO_PORT1, &GPIO_InitStructure);
    /*=====================通道2======================*/
    RCC_AHB1PeriphClockCmd(TEMP_ADC_GPIO_CLK2,ENABLE); // 使能 GPIO 时钟
    GPIO_InitStructure.GPIO_Pin = TEMP_ADC_GPIO_PIN2; // 配置 IO
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;     //不上拉不下拉
    GPIO_Init(TEMP_ADC_GPIO_PORT2, &GPIO_InitStructure);
    /*=====================通道3=======================*/
    RCC_AHB1PeriphClockCmd(TEMP_ADC_GPIO_CLK3,ENABLE); // 使能 GPIO 时钟
    GPIO_InitStructure.GPIO_Pin = TEMP_ADC_GPIO_PIN3;  // 配置 IO
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;   //不上拉不下拉
    GPIO_Init(TEMP_ADC_GPIO_PORT3, &GPIO_InitStructure);
}

使用到GPIO时候都必须开启对应的GPIO时钟,GPIO用于AD转换功能必须配置为模拟输入模式。

(3)配置ADC工作模式

static void Temp_ADC_Mode_Config(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;
    ADC_CommonInitTypeDef ADC_CommonInitStructure;     
    // --------------DMA Init 结构体参数初始化-------------     
    // ADC1使用DMA2,数据流0,通道0,
    RCC_AHB1PeriphClockCmd(TEMP_ADC_DMA_CLK, ENABLE);   // 开启DMA时钟
    DMA_InitStructure.DMA_PeripheralBaseAddr = TEMP_ADC_DR_ADDR; // 外设基址为:ADC 数据寄存器地址
    DMA_InitStructure.DMA_Memory0BaseAddr = (u32)ADC_ConvertedValue;  // AD值存储地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;   // 数据传输方向为外设到存储器
    DMA_InitStructure.DMA_BufferSize = TEMP_NOFCHANEL; // 缓冲区大小,指一次传输的数据量
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;// 外设寄存器只有一个,地址不递增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;   // 存储器地址固定
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  // 外设数据大小为半字
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;   // 存储器数据大小也为半字
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环传输模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;// DMA传输通道优先级为高
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;    // 禁止DMA FIFO ,使用直连模式
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; // FIFO大小,FIFO禁止时不用配置
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;  
    DMA_InitStructure.DMA_Channel = TEMP_ADC_DMA_CHANNEL; // 选择 DMA 通道,通道存在于流中
    DMA_Init(TEMP_ADC_DMA_STREAM, &DMA_InitStructure);//初始化DMA流,
    DMA_Cmd(TEMP_ADC_DMA_STREAM, ENABLE); // 使能DMA流

    RCC_APB2PeriphClockCmd(TEMP_ADC_CLK , ENABLE);// 开启ADC时钟     
    // -------------ADC Common 结构体 参数初始化----------------
    ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;// 独立ADC模式
    ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;// 时钟为fpclk x分频
    ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; // 禁止DMA直接访问模式     
    ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;  // 采样时间间隔
    ADC_CommonInit(&ADC_CommonInitStructure);     
    // -------------------ADC Init 结构体 参数初始化--------------------------
    ADC_StructInit(&ADC_InitStructure);
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;// ADC 分辨率
    ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 扫描模式,多通道采集需要
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  // 连续转换
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//禁止外部边沿触发
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;  //外部触发通道,使用软件触发时此值随便赋值即可
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐ADC_InitStructure.ADC_NbrOfConversion = TEMP_NOFCHANEL;      //转换通道3个                             
    ADC_Init(TEMP_ADC, &ADC_InitStructure);

    
  // 配置 ADC 通道转换顺序和采样时间周期
  ADC_RegularChannelConfig(TEMP_ADC, TEMP_ADC_CHANNEL1, 1, ADC_SampleTime_3Cycles);
  ADC_RegularChannelConfig(TEMP_ADC, TEMP_ADC_CHANNEL2, 2, ADC_SampleTime_3Cycles);
  ADC_RegularChannelConfig(TEMP_ADC, TEMP_ADC_CHANNEL3, 3, ADC_SampleTime_3Cycles);

    ADC_DMARequestAfterLastTransferCmd(TEMP_ADC, ENABLE); // 使能DMA请求      
    ADC_DMACmd(TEMP_ADC, ENABLE);// 使能ADC DMA
    ADC_Cmd(TEMP_ADC, ENABLE);  // 使能ADC
    ADC_SoftwareStartConv(TEMP_ADC);//开始ADC转换,软件触发
}

首先,使用DMA_InitTypeDef定义了DMA初始化类型变量,另外使用ADC_InitTypeDef和ADC_CommonInitTypeDef结构体分别定义一个ADC初始化和ADC通用类型变量。

调用RCC_APB2PeriphClockCmd()开启ADC时钟以及RCC_AHB1PeriphClockCmd()开启DMA时钟。

对DMA进行必要的配置。首先设置外设基地址就是ADC的规则数据寄存器地址;存储器的地址就是指定的数据存储区空间,ADC_ConvertedValue是我们定义的一个全局数组名,它是一个无符号16位含有3个元素的整数数组;ADC规则转换对应只有一个数据寄存器,所以地址不能递增,而定义的存储区是专门用来存放不同通道数据的,所以需要自动地址递增。ADC的规则数据寄存器只有低16位有效,实际存放的数据只有12位而已,所以设置数据大小为半字大小。ADC配置为连续转换模式,DMA也设置为循环传输模式。设置好DMA相关参数后就使能DMA的ADC通道。

接下来使用ADC_CommonInitTypeDef结构体变量ADC_CommonInitStructure来配置ADC为独立模式、分频系数4、20个周期的采样延迟,并调用ADC_CommonInit函数完成ADC通用工作环境配置。

使用ADC_InitTypeDef结构体变量ADC_InitStructure来配置ADC1为12位分辨率、使能扫描模式、启动连续转换、使用内部软件触发无需外部触发事件、使用右对齐数据格式、转换通道为3,并调用ADC_Init函数完成ADC1工作环境配置。

ADC_RegularChannelConfifig函数用来绑定ADC通道转换顺序和采样时间。分别绑定3个ADC通道引脚并设置相应的转换顺序。

ADC_DMARequestAfterLastTransferCmd函数控制是否使能ADC的DMA请求,如果使能请求,并调用ADC_DMACmd函数使能DMA,则在ADC转换完成后就请求DMA实现数据传输。ADC_Cmd函数控制ADC转换启动和停止。

最后使用软件触发调用ADC_SoftwareStartConvCmd函数进行使能配置。

(4)Main程序

/**  主函数  */
int main(void)
{
    Debug_USART_Config();
    Temp_Init();
    while (1)
    {
        ADC_ConvertedValueLocal[0] =(float) ADC_ConvertedValue[0]/4096*(float)3.3;
        ADC_ConvertedValueLocal[1] =(float) ADC_ConvertedValue[1]/4096*(float)3.3;
        ADC_ConvertedValueLocal[2] =(float) ADC_ConvertedValue[2]/4096*(float)3.3;
        printf("\r\n PB0 value = %f V \r\n",ADC_ConvertedValueLocal[0]);
        printf("\r\n PB1 value = %f V \r\n",ADC_ConvertedValueLocal[1]);
        printf("\r\n PA6 value = %f V \r\n",ADC_ConvertedValueLocal[2]);
        Delay(0xffffff);  
    }
}

主函数先调用Debug_USART_Config函数配置调试串口相关参数,接下来调用Temp_Init函数进行ADC初始化配置并启动ADC。配置了DMA数据传输,它会自动把ADC转换完成后数据保存到数组ADC_ConvertedValue内,我们只要使用数组就可以了。经过简单地计算就可以得到每个通道对应的实际电压。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 11

1、MM32F0160的ADC简介

ADC外设是12位的逐次逼近型(SAR)模拟数字转换器,可以将模拟信号转换成数字信号。

ADC有16 个通道可测量内部或外部信号源,其中ADC有14 路外部输入通道和2路内部通道,如下图1 ADC的系统框图所示分别是通道14内部温度温度传感器通道,通道15内部电压传感器通道,即内部1.2V参考电压通道。

ADC的通道可以单次、单周期和连续进行转换。根据不同的方式又可以选择普通通道转换、任意通道转换、注入通道转换。ADC 的输入时钟不得超过16M,他是由 APB2 时钟(PCLK2) 分频产生。

1.png

图1 ADC系统框图

2、MM32F0160内部1.2V参考电压简介

MM32系列MCU出厂时,在Flash特定的内存地址存储了1.2V参考电压的校准值,即VDDA为3V3时采样1.2V电压的校准值。该校准值可以通过UM手册查阅。例如,可通过UM_MM32F0160_SC手册的ADC章节查阅到MM32F0163D7P的ADC内部1.2V参考电压的校准值存储在Flash特定的内存地址0x1FFFF7E0区域。根据本文章节1图1的ADC系统框图可知ADC内部1.2V参考电压通道为通道15。MM32其它系列的MCU的ADC内部1.2V参考电压校准值在Flash内存的存储地址,内部1.2V参考电压通道的通道号如下表1所示(注:其它MCU系列请参考UM手册)。

2.png

表1 MM32系列ADC内部1.2V参考电压校准值的存储地址和1.2V参考电压通道号

3、ADC内部1.2V参考电压的应用场景举例

ADC的参考电压为VDD,当MCU的供电VDD不稳定或采用电池供电时ADC的参考电压会随着VDD的波动而波动,随着电池电量和电压的下降而变化,从而影响ADC的采集和测量精度,这种场景就可以考虑使用MCU出厂时VDD为3.3V时ADC采样1.2V电压得到的采样校准值,读取该采样校准值并转换成电压值,作为ADC的间接参考电压使用。

4、ADC内部1.2V参考电压采样值的读取

以MM32F0163D7P为例,查阅UM_MM32F0160_SC手册ADC章节的ADC系统框图可知ADC内部1.2V参考电压通道为通道15(VSENSOR),在读取ADC内部1.2V参考电压采样值前需配置ADC通道15并使能该通道,使用任意通道配置核心代码如下所示,本文后面8.3章节有详细的应用举例。

/* Sampling value of each channel of ADC */
uint16_t ADC_Channel_Samp_Value[4] = {0x00};
/* Assign ADC1 1.2V Vref channel_15 to RANK 3 */   
ADC_ANY_CH_Config(ADC1,3,ADC_Channel_VoltReference);
/* Enable ADC 1.2V channel_15 voltage reference */
ADC1->ADCFG |=  ADC_ADCFG_VSEN;
/* Get the sampling value of ADC 1.2V channel 15 */    
ADC_Channel_Samp_Value[3] = ADC1->ADDR15;

5、ADC内部1.2V参考电压校准值的读取

以MM32F0163D7P为例,读取出厂VDDA为3V3时采样1.2V电压的采样校准值,并换算成电压值,核心代码如下所示。本文8.4章节有详细的读取举例。

/* 1.2V sampling calibration value storage address */
#define ADC_1_2V_SAMPLE_CALI_VALUE_MEM_ADDR 0x1FFFF7E0
/* Read ADC 1.2V Sample value */
Vref_1_2V_Cali = *(uint16_t*)(ADC_1_2V_SAMPLE_CALI_VALUE_MEM_ADDR);
/* Convert to voltage value */
Vref_1_2V_Cali_V = (float)(Vref_1_2V_Cali * (3.3/4096));

6、ADC各通道采样电压值与1.2V通道采样电压值换算关系

定义Calcu_ADC_Channel_V为缓存ADC各通道采样电压值,gADC_Sample_Value为缓存ADC各通道的采样值。ADC_Channel_Samp_Value[4]为缓存ADC各通道的采样值包括ADC的1.2V通道实时采样值,Vref_1_2V_Cali为VDDA为3V3时采样1.2V电压的采样校准值,Vref_1_2V_Cali由以上章节5 ADC内部1.2V参考电压校准值的读取得到,换算成电压值为Vref_1_2V_Cali_V = (float)(Vref_1_2V_Cali * (3.3/4096)) ,则存在以下关系式:

Calcu_ADC_Channel_V / gADC_Sample_Value = Vref_1_2V_Cali_V /ADC_Channel_Samp_Value[i]

其中i表示ADC各通道号包括ADC的1.2V通道。

由此,推得ADC各个通道的采样电压值为

Calcu_ADC_Channel_V = (float)(gADC_Sample_Value * (Vref_1_2V_Cali_V/ADC_Channel_Samp_Value[3]))

ADC_Channel_Samp_Value[3]缓存ADC的1.2V通道的实时采样值。即:ADC各通道的采样电压值=ADC各通道的采样值*(ADC内部采样1.2V电压的采样校准值换算得到的电压值 / ADC的1.2V通道的实时采样值)。

7、根据出厂ADC内部1.2V参考电压的校准值反推VDDA的电压值

由于Vref_1_2V_Cali / ADC_V1_2V_Channel_Sample = VDDA / 4096

得到VDDA = ( Vref_1_2V_Cali * 4096 ) / ADC_V1_2_Channel_Sample

注意事项:读取ADC_V1_2_Channel_Sample 采样值前需配置和使能1.2V通道(MM32F0163D7P的ADC 1.2V通道为通道15)。注:本文章节4有提到配置方法。

8、ADC使用注意事项

1)ADC高达1Msps转换速率,每个通道可以独立的设置采样保持时间,但需注意的是采样保持时间最小为2.5个ADC时钟周期,最大为240.5个ADC时钟周期,在应用场景条件允许情况下,在此范围内适当加大采样保持时间可以保证ADC采样精度。条件允许情况下适当降低ADC时钟频率也可以保证采样精度,但ADC的时钟不得超过16MHz。

2)ADC的内部1.2V Vref通道转换周期比规则转换通道的转换周期稍大一些,如果对ADC的采样速率有应用场景的要求,建议使用ADC的注入通道功能。

3)ADC的采样值受到输入阻抗的影响,关于ADC输入阻抗的计算可参考对应MCU系列的DS手册,电气特性,工作条件的ADC特性。

4)使用ADC时应注意用作ADC功能的GPIO的IO类型,例如IO类型为“TC”类型即标准的IO类型,不容忍VDD电压即ADC的输入采样电压不得超过VDD电源电压。此外GPIO内部带有上下拉钳位二极管,用户应避免输入ADC的采样电压过高引起倒灌影响ADC的工作条件和采样精度(关于IO类型的说明请参考对应MCU系列的DS手册管脚定义章节)。

9、ADC内部1.2V参考电压的使用完整举例

1)以MM32F0163D7P为例,查阅DS手册得到GPIO与ADC通道对应关系如下表2所示。

3.png表2 GPIO与ADC通道对应关系

2)以MM32F0163D7P为例,完整举例ADC内部1.2V参考电压的使用,本示例外接采样3路ADC电压值,首先初始化3路ADC通道,ADC输入的GPIO的分别为PA0对应ADC_Channel_0,PA1对应ADC_Channel_1,PA2对应ADC_Channel_2,ADC的GPIO初始化代码如下所示。

void ADC_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    /* Enable GPIOA Clock */
    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
    GPIO_StructInit(&GPIO_InitStruct);
    /* PA0 ADC_CH0, PA1 ADC_CH1, PA2 ADC_CH2 */
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; 
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;    
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}

3)以MM32F0163D7P为例,初始化ADC为12bit精度,时钟为72M预分频为16分频,连续转换模式,数据格式右对齐,采样保持时间为240.5个ADC时钟周期,配置任意通道模式,配置并使能ADC内部1.2V通道Channel15,ADC的初始化代码如下所示。

void ADC_Configure(void)
{
    ADC_InitTypeDef ADC_InitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2ENR_ADC, ENABLE);    /* Enable ADC clock */
    ADC_StructInit(&ADC_InitStruct);
    ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;
    ADC_InitStruct.ADC_PRESCARE   = ADC_PCLK2_PRESCARE_16;
    /* ADC continue scan convert mode */
    ADC_InitStruct.ADC_Mode = ADC_Mode_Continue;   
/* AD data right-justified */       
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; 
    ADC_Init(ADC1, &ADC_InitStruct);
    /* Configure ADC ADC_Channel_x Sample time */
    ADC_Channel_Sample_time_Configure(ADC1,ADC_Channel_0,ADC_Samctl_240_5); 
    ADC_Channel_Sample_time_Configure(ADC1,ADC_Channel_1,ADC_Samctl_240_5);
    ADC_Channel_Sample_time_Configure(ADC1,ADC_Channel_2,ADC_Samctl_240_5);
    ADC_ANY_Cmd(ADC1, DISABLE); /* Disable ADC ANYChannel */   
    ADC_ANY_NUM_Config(ADC1,3); /* Configure Multi-Channel num */ 
    /* Assign PA0 ADC1 channel_0 to RANK 0 */     
    ADC_ANY_CH_Config(ADC1,0,ADC_Channel_0); 
    /* Assign PA1 ADC1 channel_1 to RANK 1 */ 
    ADC_ANY_CH_Config(ADC1,1,ADC_Channel_1); 
    /* Assign PA2 ADC1 channel_2 to RANK 2 */  
    ADC_ANY_CH_Config(ADC1,2,ADC_Channel_2); 
    /* Assign ADC1 1.2V Vref channel_15 to RANK 3 */   
    ADC_ANY_CH_Config(ADC1,3,ADC_Channel_VoltReference); 
    ADC_ANY_Cmd(ADC1, ENABLE);   /* Enable ADC ANYChannel */   
    /* Enable ADC 1.2V channel_15 voltage reference */
    ADC1->ADCFG |=  ADC_ADCFG_VSEN; 
    ADC_Cmd(ADC1, ENABLE); /* Enable ADC */
}

4)以MM32F0163D7P为例,读取出厂VDDA为3V3时采样1.2V电压的采样校准值,并换算成电压值,代码如下所示。

#define VDDA        3.3F
#define ADC_12BIT   4096U
#define ADC_RESOLUTION VDDA / ADC_12BIT
/* 1.2V sampling calibration value storage address */
#define ADC_1_2V_SAMPLE_CALI_VALUE_MEM_ADDR 0x1FFFF7E0
/* 1.2V sampling calibration value */
uint16_t Vref_1_2V_Cali  = 0;
/* 1.2V sampling calibration value Convert to voltage value */
float Vref_1_2V_Cali_V = 0.0;
void Read_Factory_1_2V_Sample_CaliValue(void)
{
    /* Read ADC 1.2V Sample value */
    Vref_1_2V_Cali = *(uint16_t*)(ADC_1_2V_SAMPLE_CALI_VALUE_MEM_ADDR);
    /* Convert to voltage value */
    Vref_1_2V_Cali_V = (float)(Vref_1_2V_Cali * ADC_RESOLUTION);
}

5)以MM32F0163D7P为例,由以上9.4章节读取出厂VDDA为3V3时采样1.2V电压的采样校准值,由以上9.3章节ADC内部1.2V参考电压通道的配置和使能,ADC的1.2V采样电压的校准值根据以上章节6表述的公式,结合ADC的1.2V通道15的实时采样值可用于反推VDDA的电压值,同时该采样校准值换算成电压值后可间接作为ADC多通道连续采样的参考电压。ADC内部1.2V参考电压实时采样值,ADC各通道采样值的读取并换算成电压值以及在main函数初始化UART1打印功能,ADC外设的初始化,读取ADC采样1.2V的校准值,在while(1)主循环中调用获取ADC各通道采样电压值函数的详细实现的代码如下所示。本示例通过UART1每隔200ms毫秒打印输出ADC各通道采样并换算得到电压值到串口调试助手。

/* Buffer the VDDA voltage value of the reversed ADC */
float ADC_VDDA = 0.0;
/* Sampling value of each channel of ADC */
uint16_t ADC_Channel_Samp_Value[4] = {0x00};
/* Buffer the sampling value of each channel of the ADC */
uint16_t gADC_Sample_Value = 0;
/* Buffer the voltage value of each channel of ADC */
float Calcu_ADC_Channel_V = 0.0;
void Calcu_ADC_Each_Channel_Voltage_Value(void)
{
    uint8_t i = 0;
    /* Software starts the ADC conversion */
    ADC_SoftwareStartConvCmd(ADC1, ENABLE); 
    /* ADC conversion flag */
    while(ADC_GetFlagStatus(ADC1, ADC_IT_EOC) == 0);
    /* Clears the adc's pending flags */
    ADC_ClearFlag(ADC1, ADC_IT_EOC);
    /* Get the sampling value of ADC PA0 channel 0 */
    ADC_Channel_Samp_Value[0] = ADC1->ADDR0;
    /* Get the sampling value of ADC PA1 channel 1 */
    ADC_Channel_Samp_Value[1] = ADC1->ADDR1;
    /* Get the sampling value of ADC PA2 channel 2 */
    ADC_Channel_Samp_Value[2] = ADC1->ADDR2;   
    /* Get the sampling value of ADC 1.2V channel 15 */    
    ADC_Channel_Samp_Value[3] = ADC1->ADDR15;               
    /* The factory 1.2V sampling calibration value and the 1.2V channel sampling value reverse the VDDA voltage value */
    ADC_VDDA = (Vref_1_2V_Cali_V *4096) / ADC_Channel_Samp_Value[3];
    printf("ADC_VDDA = %0.2f\r\n",ADC_VDDA);
    /* Print out the voltage value of each channel calculated by ADC */
    for(i = 0; i < 4; i++)
    {
        /* Obtain the sampling value of each channel of the ADC */
        gADC_Sample_Value = ADC_Channel_Samp_Value[i];      
        /* Calculate the voltage value of each channel according to the sampling value of each channel of the ADC */
        Calcu_ADC_Channel_V = (float)(gADC_Sample_Value * (Vref_1_2V_Cali_V/ADC_Channel_Samp_Value[3]));
        if(i == 3)
        {         
            printf("ADC1_CH15_VREF: %0.2fV\r\n",Calcu_ADC_Channel_V); 
        }
        else
        {
            printf("ADC1_CH%d: %0.2fV\r\n",i,Calcu_ADC_Channel_V);  
        }               
    } 
}


int main(void)
{
    /* SysTick Init */
    DELAY_Init();
    /* UART1 Init */
    CONSOLE_Init(115200);
    /* ADC Pin Configure */
    ADC_GPIO_Configure();
    /* ADC function Configure */
    ADC_Configure();
    /* Read factory 1.2V sample calibration value and convert to voltage value */
    Read_Factory_1_2V_Sample_CaliValue();
    while (1)
    {
        if(Tick_200ms_Flag == true)
        {
            Tick_200ms_Flag = false;
            /* Get and Calculate the voltage value of each channel of the ADC */
            Calcu_ADC_Each_Channel_Voltage_Value();
        }
    }
}

6)编译程序把烧录程序到核心板,如下图2所示,3路ADC分别采集外接的电压值,ADC各通道采样换算得到的电压值打印输出到串口调试助手,测试结果分别为ADC_VDDA该值是通过读取出厂VDDA为3V3时采样1.2V电压的采样校准值结合1.2V通道15的实时采样值反推得到的VDDA(即VDD的电压值)的电压值。ADC_CH0采集1.0V电压,实际采到1.03V,ADC_CH1接GND采集0V电压,实际采集到0V,ADC_CH2采集0.50V电压,实际采集到0.50V。其中ADC_CH15_VREF采集ADC内部1.2V通道实时采样得到的电压值,采集到1.18V,该值可参考以上9.4章节表述的出厂VDDA为3V3时采样1.2V电压的采样校准值换算得到的电压值接近。

4.png

图2

7)如下图3所示,换一组电压采集,ADC_CH0采集2.50V电压实际采集到2.51V,ADC_CH1采集3.32V电源电压实际采集到3.31V,ADC_CH2采集1.50V电压,实际采到1.50V电压。以上测试均在ADC允许的误差范围。

5.png

图3

来源:灵动MM32MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 33

页面

订阅 RSS - ADC