1 ADC简介
1.1 是什么
ADC,即模数转换器(Analog-to-Digital Converter),是一种电子设备或模块,用于将连续的模拟信号转换为离散的数字信号。这种转换使得微控制器或数字系统能够处理和分析来自外部世界的模拟信号。
1.2 工作原理
ADC的工作原理基于采样和量化两个过程:
-
采样(Sampling):模拟信号在一定时间间隔内被测量或采样。这意味着模拟信号在时间上被离散化,以便后续转换处理。
-
量化(Quantization):采样后的模拟信号的振幅被转换为相应的离散数字值。这是ADC的核心功能,通过将连续的模拟信号转换为数字表示,可以对其进行数字处理和存储。
1.3 组成
ADC通常由以下几个部分组成:
-
1.采样保持电路(Sample-and-Hold Circuit):用于在ADC转换期间保持模拟信号的稳定性,以确保准确的转换。
-
2.比较器(Comparator):用于比较采样信号与参考电压,确定其相对大小。
-
3.控制逻辑(Control Logic):管理ADC的各个部分,并确保转换按照预期进行。
-
4.数字转换器(Digital Converter):执行模拟信号到数字信号的转换。
ADC有不同的类型和规格,包括分辨率、采样率、参考电压等。这些规格决定了ADC的性能和适用范围。ADC在各种领域广泛应用,包括传感器接口、音频处理、医疗设备、通信系统等。
STM32F103系列拥有3个ADC模块,每个模块具备12位的精度,并且最多支持16个外部通道。ADC1和ADC2各自拥有16个外部通道,而ADC3则根据CPU引脚的不同而提供不同数量的通道,通常为8个外部通道。
2 ADC架构分析
上图为ADC功能框图,主要可以分为电压输入、输入通道、转换顺序、触发源、转换时间、数据寄存器与中断这七个部分。
《STM32F10X-中文参考手册》图24
2.1 电压输入
决定输入电压的引脚:VREF+、VREF-、VDDA、VSSA。
一般VREF+与VDDA接3.3V,VREF-与VSSA接地。
- VDDA:模拟电源的电压
- VSSA:模拟地
ADC输入范围为:VREF- ≤ VIN ≤ VREF+ 。(0~3.3V)
超出0~3.3V的电压测试方法:
在外部硬件电路上,把高于3.3V的电压降低,把低于0V的电压抬高。
上图中的电感为600H。
对于上面的这个电路图,ADC可以测量-10V~10V。(图中的四个电阻阻值可以扩大10倍)
2.2 输入通道
这些引脚可以在 《STM32F103xCDE_数据手册-英文》 -> 3.Pinouts and pin descriptions -> Table 5. High-density STM32F103xx pin definitions 中查找
2.2.1 输入通道分类
外部的 16 个通道在转换的时候又分为规则通道和注入通道,其中规则通道最多有 16路,注入通道最多有 4 路。
-
规则通道:是ADC进行模拟信号到数字信号转换的基本方式,是最常用的通道类型。
-
注入通道:是一种特殊的通道类型,它允许在规则通道转换过程中插入额外的转换,通常用于需要快速响应的场合。
- 注入通道只有在规则通道存在时才会出现。
规则通道与注入通道的区别:
-
1.转换序列:规则通道按照预设顺序转换,注入通道则可以独立于规则通道进行转换。
-
2.触发方式:规则通道通常由软件或自动触发,注入通道则可以由软件、定时器或外部事件触发。
-
3.数据寄存器:规则通道和注入通道的数据寄存器是独立的,允许同时进行不同的数据处理。其中,规则通道只有一个16位的数据寄存器,而注入通道则有4个16位的数据寄存器。
-
4.应用场景:规则通道适合顺序采样,注入通道适合需要快速响应或中断的场合。
2.3 转换顺序
2.3.1 规则顺序
规则序列寄存器有 3 个,分别为 SQR3、 SQR2、 SQR1。
SQR3 控制着规则序列中的第一个到第六个转换,SQR2 控制着规则序列中的第七个到第十二个转换,SQR3 控制着规则序列中的第十三个到第十六个转换。
如果想要通道16第一个转换,那么在SQR1[4:0]位写16即可;如果想要通道2在第八个转换,那么在SQR8[4:0]位写2即可;具体使用多少个通道,则由SQR1寄存器的SQL[3:0]位决定,最多16个通道。
2.3.2 注入序列
这张图为JL = 4(11)时的转换顺序。
注入序列寄存器 JSQR 只有一个,最多支持 4 个通道。
具体转换多少个通道,由JL[2:0]位决定,JSQR的转换顺序跟SQR的转换顺序不一样:
-
当 JL = 00时(1个转换):转换顺序是从JSQR4[4:0]开始,而不是从JSQR1[4:0]开始;
-
当 JL < 4(11)时:第一次转换的不是JSQR1[4:0],而是JSQRx[4:0],x = 4 - JL。
-
当 JL = 4(11)时:JSQR的转换顺序跟SQR一样。
假设JL = 1,则只有JSQ4[4:0]这一个通道转换,从JSQ4[4:0]开始转换。
假设JL = 2,则JSQ4[4:0]与JSQR3[4:0]这两个通道转换,从JSQ3[4:0]开始转换。(此时JSQ3[4:0]设置的是第一个转换的通道)
假设JL = 3,则JSQ4[4:0]、JSQR3[4:0]与JSQR2[4:0]这三个通道转换,从JSQ2[4:0]开始转换。此时JSQ2[4:0]设置的是第一个转换的通道)
2.4 触发源
ADC触发源:是指启动ADC转换的信号源。
在STM32微控制器中,ADC的触发源可以分为软件触发和硬件触发两大类。
2.4.1 软件触发
-
定义:软件触发是指通过软件命令来启动ADC转换的过程。
-
触发方式:当软件设置ADC控制寄存器2(ADC_CR2)的ADON位来进行控制,写1时开始转换,写0时停止转换。
-
适用场景:适用于不需要外部事件触发,而是由程序逻辑控制转换时机的场景。
2.4.2 硬件触发
-
定义:硬件触发是指通过外部事件(如定时器、外部中断等)来启动ADC转换的过程。
-
触发源选择:触发源有很多,具体选择哪一种触发源,由 ADC 控制寄存器 2(ADC_CR2 )的 EXTSEL[2:0] 和JEXTSEL[2:0] 位来控制。
-
EXTSEL[2:0] 用于选择规则通道的触发源。
-
JEXTSEL[2:0] 用于选择注入通道的触发源。
-
-
触发源激活:触发源是否要激活,则由 ADC 控制寄存器 2(ADC_CR2) 的EXTTRIG 和 JEXTTRIG 这两位来激活。
其中 ADC3 的规则转换和注入转换的触发源与 ADC1/2的有所不同。
2.5 转换时间
ADC转换时间:是指从启动转换到ADC输出数字结果所需的时间。
-
转换时间包括了采样时间和转换时间。
-
采样时间:模拟信号在ADC内部被采样和保持的时间。
-
转换时间:ADC内部的数字转换过程所需的时间。
-
转换时间Tconv = 采样时间 + 12.5个周期
12.5个周期是采集12位AD时间,这部分时间是固定的,不随采样时间的变化而变化。
2.5.1 ADC时钟
ADC 输入时钟 ADC_CLK 由 PCLK2 经过分频产生,最大是 14M,分频因子由 RCC 时钟配置寄存器 RCC_CFGR 的位 15:14 ADCPRE[1:0] 设置,可以是 2/4/6/8 分频,一般设置PCLK2=HCLK=72M。
这里具体可以看一下RCC复位和时钟控制器。
2.5.2 采样时间
由ADC采样时间寄存器ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0] 位设置。
-
ADC_SMPR2 控制的是通道 0~9。
-
ADC_SMPR1 控制的是通道 10~17。
-
每个通道可以单独配置。
2.5.3 转换时间的计算示例
假设系统时钟位72MHz,ADCCLK位12MHz,采样时间为1.5个周期,那么:
转换时间 Tconv = 采样时间 + 12.5个周期 = 14 / 12 us = 1.167us
2.6 数据寄存器
在STM32微控制器中,ADC数据寄存器用于存储ADC转换的结果。根据转换的通道类型(规则通道或注入通道),数据寄存器可以分为规则数据寄存器和注入数据寄存器。
2.6.1 规则数据寄存器(ADC_DR)
-
概念:规则通道寄存器通常是一个32位的寄存器,只有一个。
-
存储位置:低16位在独立模式下(单独使用ADC1、ADC2或ADC3的时候)使用;高16位在双模式下(ADC1和ADC2同时使用)保存ADC2转换的规则数据,此时低16位用于保存ADC1转换的规则数据。
-
数据对齐:由于ADC的精度是12位,无论放在ADC_DR的高16位或低16位都放不满,只能左对齐或右对齐,具体是采样哪种对齐方式,则取决于ADC_CR2寄存器中的ALIGN位。
-
数据读取:在规则通道转换完成后,可以通过读取规则数据寄存器来获取转换结果。
如果使用多通道转换那转换的数据就全部都挤在了 ADC_DR寄存器 里面,前一个时间点转换的通道数据,就会被下一个时间点的另外一个通道转换的数据覆盖掉,所以当通道转换完成后就应该把数据取走,或者开启 DMA 模式,把数据传输到内存里面,不然就会造成数据的覆盖。
多通道采集最好使用DMA进行数据传输:定义一个16位的数组,这个数组有三个成员,先把数据放到ADC_DR寄存器低16位,通过DMA把数据搬运到数组中,去数组中取值。
注:ADC1与ADC3能够使用DMA功能,ADC2不具有DMA功能。
如果速度不够快的话,可以采样中断的形式,这种方式使用的比较少。
2.6.2 注入数据寄存器(ADC_JDRx)
-
概念:注入通道寄存器有四个,分别对应注入通道1到4。每个注入数据寄存器都是32位的,用于存储注入通道转换后的数据。
-
存储位置:低 16 位有效,高 16 位保留。
-
数据对齐:注入数据寄存器中的数据同样可以左对齐或右对齐,这取决于ADC控制寄存器(ADC_CR2)中的ALIGN位的设置。
-
数据读取:在注入通道转换完成后,可以通过读取相应的注入数据寄存器来获取转换结果。
2.7 中断
数据转换结束后,可以产生中断,中断分为三种:规则通道转换结束中断、注入转换通道转换结束中断与模拟看门狗中断。
2.7.1 规则通道转换结束中断与注入转换通道转换结束中断
规则通道转换结束中断(EOC)与注入转换通道转换结束中断(JEOC)跟我们平时接触的中断一样,有相应的中断标志位和中断使能位,我们还可以根据中断类型写相应配套的中断服务程序。
2.7.2 模拟看门狗中断(AWD)
- 模拟看门狗中断可以监控通道数据的上限与下限。
当被ADC转换的模拟电压低于低阈值或高于高阈值时,就会产生中断。(需要开启模拟看门狗中断)
低阈值由ADC_LTR设置,高阈值由ADC_HTR设置。
2.8 总结
-
ADC决定模拟电压的输入量,就是输入范围(默认0~3.3V,具体的输入范围需要根据对应的芯片来确定)。
-
决定外部输入电压之后,电压从通道GPIO输入。其中通道又分成规则通道与注入通道。
- 规则数据寄存器(ADC_SQR),注入序列寄存器(ADC_JSQR):决定我们要使用多少个通道,还有通道转换顺序。
-
开始转换之前,打开输入转换器的电压(ADON)。
-
选择触发源:软件、定时器或外部GPIO。
-
开始转换,转换时间由ADCCLK相关(最高14M,通常配置成12M)。
-
转换的数据放到数据寄存器,往往要配合DMA来传输数据。
- 注入通道数据寄存器有4个,所以不存在多通道采集的时候数据覆盖的问题。
-
数据转换完成会产生中断(EOC、JEOC、AWD)。
3 电压转换
3.1 如何根据数据量算出模拟量
假设电压输入范围为0~3.3V、ADC分辨率为12位,则最小精度为3.3/2^12;
假设数字量为X,则有模拟量Y = (3.3/2^12)* X。
4 ADC初始化结构体
/**
* @brief ADC Init structure definition
*/
typedef struct
{
uint32_t ADC_Mode; /*!< Configures the ADC to operate in independent or
dual mode.
This parameter can be a value of @ref ADC_mode */
FunctionalState ADC_ScanConvMode; /*!< Specifies whether the conversion is performed in
Scan (multichannels) or Single (one channel) mode.
This parameter can be set to ENABLE or DISABLE */
FunctionalState ADC_ContinuousConvMode; /*!< Specifies whether the conversion is performed in
Continuous or Single mode.
This parameter can be set to ENABLE or DISABLE. */
uint32_t ADC_ExternalTrigConv; /*!< Defines the external trigger used to start the analog
to digital conversion of regular channels. This parameter
can be a value of @ref ADC_external_trigger_sources_for_regular_channels_conversion */
uint32_t ADC_DataAlign; /*!< Specifies whether the ADC data alignment is left or right.
This parameter can be a value of @ref ADC_data_align */
uint8_t ADC_NbrOfChannel; /*!< Specifies the number of ADC channels that will be converted
using the sequencer for regular channel group.
This parameter must range from 1 to 16. */
}ADC_InitTypeDef;
- ADC_Mode:设置 ADC 工作在独立或者双 ADC 模式。
#define ADC_Mode_Independent ((uint32_t)0x00000000) // ADC1 和 ADC2 工作在独立模式
#define ADC_Mode_RegInjecSimult ((uint32_t)0x00010000) // ADC1 和 ADC2 工作在独立模式
#define ADC_Mode_RegSimult_AlterTrig ((uint32_t)0x00020000) // ADC1 和 ADC2 工作在同步规则模式和交替触发模式
#define ADC_Mode_InjecSimult_FastInterl ((uint32_t)0x00030000) // ADC1 和 ADC2 工作在同步规则模式和快速交替模式
#define ADC_Mode_InjecSimult_SlowInterl ((uint32_t)0x00040000) // ADC1 和 ADC2 工作在同步规则模式和快速交替模式
#define ADC_Mode_InjecSimult ((uint32_t)0x00050000) // ADC1 和 ADC2 工作在同步规则模式和快速交替模式
#define ADC_Mode_RegSimult ((uint32_t)0x00060000) // ADC1 和 ADC2 工作在同步规则模式和快速交替模式
#define ADC_Mode_FastInterl ((uint32_t)0x00070000) // ADC1 和 ADC2 工作在快速交替模式
#define ADC_Mode_SlowInterl ((uint32_t)0x00080000) // ADC1 和 ADC2 工作在快速交替模式
#define ADC_Mode_AlterTrig ((uint32_t)0x00090000) // ADC1 和 ADC2 工作在交替触发模式
-
ADC_ScanConvMode: 规定了模数转换工作在扫描模式(多通道)还是单次(单通道)模式。可以设置这个参数为 ENABLE 或者 DISABLE。
-
ADC_ContinuousConvMode:规定了模数转换工作在连续还是单次模式。可以设置这个参数为 ENABLE 或者 DISABLE。
使用DISABLE 配置为单次模式,转换一次后停止,需要手动控制才重新启动转换。一般设置为连续转换。
- ADC_ExternalTrigConv:定义了使用外部触发来启动规则通道的模数转换。
#define ADC_ExternalTrigConv_T1_CC1 ((uint32_t)0x00000000) /*!< For ADC1 and ADC2 选择定时器 1 的捕获比较 1 作为转换外部触发 */
#define ADC_ExternalTrigConv_T1_CC2 ((uint32_t)0x00020000) /*!< For ADC1 and ADC2 选择定时器 1 的捕获比较 2 作为转换外部触发 */
#define ADC_ExternalTrigConv_T2_CC2 ((uint32_t)0x00060000) /*!< For ADC1 and ADC2 选择定时器 2 的捕获比较 2 作为转换外部触发 */
#define ADC_ExternalTrigConv_T3_TRGO ((uint32_t)0x00080000) /*!< For ADC1 and ADC2 选择定时器 3 的 TRGO 作为转换外部触发 */
#define ADC_ExternalTrigConv_T4_CC4 ((uint32_t)0x000A0000) /*!< For ADC1 and ADC2 选择定时器 4 的捕获比较 4 作为转换外部触发 */
#define ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO ((uint32_t)0x000C0000) /*!< For ADC1 and ADC2 选择外部中断线 11 事件或定时器 8 的TRGO作为转换外部触发 */
#define ADC_ExternalTrigConv_T1_CC3 ((uint32_t)0x00040000) /*!< For ADC1, ADC2 and ADC3 选择定时器 1 的捕获比较 3 作为转换外部触发 */
#define ADC_ExternalTrigConv_None ((uint32_t)0x000E0000) /*!< For ADC1, ADC2 and ADC3 转换由软件而不是外部触发启动 */
#define ADC_ExternalTrigConv_T3_CC1 ((uint32_t)0x00000000) /*!< For ADC3 only 选择定时器 3 的捕获比较 1 作为转换外部触发 */
#define ADC_ExternalTrigConv_T2_CC3 ((uint32_t)0x00020000) /*!< For ADC3 only 选择定时器 2 的捕获比较 3 作为转换外部触发 */
#define ADC_ExternalTrigConv_T8_CC1 ((uint32_t)0x00060000) /*!< For ADC3 only 选择定时器 8 的捕获比较 1 作为转换外部触发 */
#define ADC_ExternalTrigConv_T8_TRGO ((uint32_t)0x00080000) /*!< For ADC3 only 选择定时器 8 的 TRGO 作为转换外部触发 */
#define ADC_ExternalTrigConv_T5_CC1 ((uint32_t)0x000A0000) /*!< For ADC3 only 选择定时器 5 的捕获比较 1 作为转换外部触发 */
#define ADC_ExternalTrigConv_T5_CC3 ((uint32_t)0x000C0000) /*!< For ADC3 only 选择定时器 5 的捕获比较 3 作为转换外部触发 */
一般使用软件自动触发。
- ADC_DataAlign:规定了 ADC 数据向左边对齐还是向右边对齐。
#define ADC_DataAlign_Right ((uint32_t)0x00000000) // ADC 数据右对齐
#define ADC_DataAlign_Left ((uint32_t)0x00000800) // ADC 数据左对齐
一般选择右对齐。
- ADC_NbrOfChannel:规定了顺序进行规则转换的 ADC 通道的数目。
这个数目的取值范围是 1 到 16。
5 实验设计
5.1 硬件设计
上图为指南针开发板 ADC接口 电路图。
5.2 软件设计
在这个部分,我们进行4个任务进行设计。
-
1、独立模式单通道中断读取;
-
2、独立模式单通道DMA读取;
-
3、独立模式多通道DMA读取;
-
4、双重模式多通道规则同步。
5.2.1 独立模式单通道中断读取
编程要点:
-
1、初始化ADC用到的GPIO
-
2、初始化ADC初始化结构体
-
3、配置ADC时钟、通道的转换顺序和采样时间
-
4、使能ADC转换完成中断,配置中断优先级
-
5、使能ADC,准备开始转换
-
6、校准ADC
-
7、软件触发ADC,真正开始转换
-
8、编写中断服务函数,读取ADC转换数据
-
9、编写main函数,把转换的数据打印出来
// bsp_adc.h
#ifndef __BSP_ADC_H
#define __BSP_ADC_H
#include "stm32f10x.h"
#include <stdio.h>
// ADC编号选择
// ADC2不能使用DMA,先使用ADC2来做中断实验
#define DEBUG_ADCx ADC2
#define DEBUG_ADC_CLK RCC_APB2Periph_ADC2
// ADC GPIO 引脚宏定义
// 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响
#define DEBUG_ADC_GPIO_CLK (RCC_APB2Periph_GPIOC)
#define DEBUG_ADC_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_ADC_GPIO_PORT GPIOC
#define DEBUG_ADC_GPIO_PIN GPIO_Pin_1
// ADC 通道宏定义
#define DEBUG_ADC_CHANNEL ADC_Channel_11
// ADC 中断相关宏定义
#define DEBUG_ADC_IRQ ADC1_2_IRQn
#define DEBUG_ADC_IRQHandler ADC1_2_IRQHandler
void ADC_Config(void);
#endif /* __BSP_ADC_H */
// bsp_adc.c
#include "bsp_adc.h"
void ADC_GPIO_Config(void)
{
// 1、初始化ADC用到的GPIO
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DEBUG_ADC_GPIO_CLK, ENABLE);
// 将 GPIO配置为模拟输入模式, 必须为模拟输入
GPIO_InitStructure.GPIO_Pin = DEBUG_ADC_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(DEBUG_ADC_GPIO_PORT, &GPIO_InitStructure);
}
void ADC_Mode_Config(void)
{
// 2、初始化ADC初始化结构体
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(DEBUG_ADC_CLK, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(DEBUG_ADCx, &ADC_InitStructure);
// 3、配置ADC时钟、通道的转换顺序和采样时间
RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 72/8 = 9MHz
ADC_RegularChannelConfig(DEBUG_ADCx, DEBUG_ADC_CHANNEL, 1, ADC_SampleTime_13Cycles5);
// 4、使能ADC转换完成中断
ADC_ITConfig(DEBUG_ADCx, ADC_IT_EOC, ENABLE);
// 5、使能ADC
ADC_Cmd(DEBUG_ADCx, ENABLE);
// 6、校准ADC
ADC_StartCalibration(DEBUG_ADCx); // ADC开始校准
while(ADC_GetCalibrationStatus(DEBUG_ADCx)); // 等待校准完成
// 7、软件触发ADC,真正开始转换
ADC_SoftwareStartConvCmd(DEBUG_ADCx, ENABLE);
}
static void ADC_NVIC_Config(void)
{
// 5、配置中断优先级
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* 配置ADC为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_ADC_IRQ;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
void ADC_Config(void)
{
ADC_GPIO_Config();
ADC_Mode_Config();
ADC_NVIC_Config();
}
// stm32f10x_it.c
#include "bsp_adc.h"
__IO uint16_t ADC_ConvertedValue;
void DEBUG_ADC_IRQHandler(void)
{
if(ADC_GetITStatus(DEBUG_ADCx, ADC_IT_EOC) == SET)
{
ADC_ConvertedValue = ADC_GetConversionValue(DEBUG_ADCx);
}
ADC_ClearITPendingBit(DEBUG_ADCx, ADC_IT_EOC);
}
// main.c
#include "stm32f10x.h"
#include "bsp_adc.h"
#include "usart.h"
extern __IO uint16_t ADC_ConvertedValue;
// 用于保存转换计算后的电压值
float ADC_ConvertedValueLocal;
// 软件延时
void Delay(__IO uint32_t nCount)
{
for(; nCount != 0; nCount--);
}
int main(void)
{
USART_Config();
ADC_Config();
printf("ADC实验 —— 独立模式 - 单通道 - 中断读取");
while(1)
{
ADC_ConvertedValueLocal = (float)ADC_ConvertedValue*3.3/4096;
printf("\r\n The current AD value = 0x%04XC \r\n", ADC_ConvertedValue);
printf("\r\n The current AD value = %f V \r\n", ADC_ConvertedValueLocal);
printf("\r\n \r\n");
Delay(0xffffff);
}
}
上图为正常采样结果。
- 如果在 ADC_Mode_Config函数 中 ,配置采样时间太短的话会导致程序一直在ADC中断,执行不到printf()函数。
/* 如果把 ADC_SampleTime_13Cycles5 改成 ADC_SampleTime_1Cycles5 程序会一直在ADC中断中*/
ADC_RegularChannelConfig(DEBUG_ADCx, DEBUG_ADC_CHANNEL, 1, ADC_SampleTime_13Cycles5);
5.2.2 独立模式单通道DMA读取
编程要点:
-
1、初始化ADC、DMA用到的GPIO
-
2、初始化ADC、DMA初始化结构体
-
3、配置ADC时钟、通道的转换顺序和采样时间
-
4、使能ADC,准备开始转换
-
5、使能ADC_DMA 请求
-
6、校准ADC
-
7、软件触发ADC,真正开始转换
-
8、编写main函数,把转换的数据打印出来
// bsp_dma.h
#ifndef __BSP_DMA_H
#define __BSP_DMA_H
#include "stm32f10x.h"
#define ADC_DMA_CLK RCC_AHBPeriph_DMA1
#define ADC_DMA_CHANNEL DMA1_Channel1
void dma_config(void);
#endif /* __BSP_DMA_H */
// bsp_dma.c
#include "bsp_dma.h"
#include "bsp_adc.h"
extern __IO uint16_t ADC_ConvertedValue;
void dma_config(void)
{
DMA_InitTypeDef DMA_InitStruct;
RCC_AHBPeriphClockCmd(ADC_DMA_CLK, ENABLE);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)( &(DEBUG_ADCx->DR));
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // 外设作为数据传输的来源
DMA_InitStruct.DMA_BufferSize = 1; // 1个数据单位 数据单位等于结构中参数 DMA_PeripheralDataSize 或者参数 DMA_MemoryDataSize 的值
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStruct);
DMA_Cmd(ADC_DMA_CHANNEL, ENABLE);
}
// bsp_adc.h
#ifndef __BSP_ADC_H
#define __BSP_ADC_H
#include "stm32f10x.h"
#include <stdio.h>
// ADC编号选择
// ADC2不能使用DMA,先使用ADC2来做中断实验
#define DEBUG_ADCx ADC1
#define DEBUG_ADC_CLK RCC_APB2Periph_ADC1
// ADC GPIO 引脚宏定义
// 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响
#define DEBUG_ADC_GPIO_CLK (RCC_APB2Periph_GPIOC)
#define DEBUG_ADC_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_ADC_GPIO_PORT GPIOC
#define DEBUG_ADC_GPIO_PIN GPIO_Pin_1
// ADC 通道宏定义
#define DEBUG_ADC_CHANNEL ADC_Channel_11
void ADC_Config(void);
#endif /* __BSP_ADC_H */
// bsp_adc.c
#include "bsp_adc.h"
__IO uint16_t ADC_ConvertedValue;
void ADC_GPIO_Config(void)
{
// 1、初始化ADC用到的GPIO
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DEBUG_ADC_GPIO_CLK, ENABLE);
// 将 GPIO配置为模拟输入模式, 必须为模拟输入
GPIO_InitStructure.GPIO_Pin = DEBUG_ADC_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(DEBUG_ADC_GPIO_PORT, &GPIO_InitStructure);
}
void ADC_Mode_Config(void)
{
// 2、初始化ADC初始化结构体
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(DEBUG_ADC_CLK, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(DEBUG_ADCx, &ADC_InitStructure);
// 3、配置ADC时钟、通道的转换顺序和采样时间
RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 72/8 = 9MHz
ADC_RegularChannelConfig(DEBUG_ADCx, DEBUG_ADC_CHANNEL, 1, ADC_SampleTime_13Cycles5);
// 4、使能ADC
ADC_Cmd(DEBUG_ADCx, ENABLE);
// 5、使能ADC_DMA 请求
ADC_DMACmd(DEBUG_ADCx, ENABLE);
// 6、校准ADC
ADC_StartCalibration(DEBUG_ADCx); // ADC开始校准
while(ADC_GetCalibrationStatus(DEBUG_ADCx)); // 等待校准完成
// 7、软件触发ADC,真正开始转换
ADC_SoftwareStartConvCmd(DEBUG_ADCx, ENABLE);
}
void ADC_Config(void)
{
ADC_GPIO_Config();
ADC_Mode_Config();
}
// main.c
#include "stm32f10x.h"
#include "bsp_adc.h"
#include "usart.h"
#include "bsp_dma.h"
extern __IO uint16_t ADC_ConvertedValue;
// 用于保存转换计算后的电压值
float ADC_ConvertedValueLocal;
// 软件延时
void Delay(__IO uint32_t nCount)
{
for(; nCount != 0; nCount--);
}
int main(void)
{
USART_Config();
ADC_Config();
dma_config();
printf("ADC实验 —— 独立模式 - 单通道 - DMA读取");
while(1)
{
ADC_ConvertedValueLocal = (float)ADC_ConvertedValue*3.3/4096;
printf("\r\n The current AD value = 0x%04XC \r\n", ADC_ConvertedValue);
printf("\r\n The current AD value = %f V \r\n", ADC_ConvertedValueLocal);
printf("\r\n \r\n");
Delay(0xffffff);
}
}
- 如果想要使用DMA2的话,要将DMA通道改为通道5,ADC1改为ADC3。
如果上面的参数没有配置正确的话,在串口上打印出来的数据就有异常。
5.2.3 独立模式多通道DMA读取
原理:每个通道依次采集后,将ADC_DR寄存器中的数据放入到 ADC_ConvertedValue数组中。
// bsp_dma.h
#ifndef __BSP_DMA_H
#define __BSP_DMA_H
#include "stm32f10x.h"
#define ADC_DMA_CLK RCC_AHBPeriph_DMA1
#define ADC_DMA_CHANNEL DMA1_Channel1
void dma_config(void);
#endif /* __BSP_DMA_H */
// bsp_dma.c
#include "bsp_dma.h"
#include "bsp_adc.h"
extern __IO uint16_t ADC_ConvertedValue[NOFCHANNEL];
void dma_config(void)
{
DMA_InitTypeDef DMA_InitStruct;
RCC_AHBPeriphClockCmd(ADC_DMA_CLK, ENABLE);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)( &(DEBUG_ADCx->DR));
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // 外设作为数据传输的来源
DMA_InitStruct.DMA_BufferSize = NOFCHANNEL; // 1个数据单位 数据单位等于结构中参数 DMA_PeripheralDataSize 或者参数 DMA_MemoryDataSize 的值
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStruct);
DMA_Cmd(ADC_DMA_CHANNEL, ENABLE);
}
// bsp_adc.h
#ifndef __BSP_ADC_H
#define __BSP_ADC_H
#include "stm32f10x.h"
#include <stdio.h>
#define NOFCHANNEL 6
// ADC编号选择
// ADC2不能使用DMA,先使用ADC2来做中断实验
#define DEBUG_ADCx ADC1
#define DEBUG_ADC_CLK RCC_APB2Periph_ADC1
// ADC GPIO 引脚宏定义
// 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响
#define DEBUG_ADC_GPIO_CLK (RCC_APB2Periph_GPIOC)
#define DEBUG_ADC_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_ADC_GPIO_PORT GPIOC
#define DEBUG_ADC_GPIO_PIN1 GPIO_Pin_0
#define DEBUG_ADC_GPIO_PIN2 GPIO_Pin_1
#define DEBUG_ADC_GPIO_PIN3 GPIO_Pin_2
#define DEBUG_ADC_GPIO_PIN4 GPIO_Pin_3
#define DEBUG_ADC_GPIO_PIN5 GPIO_Pin_4
#define DEBUG_ADC_GPIO_PIN6 GPIO_Pin_5
// ADC 通道宏定义
#define DEBUG_ADC_CHANNEL1 ADC_Channel_10
#define DEBUG_ADC_CHANNEL2 ADC_Channel_11
#define DEBUG_ADC_CHANNEL3 ADC_Channel_12
#define DEBUG_ADC_CHANNEL4 ADC_Channel_13
#define DEBUG_ADC_CHANNEL5 ADC_Channel_14
#define DEBUG_ADC_CHANNEL6 ADC_Channel_15
void ADC_Config(void);
#endif /* __BSP_ADC_H */
// bsp_adc.c
#include "bsp_adc.h"
__IO uint16_t ADC_ConvertedValue[NOFCHANNEL] = {0, 0, 0, 0, 0, 0};
void ADC_GPIO_Config(void)
{
// 1、初始化ADC用到的GPIO
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DEBUG_ADC_GPIO_CLK, ENABLE);
// 将 GPIO配置为模拟输入模式, 必须为模拟输入
GPIO_InitStructure.GPIO_Pin = DEBUG_ADC_GPIO_PIN1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(DEBUG_ADC_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = DEBUG_ADC_GPIO_PIN2;
GPIO_Init(DEBUG_ADC_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = DEBUG_ADC_GPIO_PIN3;
GPIO_Init(DEBUG_ADC_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = DEBUG_ADC_GPIO_PIN4;
GPIO_Init(DEBUG_ADC_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = DEBUG_ADC_GPIO_PIN5;
GPIO_Init(DEBUG_ADC_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = DEBUG_ADC_GPIO_PIN6;
GPIO_Init(DEBUG_ADC_GPIO_PORT, &GPIO_InitStructure);
}
void ADC_Mode_Config(void)
{
// 2、初始化ADC初始化结构体
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(DEBUG_ADC_CLK, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = NOFCHANNEL;
ADC_Init(DEBUG_ADCx, &ADC_InitStructure);
// 3、配置ADC时钟、通道的转换顺序和采样时间
RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 72/8 = 9MHz
ADC_RegularChannelConfig(DEBUG_ADCx, DEBUG_ADC_CHANNEL1, 1, ADC_SampleTime_13Cycles5);
ADC_RegularChannelConfig(DEBUG_ADCx, DEBUG_ADC_CHANNEL2, 2, ADC_SampleTime_13Cycles5);
ADC_RegularChannelConfig(DEBUG_ADCx, DEBUG_ADC_CHANNEL3, 3, ADC_SampleTime_13Cycles5);
ADC_RegularChannelConfig(DEBUG_ADCx, DEBUG_ADC_CHANNEL4, 4, ADC_SampleTime_13Cycles5);
ADC_RegularChannelConfig(DEBUG_ADCx, DEBUG_ADC_CHANNEL5, 5, ADC_SampleTime_13Cycles5);
ADC_RegularChannelConfig(DEBUG_ADCx, DEBUG_ADC_CHANNEL6, 6, ADC_SampleTime_13Cycles5);
// 5、使能ADC
ADC_Cmd(DEBUG_ADCx, ENABLE);
// 使能ADC_DMA 请求
ADC_DMACmd(DEBUG_ADCx, ENABLE);
// 6、校准ADC
ADC_StartCalibration(DEBUG_ADCx); // ADC开始校准
while(ADC_GetCalibrationStatus(DEBUG_ADCx)); // 等待校准完成
// 7、软件触发ADC,真正开始转换
ADC_SoftwareStartConvCmd(DEBUG_ADCx, ENABLE);
}
void ADC_Config(void)
{
ADC_GPIO_Config();
ADC_Mode_Config();
}
// main.c
#include "stm32f10x.h"
#include "bsp_adc.h"
#include "usart.h"
#include "bsp_dma.h"
extern __IO uint16_t ADC_ConvertedValue[NOFCHANNEL];
// 用于保存转换计算后的电压值
float ADC_ConvertedValueLocal[NOFCHANNEL] = {0};
// 软件延时
void Delay(__IO uint32_t nCount)
{
for(; nCount != 0; nCount--);
}
int main(void)
{
USART_Config();
ADC_Config();
dma_config();
printf("ADC实验 —— 独立模式 - 多通道 - DMA读取");
while(1)
{
ADC_ConvertedValueLocal[0] = (float)ADC_ConvertedValue[0]*3.3/4096;
ADC_ConvertedValueLocal[1] = (float)ADC_ConvertedValue[1]*3.3/4096;
ADC_ConvertedValueLocal[2] = (float)ADC_ConvertedValue[2]*3.3/4096;
ADC_ConvertedValueLocal[3] = (float)ADC_ConvertedValue[3]*3.3/4096;
ADC_ConvertedValueLocal[4] = (float)ADC_ConvertedValue[4]*3.3/4096;
ADC_ConvertedValueLocal[5] = (float)ADC_ConvertedValue[5]*3.3/4096;
printf("\r\n The current CH0 AD value = 0x%04XC \r\n", ADC_ConvertedValue[0]);
printf("\r\n The current CH1 AD value = 0x%04XC \r\n", ADC_ConvertedValue[1]);
printf("\r\n The current CH2 AD value = 0x%04XC \r\n", ADC_ConvertedValue[2]);
printf("\r\n The current CH3 AD value = 0x%04XC \r\n", ADC_ConvertedValue[3]);
printf("\r\n The current CH4 AD value = 0x%04XC \r\n", ADC_ConvertedValue[4]);
printf("\r\n The current CH5 AD value = 0x%04XC \r\n", ADC_ConvertedValue[5]);
printf("\r\n The current CH0 AD value = %f V \r\n", ADC_ConvertedValueLocal[0]);
printf("\r\n The current CH1 AD value = %f V \r\n", ADC_ConvertedValueLocal[1]);
printf("\r\n The current CH2 AD value = %f V \r\n", ADC_ConvertedValueLocal[2]);
printf("\r\n The current CH3 AD value = %f V \r\n", ADC_ConvertedValueLocal[3]);
printf("\r\n The current CH4 AD value = %f V \r\n", ADC_ConvertedValueLocal[4]);
printf("\r\n The current CH5 AD value = %f V \r\n", ADC_ConvertedValueLocal[5]);
printf("\r\n \r\n");
Delay(0xffffff);
}
}
在 bsp_adc.h 文件中定义的引脚与通道,要与 2.2章节 输入通道 中的 ADC IO对应。
5.2.4 双重模式多通道规则同步
原理:ADC1、ADC2同时采集数据,将数据放到ADC_DR寄存器中(ADC2放在高16位,ADC1放在低16位),通过DMA将DR寄存器中的数据传输到 ADC_ConvertedValue(32位) 数组中。
// bsp_dma.h
#ifndef __BSP_DMA_H
#define __BSP_DMA_H
#include "stm32f10x.h"
#define ADCx 1
#if ADCx == 1
#define ADC_DMA_CLK RCC_AHBPeriph_DMA1
#define ADC_DMA_CHANNEL DMA1_Channel1
#elif ADCx == 3
#define ADC_DMA_CLK RCC_AHBPeriph_DMA2
#define ADC_DMA_CHANNEL DMA1_Channel5
#endif
void dma_config(void);
#endif /* __BSP_DMA_H */
// bsp_dma.c
#include "bsp_dma.h"
#include "bsp_adc.h"
extern __IO uint32_t ADC_ConvertedValue[NOFCHANNEL];
void dma_config(void)
{
DMA_InitTypeDef DMA_InitStruct;
RCC_AHBPeriphClockCmd(ADC_DMA_CLK, ENABLE);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)( &(DEBUG_ADCx_1->DR));
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // 外设作为数据传输的来源
DMA_InitStruct.DMA_BufferSize = NOFCHANNEL; // 1个数据单位 数据单位等于结构中参数 DMA_PeripheralDataSize 或者参数 DMA_MemoryDataSize 的值
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStruct);
DMA_Cmd(ADC_DMA_CHANNEL, ENABLE);
}
// bsp_adc.h
#ifndef __BSP_ADC_H
#define __BSP_ADC_H
#include "stm32f10x.h"
#include <stdio.h>
// ADC编号选择
// 双ADC模式的第一个ADC必须是ADC1
#define DEBUG_ADCx_1 ADC1
#define DEBUG_ADC_1_CLK RCC_APB2Periph_ADC1
// ADC GPIO 引脚宏定义
// 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响
#define DEBUG_ADC_1_GPIO_CLK (RCC_APB2Periph_GPIOC)
#define DEBUG_ADC_1_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_ADC_1_GPIO_PORT GPIOC
#define DEBUG_ADC_1_GPIO_PIN GPIO_Pin_1
// ADC 通道宏定义
#define DEBUG_ADC_1_CHANNEL ADC_Channel_11
// 双ADC模式的第二个ADC必须是ADC2
#define DEBUG_ADCx_2 ADC2
#define DEBUG_ADC_2_CLK RCC_APB2Periph_ADC2
// ADC GPIO 引脚宏定义
// 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响
#define DEBUG_ADC_2_GPIO_CLK (RCC_APB2Periph_GPIOC)
#define DEBUG_ADC_2_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_ADC_2_GPIO_PORT GPIOC
#define DEBUG_ADC_2_GPIO_PIN GPIO_Pin_4
// ADC 通道宏定义
#define DEBUG_ADC_2_CHANNEL ADC_Channel_14
#define NOFCHANNEL 1
void ADC_Config(void);
#endif /* __BSP_ADC_H */
// bsp_adc.c
#include "bsp_adc.h"
__IO uint32_t ADC_ConvertedValue[NOFCHANNEL] = {0};
void ADC_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DEBUG_ADC_1_GPIO_CLK, ENABLE);
RCC_APB2PeriphClockCmd(DEBUG_ADC_2_GPIO_CLK, ENABLE);
// 将 GPIO配置为模拟输入模式, 必须为模拟输入
GPIO_InitStructure.GPIO_Pin = DEBUG_ADC_1_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(DEBUG_ADC_1_GPIO_PORT, &GPIO_InitStructure);
// ADC2 GPIO 初始化
GPIO_InitStructure.GPIO_Pin = DEBUG_ADC_2_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(DEBUG_ADC_2_GPIO_PORT, &GPIO_InitStructure);
}
void ADC_Mode_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
// 初始化 ADC1
RCC_APB2PeriphClockCmd(DEBUG_ADC_1_CLK, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = NOFCHANNEL;
ADC_Init(DEBUG_ADCx_1, &ADC_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 72/8 = 9MHz
ADC_RegularChannelConfig(DEBUG_ADCx_1, DEBUG_ADC_1_CHANNEL, 1, ADC_SampleTime_55Cycles5);
ADC_Cmd(DEBUG_ADCx_1, ENABLE);
// 使能DMA ADC 请求
ADC_DMACmd(DEBUG_ADCx_1, ENABLE);
// 初始化 ADC2
RCC_APB2PeriphClockCmd(DEBUG_ADC_2_CLK, ENABLE);
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = NOFCHANNEL;
ADC_Init(DEBUG_ADCx_2, &ADC_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 72/8 = 9MHz
ADC_RegularChannelConfig(DEBUG_ADCx_2, DEBUG_ADC_2_CHANNEL, 1, ADC_SampleTime_55Cycles5);
ADC_Cmd(DEBUG_ADCx_2, ENABLE);
ADC_ResetCalibration(DEBUG_ADCx_1); // 初始化ADC 校准器
while(ADC_GetCalibrationStatus(DEBUG_ADCx_1)); // 等待校准寄存器初始化完成
ADC_StartCalibration(DEBUG_ADCx_1); // ADC开始校准
while(ADC_GetCalibrationStatus(DEBUG_ADCx_1)); // 等待校准完成
ADC_ResetCalibration(DEBUG_ADCx_2); // 初始化ADC 校准器
while(ADC_GetCalibrationStatus(DEBUG_ADCx_2)); // 等待校准寄存器初始化完成
ADC_StartCalibration(DEBUG_ADCx_2); // ADC开始校准
while(ADC_GetCalibrationStatus(DEBUG_ADCx_2)); // 等待校准完成
ADC_SoftwareStartConvCmd(DEBUG_ADCx_1, ENABLE);
// 使能 ADC2 的外部触发转换,这个触发来源于ADC1的规则组多路开关
ADC_ExternalTrigConvCmd(DEBUG_ADCx_2, ENABLE);
}
void ADC_Config(void)
{
ADC_GPIO_Config();
ADC_Mode_Config();
}
// main.c
#include "stm32f10x.h"
#include "bsp_adc.h"
#include "usart.h"
#include "bsp_dma.h"
extern __IO uint32_t ADC_ConvertedValue[NOFCHANNEL];
// 用于保存转换计算后的电压值
float ADC_ConvertedValueLocal[NOFCHANNEL*2] = {0};
// 软件延时
void Delay(__IO uint32_t nCount)
{
for(; nCount != 0; nCount--);
}
int main(void)
{
uint16_t temp0 = 0, temp1 = 0;
USART_Config();
ADC_Config();
dma_config();
printf("ADC实验 —— 双重模式 - 多通道 - 规则同步");
while(1)
{
// 取出ADC1数据寄存器的高16位,这个是ADC2的转换数据
temp0 = (ADC_ConvertedValue[0]&0XFFFF0000) >> 16;
// 取出ADC1数据寄存器的低16位,这个是ADC1的转换数据
temp1 = (ADC_ConvertedValue[0]&0XFFFF);
ADC_ConvertedValueLocal[0] = (float)temp0*3.3/4096;
ADC_ConvertedValueLocal[1] = (float)temp1*3.3/4096;
printf("\r\n The current ADC_1 AD value = 0x%04XC \r\n", temp1);
printf("\r\n The current ADC_1 AD value = %f V \r\n", ADC_ConvertedValueLocal[1]);
printf("\r\n The current ADC_2 AD value = 0x%04XC \r\n", temp0);
printf("\r\n The current ADC_2 AD value = %f V \r\n", ADC_ConvertedValueLocal[0]);
printf("\r\n \r\n");
Delay(0xffffff);
}
}