stm32模数转换器 (ADC)
嵌入式老杨 2026-01-14


STM32F4xx 的 ADC 是连接模拟与数字世界的关键模块,具备多通道、高分辨率等特性,支持多种工作模式,可满足传感器数据采集等场景需求。本文围绕其配置与应用展开,助于掌握相关开发要点。


01


stm32 ADC简介


ADC(Analog-to-Digital Converter,模数转换器)是一种把连续变化的模拟电信号转换成离散数字量的电路或模块。它以“采样-保持-量化-编码”为核心流程,输出一串可供数字系统(MCU、DSP、CPU)处理的二进制数值。ADC 是物理世界与数字世界的“翻译官”,没有它,智能设备就成了“聋子”和“瞎子”。


1.控制器简介
• 3 组独立 12-bit 逐次逼近型 ADC(ADC1/2/3);
• 可独立运行,也可双/三重模式并联,提高采样率或实现交错采样;
• 每组含 16 个外部通道 + 3 个内部通道(温度、VREFINT、VBAT,仅 ADC1 可用), 最多 19 路复用通道;
• 通道分两类:规则通道(最多 16 路,主循环)和注入通道(最多 4 路,中断级)。


2.电气特性
• 供电:2.4 V – 3.6 V(全速) / 1.8 V – 3.6 V(低速);
• 输入范围:VREF- ≤ VIN ≤ VREF+(VREF- 通常接 VSSA);
• 分辨率:12/10/8/6 bit 可配;
• 最高转换速率:2.4 MSPS(ADCCLK ≤ 36 MHz,采样周期=3 时,转换时间≈0.41 µs);
• 采样保持电容:≈4 pF;外部输入阻抗需 ≤ 50 kΩ(保证精度)。


3.典型应用场景
• 多路电池/传感器电压巡检
• 高速示波器/音频采样(DMA + 双/三重模式)
• 温度、VBAT、内部参考电压监控
• 电机控制中电流环同步采样(双 ADC 同时触发)


02


ADC功能框图


STM32F4xx 的 ADC可将外部模拟信号转换为数字信号。以下结合其参考手册,对单个 ADC 框图各模块功能、ADC 工作原理,以及相关寄存器进行介绍

1.单个 ADC 框图


①模拟输入相关模块

GPIO 端口与模拟复用器

ADC 可以通过 GPIO 端口连接外部模拟信号源。GPIO 端口的引脚可配置为模拟输入模式,连接到模拟复用器。模拟复用器能够选择多达 16 个外部模拟输入通道(ADCx_IN0 - ADCx_IN15),此外还可以连接内部的温度传感器、VREFINT 以及 VBAT 信号。

对于 STM32F40x 和 STM32F41x 器件,温度传感器内部连接到通道 ADC1_IN16。内部参考电压 VREFINT 连接到 ADC1_IN17。

对于 STM23F42x 和 STM32F43x 器件,温度传感器内部连接到与 VBAT 共用的通道ADC1_IN18。一次只能选择一个转换(温度传感器或 VBAT)。同时设置了温度传感器和 VBAT 转换时,将只进行 VBAT 转换。内部参考电压 VREFINT 连接到 ADC1_IN17。

VBAT通道连接到通道 ADC1_IN18。该通道也可转换为注入通道或规则通道。

以STM32F40x 为例,ADC的各个通道IO引脚实际如下表所示:

通道

ADC1 GPIO/信号

ADC2 GPIO/信号

ADC3 GPIO/信号

IN0

PA0

PA0

PA0

IN1

PA1

PA1

PA1

IN2

PA2

PA2

PA2

IN3

PA3

PA3

PA3

IN4

PA4

PA4

PF6

IN5

PA5

PA5

PF7

IN6

PA6

PA6

PF8

IN7

PA7

PA7

PF9

IN8

PB0

PB0

PF10

IN9

PB1

PB1

PF3

IN10

PC0

PC0

未引出

IN11

PC1

PC1

未引出

IN12

PC2

PC2

未引出

IN13

PC3

PC3

未引出

IN14

PC4

PC4

PF4

IN15

PC5

PC5

PF5

IN16

温度传感器

内部接地,不可用

内部接地,不可用

IN17

VREFINT

内部接地,不可用

内部接地,不可用

IN18

VBAT

内部接地,不可用

内部接地,不可用

相关寄存器:GPIO 相关的配置寄存器(如 GPIOx_MODER 等)用于设置引脚为模拟输入模式;ADC 本身的通道选择配置通过规则序列寄存器(ADC_SQR3、ADC_SQR2、ADC_SQR1)和注入序列寄存器(ADC_JSQR)来实现。


VREF+、VREF-、VDDA、VSSA

VREF+ 和 VREF- 是 ADC 的参考电压正负极,为 ADC 转换提供电压基准,决定了 ADC 能够转换的模拟电压范围(通常 VREF- 接地,VREF+ 接稳定的参考电压,如 3.3V 时,输入电压范围为 0 - 3.3V);VDDA 是芯片模拟电源,VSSA 是芯片模拟地,为 ADC 模块提供稳定的电源。

相关寄存器:这些引脚的连接和配置在硬件设计阶段确定,在软件层面没有直接配置的寄存器,但 ADC 的转换结果会依赖于参考电压的稳定性。


②通道分组与转换模块

规则通道

规则通道最多支持 16 个通道,可以按照设定的顺序进行转换。在转换过程中,通道的转换顺序和数量由规则序列寄存器(ADC_SQR3、ADC_SQR2、ADC_SQR1)来设置。转换完成后,数据会存储在规则数据寄存器(ADC_DR)中。

相关寄存器:ADC_SQR1 的 L [3:0] 位决定要转换的通道数量,SQ1 - SQ16 位用于配置每个通道的转换顺序;ADC_DR 用于存放转换后的结果,数据格式可以通过 ADC_CR2 的 ALIGN 位设置为左对齐或右对齐。


注入通道

注入通道最多支持 4 个通道,具有更高的优先级。当注入通道触发转换时,会打断规则通道的转换,优先完成注入通道的转换,然后再恢复规则通道的转换。注入通道转换后的数据存储在注入数据寄存器(ADC_JDRx,x = 1 - 4)中。

相关寄存器:ADC_JSQR 的 JL [1:0] 位决定注入通道的数量,JSQR4 - JSQR1 位用于配置注入通道的转换顺序;ADC_JDRx 用于存放注入通道转换后的结果,数据格式同样受 ADC_CR2 的 ALIGN 位控制。


③触发源模块

EXTSEL [3:0] 和 JEXTSEL [3:0]

EXTSEL [3:0] 用于选择规则通道的外部触发源,JEXTSEL [3:0] 用于选择注入通道的外部触发源。触发源可以是定时器的触发信号(如 TIM1_CH1、TIM2_TRGO 等),也可以是外部中断线(EXTI_15、EXTI_11 等)。

相关寄存器:ADC_CR2 中的 EXTSEL [3:0] 和 JEXTSEL [3:0] 位用于设置触发源选择;EXTEN [1:0] 和 JEXTEN [1:0] 位用于设置触发极性(禁止触发检测、上升沿检测、下降沿检测、上升沿和下降沿均检测)。


软件触发

除了外部触发,还可以通过软件触发 ADC 转换。向 ADC_CR2 的 ADON 位写 1 启动 ADC,写 0 停止 ADC;写 1 到 ADC_CR2 的 SWSTART 位可以启动规则通道转换,写 1 到 ADC_CR2 的 JSWSTART 位可以启动注入通道转换。

相关寄存器:ADC_CR2 中的 ADON、SWSTART、JSWSTART 位用于软件触发控制。

外部触发极性设置方法如下表所示:


规则通道的触发源选择方法如下表所示:


注入通道的触发源选择方法如下表所示:


④转换时间相关模块

ADCCLK(ADC 时钟)

ADCCLK 是 ADC 进行转换的时钟信号,由 PCLK2 经过分频产生。ADC 的转换时间由采样时间和转换周期组成,ADCCLK 的频率会影响转换速度。

相关寄存器:通过 ADC 公共控制寄存器(ADC_CCR)的 ADCPRE [1:0] 位设置分频系数,可选 2、4、6 和 8 分频,从而调整 ADCCLK 的频率。


采样时间设置

ADC 需要一定的时间对输入模拟信号进行采样,采样时间通过 ADC 采样时间寄存器(ADC_SMPR1 和 ADC_SMPR2)来设置。不同通道可以设置不同的采样时间,以适应不同的信号特性。

相关寄存器:ADC_SMPR2 控制通道 0 - 9 的采样时间,ADC_SMPR1 控制通道 10 - 17 的采样时间,通过 SMP [2:0] 位设置采样周期数。


转换时间计算方法

STM32F4xx 的 ADC 属于逐次逼近型,总转换时间 Tconv 由两段相加:Tconv = Tsampling + Tconvert。

Tsampling(采样保持时间) 由 ADC_SMPRx 寄存器中的 SMP[2:0] 决定,可选 3/15/28/56/84/112/144/480 个 ADCCLK 周期。

Tconvert(逐次逼近时间)固定为 12.5 个 ADCCLK 周期(12 位分辨率时,如果设置为 10/8/6 位,则Tconvert分别为 10.5 / 8.5 / 6.5 个ADCCLK 周期)。

举例:

设置ADCCLK = 21 MHz(APB2=84 MHz,预分频 4);ADC_SMPR1. SMP[2:0] 设置为001B,则Tsampling=15 个 ADCCLK 周期;设置ADC_CR1.RES[1:0]设置为00B(12 位分辨率),则Tconvert=12.5 个 ADCCLK 周期;

转换时间Tconv = Tsampling + Tconvert=(12.5+15)/ 21 MHz ≈ 1.31 µs。


⑤中断与标志模块

中断使能位与标志位

包括转换结束标志(EOC)、注入转换结束标志(JEOC)、模拟看门狗事件标志(AWD)、DMA 溢出标志(OVR)等。中断使能位(EOCIE、JEOCIE、AWDIE、OVRIE)用于使能对应的中断,当标志位被置位且中断使能时,会向 NVIC(嵌套向量中断控制器)发送中断请求,通知 MCU 进行相应处理。

相关寄存器:ADC_ISR 寄存器用于读取这些标志位,ADC_IER 寄存器用于设置中断使能。


模拟看门狗

模拟看门狗可以监控 ADC 转换的输入电压,当输入电压超出设定的阈值上限或低于阈值下限时,会触发模拟看门狗事件,置位 AWD 标志,并根据使能情况产生中断。

相关寄存器:通过 ADC_CR1 的 AWDEN 位使能模拟看门狗,ADC_LTR 和 ADC_HTR 寄存器分别用于设置阈值下限和上限。


2.多重 ADC 框图


①模拟输入前端

GPIO 端口

作为模拟信号输入接口,可接入外部模拟信号(ADCx_IN0~IN15 )、内部信号(温度传感器、VREFINTVBAT )。需通过 GPIO 配置寄存器(如 GPIOx_MODER ),将引脚设为模拟输入模式,断开数字功能,避免干扰。ADC的各个通道IO引脚同单个 ADC GPIO 端口。


多路选择器

按配置选通不同通道信号,送至 ADC 转换器。规则 / 注入通道的选通逻辑,由规则序列寄存器(ADC_SQR 系列)、注入序列寄存器(ADC_JSQR )控制,决定通道转换顺序、数量。


②主 / 从 ADC 核心( ADC1永远为主,ADC2/3只能为从)

ADC1(主)

具备完整规则 / 注入通道逻辑,可独立或主导多 ADC 协同工作。规则通道用于常规多通道轮询,注入通道支持 “插队” 优先转换。转换后数据存入规则数据寄存器(16 位,ADC_DR注入数据寄存器(4×16 位,ADC_JDRx


ADC2/3(从)

功能与主 ADC 类似,但多 ADC 模式下,需由主 ADC 或统一触发源协调。从 ADC 转换数据同样存入自身 ADC_DRADC_JDRx ,或在双 / 三重模式下,按需共享到通用规则数据寄存器(32 位,ADC_CDR


③多 ADC 模式控制(通用部分)

双 / 三重模式控制

通过 ADC_CCR(ADC 公共控制寄存器)配置,如 MULTI[5:0] 位,设定多 ADC 协同模式(同步触发、交叉采样等 )。可实现多 ADC 同时采样同一信号(提升精度)、分时采样不同信号(增加通道数 )。


通用规则数据寄存器(ADC_CDR

双 / 三重模式下,存储多 ADC 规则通道转换结果(如 ADC1、ADC2 结果合并存储 ),便于批量读取。


④触发与同步逻辑

启动触发复用(规则组 / 注入组)

规则组触发源可来自定时器(TIMx_CHTRGO )、外部中断(EXTI_11/15 )等,由 ADC_CR2EXTSEL[3:0] 选择;注入组触发由 JEXTSEL[3:0] 配置。多 ADC 模式下,主 ADC 可作为触发源,同步启动从 ADC 转换。


内部触发链路

主 ADC 转换事件可作为从 ADC 的触发信号,通过 ADC_CCR 配置触发关联,实现多 ADC 同步采样 / 转换。


⑤数据存储与输出

规则 / 注入数据寄存器

单 ADC 独立工作时,转换结果存入自身 ADC_DR(规则)、ADC_JDRx(注入);多 ADC 协同模式下,可按需将结果合并到 ADC_CDR ,或通过 DMA 直接传输到内存,减少 CPU 干预。


03


ADC模式介绍


1.ADC常见模式

ADC 配置参数及选项如下:

工作模式:决定有几个 ADC 同时工作。独立模式 / 双重 ADC 模式 / 三重 ADC 模式(3 种);

通道数量:一次转换序列里包含 1 个还是多个通道。单通道 / 多通道(2 种);

DMA 使用:转换结果是否由 DMA 自动搬运。使用 DMA / 不使用 DMA(2 种);

通道类型:数据放到哪个寄存器。规则通道 / 注入通道 / 规则 + 注入通道(3 种);

扫描方式:转换完成后停止(单次)或持续循环(连续)。单次扫描 / 连续扫描(2 种);

触发方式:谁来下达“开始转换”命令。软件触发 / 外部触发(2 种)。


以下是几种常见模式的配置流程及关键寄存器设置:

序号

工作模式

通道数量

DMA 使用

通道类型

扫描方式

触发方式

1

独立模式

单通道

无 DMA

规则通道

单次扫描

软件触发

2

独立模式

单通道

无 DMA

规则通道

连续扫描

软件触发

3

独立模式

单通道

无 DMA

注入通道

单次扫描

软件触发

4

独立模式

多通道

无 DMA

规则通道

单次扫描

软件触发

5

独立模式

多通道

有 DMA

规则通道

连续扫描

软件触发

6

独立模式

多通道

无 DMA

规则 + 注入

连续扫描

外部触发

7

双重 ADC

多通道

无 DMA

规则通道

单次扫描

软件触发

8

三重 ADC

多通道

有 DMA

规则通道

连续扫描

外部触发

注:实际有效组合约 30 余种,表格仅展示部分典型组合。


2.配置流程及寄存器设置

① 独立模式 - 单规则通道 - 单次扫描 - 软件触发(无 DMA)

  • 使能ADC时钟:RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

  • 配置ADC_CR1:
    分辨率:ADC1->CR1 &= ~ADC_CR1_RES; // 12位
    扫描模式:ADC1->CR1 &= ~ADC_CR1_SCAN; // 单通道禁用扫描

  • 配置ADC_CR2:
    连续转换:ADC1->CR2 &= ~ADC_CR2_CONT; // 单次转换
    触发方式:ADC1->CR2 |= ADC_CR2_EXTSEL_0; // 软件触发
    使能ADC:ADC1->CR2 |= ADC_CR2_ADON;

  • 配置规则序列:
    通道数:ADC1->SQR1 &= ~ADC_SQR1_L; // 1个转换
    通道:ADC1->SQR3 = (0 << 0); // 通道0(PA0)

  • 启动转换:ADC1->CR2 |= ADC_CR2_SWSTART;

  • 等待EOC标志:while(!(ADC1->SR & ADC_SR_EOC));

  • 读取数据:uint16_t value = ADC1->DR。


②独立模式 - 单规则通道 - 连续扫描 - 软件触发(无 DMA)

  • 使能ADC时钟:RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

  • 配置ADC_CR1:
    分辨率:ADC1->CR1 &= ~ADC_CR1_RES; // 12位
    扫描模式:ADC1->CR1 &= ~ADC_CR1_SCAN; // 单通道

  • 配置ADC_CR2:
    连续转换:ADC1->CR2 |= ADC_CR2_CONT; // 连续模式
    触发方式:ADC1->CR2 |= ADC_CR2_EXTSEL_0; // 软件触发
    使能ADC:ADC1->CR2 |= ADC_CR2_ADON;

  • 配置规则序列:
    通道数:ADC1->SQR1 &= ~ADC_SQR1_L; // 1个转换
    通道:ADC1->SQR3 = (0 << 0); // 通道0(PA0)

  • 启动转换:ADC1->CR2 |= ADC_CR2_SWSTART;

  • 循环读取数据:
    while(1) {
    if (ADC1->SR & ADC_SR_EOC) {
    uint16_t value = ADC1->DR;
    // 处理数据
    }
    }


③独立模式 - 单注入通道 - 单次扫描 - 软件触发(无 DMA)

  • 使能ADC时钟:RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

  • 配置ADC_CR1:
    分辨率:ADC1->CR1 &= ~ADC_CR1_RES; // 12位

  • 配置ADC_CR2:
    注入触发:ADC1->CR2 |= ADC_CR2_JEXTSEL_0; // 软件触发
    注入自动触发:ADC1->CR2 &= ~ADC_CR2_JAUTO; // 禁用自动触发
    使能ADC:ADC1->CR2 |= ADC_CR2_ADON;

  • 配置注入序列:
    通道数:ADC1->JSQR &= ~ADC_JSQR_JL; // 1个注入通道
    通道:ADC1->JSQR = (18 << 0); // 通道18(VBAT)

  • 使能注入转换完成中断:
    ADC1->CR1 |= ADC_CR1_JEOCIE;
    NVIC_EnableIRQ(ADC_IRQn);

  • 触发注入转换:ADC1->CR2 |= ADC_CR2_JSWSTART;
    /* 中断处理函数 */
    void ADC_IRQHandler(void) {
    if (ADC1->SR & ADC_SR_JEOC) {
    uint16_t vbat_value = ADC1->JDR1;
    ADC1->SR &= ~ADC_SR_JEOC; // 清除标志
    }
    }


④独立模式 - 多规则通道 - 单次扫描 - 软件触发(无 DMA)

  • 使能ADC时钟:RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

  • 配置ADC_CR1:
    分辨率:ADC1->CR1 &= ~ADC_CR1_RES; // 12位
    扫描模式:ADC1->CR1 |= ADC_CR1_SCAN; // 多通道启用扫描

  • 配置ADC_CR2:
    连续转换:ADC1->CR2 &= ~ADC_CR2_CONT; // 单次转换
    触发方式:ADC1->CR2 |= ADC_CR2_EXTSEL_0; // 软件触发
    使能ADC:ADC1->CR2 |= ADC_CR2_ADON;

  • 配置规则序列(假设3个通道):
    通道数:ADC1->SQR1 |= (2 << 20); // 3个转换
    通道顺序:
    ADC1->SQR3 = (0 << 0) | (1 << 5); // 通道0、1
    ADC1->SQR2 = (2 << 0); // 通道2

  • 启动转换:ADC1->CR2 |= ADC_CR2_SWSTART;

  • 循环读取所有通道数据:
    for (int i = 0; i < 3; i++) {
    while(!(ADC1->SR & ADC_SR_EOC));
    uint16_t value = ADC1->DR;
    // 处理数据
    }


⑤独立模式 - 多规则通道 - 连续扫描 - DMA(定时器触发)

  • 使能ADC、DMA和定时器时钟:
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;

  • 配置ADC_CR1:
    分辨率:ADC1->CR1 &= ~ADC_CR1_RES; // 12位
    扫描模式:ADC1->CR1 |= ADC_CR1_SCAN; // 多通道

  • 配置ADC_CR2:
    连续转换:ADC1->CR2 |= ADC_CR2_CONT; // 连续模式
    DMA模式:ADC1->CR2 |= ADC_CR2_DMA | ADC_CR2_DDS; // 启用DMA循环
    触发方式:ADC1->CR2 |= ADC_CR2_EXTSEL_2 | ADC_CR2_EXTEN_0; // TIM2_TRGO上升沿触发
    使能ADC:ADC1->CR2 |= ADC_CR2_ADON;

  • 配置规则序列(假设4个通道):
    通道数:ADC1->SQR1 |= (3 << 20); // 4个转换
    通道顺序:
    ADC1->SQR3 = (0 << 0) | (1 << 5) | (2 << 10); // 通道0、1、2
    ADC1->SQR2 = (3 << 0); // 通道3

  • 配置DMA2 Stream0:
    DMA2_Stream0->CR |= (0 << 25);// 通道0
    DMA2_Stream0->CR |= DMA_SxCR_DIR_0;// 外设到内存
    DMA2_Stream0->CR |= DMA_SxCR_CIRC;// 循环模式
    DMA2_Stream0->CR |= DMA_SxCR_MINC;// 内存递增
    DMA2_Stream0->NDTR = 4;// 传输4个数据
    DMA2_Stream0->PAR = (uint32_t)&ADC1->DR;// 外设地址
    DMA2_Stream0->M0AR = (uint32_t)adc_values;// 内存地址
    DMA2_Stream0->CR |= DMA_SxCR_EN;// 启用DMA

  • 配置定时器2(1kHz触发):
    TIM2->PSC = 83; // 84MHz / 84 = 1MHz
    TIM2->ARR = 999; // 1MHz / 1000 = 1kHz
    TIM2->CR2 |= TIM_CR2_MMS_1; // 主模式选择:更新事件
    TIM2->CR1 |= TIM_CR1_CEN; // 启用定时器


⑥独立模式 - 规则 + 注入通道 - 连续扫描(无 DMA)

  • 使能ADC时钟:RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

  • 配置ADC_CR1:
    分辨率:ADC1->CR1 &= ~ADC_CR1_RES; // 12位
    扫描模式:ADC1->CR1 |= ADC_CR1_SCAN; // 多通道

  • 配置ADC_CR2:
    连续转换:ADC1->CR2 |= ADC_CR2_CONT; // 规则通道连续转换
    注入触发:ADC1->CR2 |= ADC_CR2_JEXTSEL_2; // TIM3_TRGO触发
    注入自动触发:ADC1->CR2 |= ADC_CR2_JAUTO; // 自动注入
    使能ADC:ADC1->CR2 |= ADC_CR2_ADON;

  • 配置规则序列(假设2个通道):
    通道数:ADC1->SQR1 |= (1 << 20); // 2个转换
    通道顺序:
    ADC1->SQR3 = (0 << 0) | (1 << 5); // 通道0、1

  • 配置注入序列(假设1个通道):
    通道数:ADC1->JSQR &= ~ADC_JSQR_JL; // 1个注入通道
    通道:ADC1->JSQR = (16 << 0); // 通道16(温度传感器)

  • 使能注入转换完成中断:
    ADC1->CR1 |= ADC_CR1_JEOCIE;
    NVIC_EnableIRQ(ADC_IRQn);

  • 配置定时器3(用于触发注入转换):
    TIM3->PSC = 83;
    TIM3->ARR = 9999; // 约840Hz
    TIM3->CR2 |= TIM_CR2_MMS_1; // 主模式选择:更新事件
    TIM3->CR1 |= TIM_CR1_CEN; // 启用定时器


⑦双重 ADC 模式 - 规则通道 - 交替采样(无 DMA)

  • 使能ADC1和ADC2时钟:
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_ADC2EN;

  • 配置ADC_CCR(公共控制寄存器):
    ADC->CCR |= ADC_CCR_MULTI_0 | ADC_CCR_MULTI_1; // 双重ADC交替模式

  • 配置ADC1_CR1(主ADC):
    分辨率:ADC1->CR1 &= ~ADC_CR1_RES; // 12位
    扫描模式:ADC1->CR1 &= ~ADC_CR1_SCAN; // 单通道

  • 配置ADC1_CR2:
    连续转换:ADC1->CR2 |= ADC_CR2_CONT; // 连续模式
    触发方式:ADC1->CR2 |= ADC_CR2_EXTSEL_0; // 软件触发
    使能ADC:ADC1->CR2 |= ADC_CR2_ADON;

  • 配置ADC2_CR2(从ADC):
    使能ADC2:ADC2->CR2 |= ADC_CR2_ADON;

  • 配置规则序列(主ADC):
    通道数:ADC1->SQR1 &= ~ADC_SQR1_L; // 1个转换
    通道:ADC1->SQR3 = (0 << 0); // 通道0(PA0)

  • 配置ADC2规则序列(与ADC1相同):
    通道数:ADC2->SQR1 &= ~ADC_SQR1_L;
    通道:ADC2->SQR3 = (0 << 0);

  • 启动转换(仅需启动主ADC):
    ADC1->CR2 |= ADC_CR2_SWSTART;

  • 读取组合结果(32位数据):
    uint32_t combined_value = ADC1->DR;
    uint16_t adc1_value = combined_value & 0xFFFF;
    uint16_t adc2_value = (combined_value >> 16) & 0xFFFF;


⑧三重 ADC 模式 - 规则通道 - 交替采样 - DMA(外部触发)

  • 使能ADC1、ADC2、ADC3和DMA时钟:
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_ADC2EN | RCC_APB2ENR_ADC3EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;

  • 配置ADC_CCR(公共控制寄存器):
    ADC->CCR |= ADC_CCR_MULTI_0 | ADC_CCR_MULTI_1 | ADC_CCR_MULTI_2; // 三重ADC交替模式

  • 配置ADC1_CR1(主ADC):
    分辨率:ADC1->CR1 &= ~ADC_CR1_RES; // 12位
    扫描模式:ADC1->CR1 &= ~ADC_CR1_SCAN; // 单通道

  • 配置ADC1_CR2:
    连续转换:ADC1->CR2 |= ADC_CR2_CONT; // 连续模式
    触发方式:ADC1->CR2 |= ADC_CR2_EXTSEL_1 | ADC_CR2_EXTEN_0; // TIM8_TRGO上升沿触发
    DMA模式:ADC1->CR2 |= ADC_CR2_DMA | ADC_CR2_DDS; // 启用DMA循环
    使能ADC:ADC1->CR2 |= ADC_CR2_ADON;

  • 配置ADC2和ADC3(从ADC):
    使能ADC2和ADC3:
    ADC2->CR2 |= ADC_CR2_ADON;
    ADC3->CR2 |= ADC_CR2_ADON;

  • 配置规则序列(所有ADC相同):
    通道数:ADC1->SQR1 &= ~ADC_SQR1_L; // 1个转换
    通道:ADC1->SQR3 = (0 << 0); // 通道0(PA0)
    同样配置ADC2和ADC3的SQR3

  • 配置DMA2 Stream0:
    DMA2_Stream0->CR |= (0 << 25);// 通道0
    DMA2_Stream0->CR |= DMA_SxCR_DIR_0;// 外设到内存
    DMA2_Stream0->CR |= DMA_SxCR_CIRC;// 循环模式
    DMA2_Stream0->CR |= DMA_SxCR_MINC;// 内存递增
    DMA2_Stream0->NDTR = 3;// 传输3个数据(每个ADC一个)
    DMA2_Stream0->PAR = (uint32_t)&ADC1->DR;// 外设地址
    DMA2_Stream0->M0AR = (uint32_t)adc_values;// 内存地址
    DMA2_Stream0->CR |= DMA_SxCR_EN;// 启用DMA

  • 配置定时器8(高速触发):
    TIM8->PSC = 0; // 84MHz
    TIM8->ARR = 41; // 约2MHz采样率
    TIM8->CR2 |= TIM_CR2_MMS_1; // 主模式选择:更新事件
    TIM8->CR1 |= TIM_CR1_CEN; // 启用定时器

04


硬件说明


手动调节电位器旋钮,产生0~3.3V电压到VR,VR连接到stm32 PB0(ADC12_IN8)引脚。配置ADC采集VR的电压,通过串口打印采集的电压数据,使用串口调试助手观察。

05


程序设计案例


本次 STM32f4xx ADC 程序配置的模式为:独立模式、单通道、使用 DMA、规则通道、连续扫描软件触发,具体实现如下。

编程步骤:

1)初始 ADC 用到的 GPIO;

2)设置 ADC 的工作参数并初始化;

3)设置 ADC 工作时钟;

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

5)配置使能 ADC 转换完成中断,在中断内读取转换完数据;

6)使能 ADC;

7)使能软件触发 ADC 转换。

main.c

int main(void){  /* 配置系统时钟为168 MHz */ SystemClock_Config();  /* 初始化USART1 配置模式为 115200 8-N-1 */ DEBUG_USART_Config();  Rheostat_Init(); while (1) { ADC_Vol =(float) ADC_ConvertedValue/4096*(float)3.3; // 读取转换的AD值 printf("\r\n The current AD value = 0x%04X \r\n", ADC_ConvertedValue);  printf("\r\n The current AD value = %f V \r\n",ADC_Vol);  Delay(0x8fffff);  } }


bsp_adc.c

static void Rheostat_ADC_GPIO_Config(void){ GPIO_InitTypeDef GPIO_InitStructure; // 使能 GPIO 时钟 RHEOSTAT_ADC_GPIO_CLK_ENABLE(); // 配置 IO GPIO_InitStructure.Pin = RHEOSTAT_ADC_GPIO_PIN; GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;  GPIO_InitStructure.Pull = GPIO_NOPULL ; //不上拉不下拉 HAL_GPIO_Init(RHEOSTAT_ADC_GPIO_PORT, &GPIO_InitStructure); } static void Rheostat_ADC_Mode_Config(void){  // ------------------DMA Init 结构体参数 初始化-------------------------- // ADC1使用DMA2,数据流0,通道0,这个是手册固定死的 // 开启DMA时钟 RHEOSTAT_ADC_DMA_CLK_ENABLE(); // 数据传输通道 DMA_Init_Handle.Instance = RHEOSTAT_ADC_DMA_STREAM; // 数据传输方向为外设到存储器  DMA_Init_Handle.Init.Direction = DMA_PERIPH_TO_MEMORY;  // 外设寄存器只有一个,地址不用递增 DMA_Init_Handle.Init.PeriphInc = DMA_PINC_DISABLE; // 存储器地址固定 DMA_Init_Handle.Init.MemInc = DMA_MINC_ENABLE;  // // 外设数据大小为半字,即两个字节  DMA_Init_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //  存储器数据大小也为半字,跟外设数据大小相同 DMA_Init_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;  // 循环传输模式 DMA_Init_Handle.Init.Mode = DMA_CIRCULAR; // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响 DMA_Init_Handle.Init.Priority = DMA_PRIORITY_HIGH; // 禁止DMA FIFO   ,使用直连模式 DMA_Init_Handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;  // FIFO 大小,FIFO模式禁止时,这个不用配置  DMA_Init_Handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL; DMA_Init_Handle.Init.MemBurst = DMA_MBURST_SINGLE; DMA_Init_Handle.Init.PeriphBurst = DMA_PBURST_SINGLE;  // 选择 DMA 通道,通道存在于流中 DMA_Init_Handle.Init.Channel = RHEOSTAT_ADC_DMA_CHANNEL;  //初始化DMA流,流相当于一个大的管道,管道里面有很多通道 HAL_DMA_Init(&DMA_Init_Handle);   HAL_DMA_Start (&DMA_Init_Handle,RHEOSTAT_ADC_DR_ADDR,(uint32_t)&ADC_ConvertedValue,1);  // 开启ADC时钟 RHEOSTAT_ADC_CLK_ENABLE(); // -------------------ADC Init 结构体 参数 初始化------------------------ // ADC1 ADC_Handle.Instance = RHEOSTAT_ADC; // 时钟为fpclk 4分频  ADC_Handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4; // ADC 分辨率 ADC_Handle.Init.Resolution = ADC_RESOLUTION_12B; // 禁止扫描模式,多通道采集才需要  ADC_Handle.Init.ScanConvMode = DISABLE;  // 连续转换  ADC_Handle.Init.ContinuousConvMode = ENABLE; // 非连续转换  ADC_Handle.Init.DiscontinuousConvMode = DISABLE; // 非连续转换个数 ADC_Handle.Init.NbrOfDiscConversion   = 0; //禁止外部边沿触发  ADC_Handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //使用软件触发,外部触发不用配置,注释掉即可 //ADC_Handle.Init.ExternalTrigConv      = ADC_EXTERNALTRIGCONV_T1_CC1; //数据右对齐  ADC_Handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; //转换通道 1个 ADC_Handle.Init.NbrOfConversion = 1; //使能连续转换请求 ADC_Handle.Init.DMAContinuousRequests = ENABLE; //转换完成标志 ADC_Handle.Init.EOCSelection          = DISABLE;  // 初始化ADC  HAL_ADC_Init(&ADC_Handle); //--------------------------------------------------------------------------- ADC_Config.Channel      = RHEOSTAT_ADC_CHANNEL; ADC_Config.Rank         = 1; // 采样时间间隔  ADC_Config.SamplingTime = ADC_SAMPLETIME_56CYCLES; ADC_Config.Offset       = 0; // 配置 ADC 通道转换顺序为1,第一个转换,采样时间为3个时钟周期 HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);  HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t*)&ADC_ConvertedValue, 1);}


06


实验结果


通过调节电位器旋钮,每个1秒返回ADC测量的电压值,可以看到电压从0V左右缓慢上升到3.3V左右。


07


代码链接


工程代码链接:

https://gitee.com/ylm1101111/stm32_basic9.git


声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。 微信联系小助理
0
评论
  • 相关技术文库
  • 单片机
  • 嵌入式
  • MCU
  • STM
下载排行榜
更多
评测报告
更多
广告