ADC部分理论知识

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-08-29 08:09:36 Views: 0 Likes: 0 Comments: 0

148.6.5 ADC部分理论知识

/* 基于 KEIL/7ADC3_DMA 工程的关键代码。
 * 注意:工程快照遗漏 ADC 时钟分频,下面 SystemClock_Config 中的 /6 是按参考手册补上的必要修正。 */
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "uart.h"

uint16_t AD_Buf[2]; // DMA 按 Rank1、Rank2 依次写入 PA1、PA2 的 12 位结果

void Init_DMA(void)
{
    __HAL_RCC_DMA2_CLK_ENABLE();
    HAL_NVIC_SetPriority(DMA2_Channel4_5_IRQn, 0, 0); // 大容量 F103 的 Ch4/5 共用向量
    HAL_NVIC_EnableIRQ(DMA2_Channel4_5_IRQn);
}

void Init_ADC3(void)
{
    ADC_ChannelConfTypeDef sConfig = {0};

    hadc3.Instance = ADC3;
    hadc3.Init.ScanConvMode = ADC_SCAN_ENABLE;
    hadc3.Init.ContinuousConvMode = ENABLE;
    hadc3.Init.DiscontinuousConvMode = DISABLE;
    hadc3.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc3.Init.NbrOfConversion = 2;
    HAL_ADC_Init(&hadc3);

    sConfig.Channel = ADC_CHANNEL_1;
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
    HAL_ADC_ConfigChannel(&hadc3, &sConfig);

    sConfig.Channel = ADC_CHANNEL_2;
    sConfig.Rank = ADC_REGULAR_RANK_2;
    HAL_ADC_ConfigChannel(&hadc3, &sConfig);
}

void SystemClock_Config(void)
{
    RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
    /* HSE x9、PCLK2=72 MHz 的其余系统时钟配置与工程快照一致。 */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6; // 72 / 6 = 12 MHz,不超过 14 MHz
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    Init_GPIO();
    Init_DMA();
    Init_UART1();
    Init_ADC3();

    HAL_ADCEx_Calibration_Start(&hadc3);
    HAL_ADC_Start_DMA(&hadc3, (uint32_t *)AD_Buf, 2);

    while (1)
    {
        printf("ADC3_DMA PA1= V:%1.2f  Value:%d\n\r", AD_Buf[0] * 3.3 / 4096.0, AD_Buf[0]);
        printf("ADC3_DMA PA2= V:%1.2f  Value:%d\n\r", AD_Buf[1] * 3.3 / 4096.0, AD_Buf[1]);
        HAL_Delay(100);
    }
}

一、dma模式的移植

本节使用 视频配套/3、STM32单片机裸机部分/code/KEIL/7ADC3_DMA,将此前 ADC-DMA 实验从 ADC1 改为 ADC3。移植的关键不是全局替换名字,而是重新核对 芯片型号是否有 ADC3、ADC3 的 DMA 固定映射、IRQ 名称和 ADC 时钟

1.1 复制工程

  • 视频从上一节工程复制出一个新工程,再命名为 7ADC3_DMA。复制工程能复用时钟、GPIO、串口和打印重定向等已验证部分,但也会继承旧外设配置,不能认为“能编译”就代表移植正确。
  • 工程名只是目录和工程文件标识,不会选择 ADC 外设;真正选择 ADC3 的位置在 hadc3.Instance = ADC3、ADC3 时钟使能、DMA 通道和主函数启动参数中。
  • 本实验必须使用具有 ADC3 和 DMA2 的 STM32F103 大容量型号,例如工程所用 STM32F103ZET6。较小容量的 F103 型号可能没有 ADC3/DMA2,不能照搬。

1.2 添加头文件

  • #include "dma.h" 让使用 Init_DMA() 的源文件看见函数声明;头文件只声明接口,不会自动产生函数实现。
  • dma.c 存放 Init_DMA() 的定义,dma.h 存放其声明。文件必须同时加入工程编译列表,否则即使包含了头文件,链接阶段仍会报“未定义的符号”。
  • 推荐的依赖关系为:main.c 包含 adc.hdma.huart.hdma.c 包含 dma.hadc.c 包含 adc.h。不要在头文件中互相无条件包含全部模块,以免形成循环依赖。

1.3 初始化dma

  • Init_DMA() 至少应完成两件事:使能 DMA2 控制器时钟 __HAL_RCC_DMA2_CLK_ENABLE(),并配置 ADC3 对应的 DMA2 Channel5。
  • DMA 配置本身位于 HAL_ADC_MspInit() 的 ADC3 分支:方向为外设到内存、ADC 数据寄存器地址不递增、数组地址递增、两端都按半字(16 位)访问、循环模式。
  • 仅仅“打开 DMA 时钟”不能传输数据;还需要 __HAL_LINKDMA(adcHandle, DMA_Handle, hdma_adc3) 把 ADC3 句柄和 DMA 句柄关联,最后由 HAL_ADC_Start_DMA() 同时启动转换和传输请求。

1.4 添加c文件

  • 新建 dma.c 后,需要在其中定义 void Init_DMA(void),并将文件加入 Keil 工程的源文件组。只在资源管理器中创建文件但未加入工程,Keil 不会编译它。
  • 本工程的 dma.c 只负责 DMA2 时钟和 NVIC;具体的 DMA2_Channel5 参数因 DMA 与 ADC3 的绑定关系在 adc.cHAL_ADC_MspInit() 中初始化。两处职责不同,不应重复初始化同一个 DMA 句柄。

1.5 添加头文件

  • dma.h 需要包含 main.h,使 void Init_DMA(void); 所在的工程拥有 HAL 类型和基础定义;同时要使用包含保护:
#ifndef __DMA_H__
#define __DMA_H__

#include "main.h"
void Init_DMA(void);

#endif
  • 包含保护防止同一个头文件被间接包含多次造成重复声明。宏名称应专用于当前文件,例如 __DMA_H__;复制 adc.h 模板后必须改名,不能保留 __ADC_H__

1.6 修改头文件

  • 可以以 adc.h 为模板创建 dma.h,但需要保留 #include "main.h" 和包含保护,并删除与 ADC 句柄、Init_ADCx() 无关的声明。
  • 原始工程中 ADC3 句柄由 adc.c 定义、由 adc.hextern ADC_HandleTypeDef hadc3; 声明;DMA3 句柄同理应由 adc.c 定义、需要在中断文件中以 extern DMA_HandleTypeDef hdma_adc3; 引用。
  • 复制后只通过搜索替换 adcdma 容易误伤标识符和注释。完成替换后必须逐项检查函数名、包含保护、句柄定义位置和被包含的文件。

1.7 声明函数

  • 函数声明应在 dma.h 中,调用它的 main.c 必须 #include "dma.h"。否则编译器在编译 main.c 时无法知道 Init_DMA() 的原型。
  • C 语言中“能看到声明”与“存在定义”是两回事:缺声明通常是编译警告或错误,缺定义通常是链接错误。二者都要通过头文件、源文件和工程文件列表一并检查。
  • 保存、重新编译后错误消失,只表示声明/链接关系已建立;仍需确认初始化调用顺序是 Init_DMA()HAL_ADC_Start_DMA() 之前。

1.8 编译工程

  • 从 ADC1-DMA 代码复制配置后,不能继续使用 DMA1_Channel1。STM32 的 DMA 请求映射由芯片硬件固定,软件只能选择手册允许的通道,不能任意把 ADC3 绑定到 DMA1 任一通道。
  • STM32F103 大容量产品的 DMA1 有 7 个通道、DMA2 有 5 个通道。参考手册明确 ADC3 的 DMA 请求只存在于大容量产品;本工程应使用 DMA2 Channel5
  • 编译器不能检查“外设请求是否接到了正确 DMA 通道”。错误通道仍可能通过编译,却表现为 DMA 缓冲区始终为 0 或从不更新,因此必须查 DMA 请求映射表和实际芯片型号。

1.9 相关手册

  • 查 DMA 映射时使用 STM32F103 对应的参考手册 DMA 章节,而不是只看 IDE 自动补全。参考手册说明 DMA 通道与外设请求的连线由硬件固定,并明确 ADC3 DMA 请求仅在大容量产品存在。
  • 查中断处理函数名时看启动文件的向量表。例如本工程的 startup_stm32f103xe.s 包含 DMA2_Channel4_5_IRQHandler,这才是大容量 STM32F103 对 DMA2 Channel5 的正确入口名。
  • 查引脚/封装时看数据手册或 CubeMX 引脚图;查寄存器、DMA 映射和时序时看参考手册。两类手册不能互相替代。

1.10 修改之前添加的dma头文件

  • DMA2 Channel4 和 Channel5 是两个独立传输通道,配置时仍应使用具体的 DMA2_Channel5;不要因为中断名里有 “4_5” 就把 ADC3 配到 Channel4。
  • 对 STM32F103 大容量产品,DMA2 Channel4 和 Channel5 共用同一个 NVIC 向量,因此中断函数名为 DMA2_Channel4_5_IRQHandler。该 ISR 内应把 hdma_adc3 传给 HAL_DMA_IRQHandler(),由 HAL 根据 Channel5 状态位处理事件。
  • ADC1 的 DMA 请求走 DMA1 Channel1;ADC3 的 DMA 请求走 DMA2 Channel5;ADC2 没有独立 DMA 请求,但可在双 ADC 模式中借助 ADC1 的 DMA 通路传输。原始笔记中“ADC2 无 DMA 支持”的说法应理解为“不能单独直接产生 DMA 请求”,不能误解为 ADC2 在所有 DMA 场景都不能使用。

1.11 修改adc头文件

  • 将 ADC1/ADC2 迁移到 ADC3 时,应在 adc.h 声明 extern ADC_HandleTypeDef hadc3;void Init_ADC3(void);,并在 adc.c 中定义同名句柄和函数。
  • 选择 ADC3 的前提是目标芯片有 ADC3。不能为了 DMA 而在没有 ADC3 的 MCU 上把 ADC1 字样直接改成 ADC3;这种代码通常无法编译或无法在目标硬件上工作。
  • 本实验使用 ADC3 的 Channel1、Channel2,分别连接 PA1、PA2。通道号与引脚的对应关系应以 STM32F103ZET6 的资料为准,而不是根据 ADC1 的命名猜测。

1.12 修改工程代码

1.12.1 代码初始化与函数修改
  • 主函数中注释掉旧的 Init_ADC2(),调用 Init_ADC3();随后应对 hadc3 校准并调用 HAL_ADC_Start_DMA(&hadc3, ...)。旧 ADC2 初始化若保留且没有使用,可以删除以免混淆。
  • Init_ADC3() 只负责 ADC3 参数和规则组通道;DMA 时钟需先由 Init_DMA() 打开,USART 初始化需在 printf 前完成。
  • 推荐顺序:系统时钟(含 ADC 分频)→ GPIO → DMA → USART → ADC3 → 校准 → 启动 ADC-DMA。这样可以在 ADC-DMA 关联和时钟都就绪后再发出转换请求。
1.12.2 函数声明与结构体定义
  • hadc3hdma_adc3 是两个不同的 HAL 句柄:前者描述 ADC3 外设,后者描述 DMA2 Channel5。__HAL_LINKDMAhadc3.DMA_Handle 指向后者。
  • 句柄只能在一个 .c 文件中真正定义,其他文件用 extern 声明。多处定义会导致重复符号;没有外部声明则中断文件无法访问 hdma_adc3
  • 函数名必须保持声明、定义、调用三处一致,例如都使用 Init_ADC3,不能混用 init_adc3adc_start 等相近拼写。
1.12.3 ADC3初始化与配置
  • hadc3.Instance = ADC3 指定外设基地址;其余初始化字段也要逐项复核,不能因为“模板看起来相同”就跳过。
  • 当前工程设置 ScanConvMode=ENABLENbrOfConversion=2,规则组 Rank1 为 Channel1/PA1,Rank2 为 Channel2/PA2;因此 AD_Buf[0] 对应 PA1,AD_Buf[1] 对应 PA2。
  • ContinuousConvMode=ENABLE 使一个两通道规则序列结束后自动开始下一轮;DiscontinuousConvMode=DISABLE,不能与连续规则转换混用。DMA 采用 DMA_CIRCULAR,两次半字传输后从缓冲区开头覆盖写入。
  • 每路均使用 239.5 个 ADC 时钟周期的采样时间。长采样时间有利于高源阻抗传感器,但会降低每轮序列的最高频率。
1.12.4 ADC与DMA配置细节
  • HAL_ADC_MspInit() 可通过 if (adcHandle->Instance == ADC2)if (adcHandle->Instance == ADC3) 分支服务多个 ADC。ADC3 分支必须使能 __HAL_RCC_ADC3_CLK_ENABLE(),并初始化 hdma_adc3
  • 源码中 ADC3 分支的注释仍写着 “ADC1 clock/GPIO/DMA Init”,这是复制遗留的注释错误;实际执行的是 ADC3 时钟和 DMA2 Channel5,阅读时应以代码标识符为准。
  • DMA 参数含义:外设端为 ADC3 DR,地址固定;内存端为 AD_Buf,地址递增;两端半字对齐;方向外设到内存;循环模式持续覆盖两个数组元素;优先级 Low 只表示与其他 DMA 请求竞争时的仲裁级别。
1.12.5 GPIO与ADC时钟配置
  • ADC3 的 Channel1/2 在本工程使用 PA1/PA2,GPIOA 时钟必须使能,两个引脚配置为 GPIO_MODE_ANALOG。模拟模式不是普通数字输入模式,能避免数字输入缓冲带来的额外干扰。
  • PA1 应接光敏传感器 AO,PA2 接 3V3 作为已知电压参考演示。两者均要与开发板 GND 共地;ADC 输入只能落在 VSSA~VDDA 范围,传感器若由 5 V 供电且 AO 可能达到 5 V,必须先分压或做电平转换。
  • 必须修正 ADC 时钟:工程 temp.ioc 显示 RCC.ADCFreqValue=36000000,而 SystemClock_Config() 也没有调用 HAL_RCCEx_PeriphCLKConfig() 设置 ADC 分频。在 PCLK2=72 MHz 时,默认 /2 会得到 36 MHz,超过 STM32F103 的 14 MHz 上限。应显式选择 RCC_ADCPCLK2_DIV6 得到 12 MHz;这不是可选优化,而是硬件规格要求。
1.12.6 主函数修改与ADC校准
  • HAL_ADCEx_Calibration_Start(&hadc3) 应在 ADC3 初始化后、HAL_ADC_Start_DMA() 前调用。参考手册建议每次上电后校准一次,HAL 会处理关停、使能、等待和校准寄存器复位等前置步骤。
  • HAL_ADC_Start_DMA(&hadc3, (uint32_t *)AD_Buf, 2) 的长度 2 是 DMA 数据项数量,必须同时匹配 AD_Buf[2] 和两个规则组 Rank。若长度、数组容量、Rank 数三者不一致,会发生漏数据、顺序错误或数组越界。
  • 主循环的 HAL_Delay(100) 控制的是串口打印和 LED 翻转节奏,不会限制 ADC3-DMA 的采样频率;ADC 仍按连续转换和 DMA 循环高速覆盖 AD_Buf
1.12.7 变量定义与打印修改
  • AD_Buf 应为 uint16_t AD_Buf[2]。ADC 结果实际只有 12 位,但 DMA 配置为半字传输,16 位数组与数据宽度匹配。
  • 打印文本改为 ADC3_DMA PA1ADC3_DMA PA2,用于和旧工程区分。电压换算 Raw * 3.3 / 4096.0 假定 VDDA=3.3 V,原始码范围是 0~4095;真实项目应使用实测 VDDA。
  • 同一个 DMA 缓冲区在后台持续改写,先读 AD_Buf[0] 后读 [1] 并不保证来自同一轮扫描。演示打印通常足够;需一致快照时应在 DMA 半传输/全传输回调中复制缓冲区或使用双缓冲。

1.13 编译错误

  • 视频中的编译问题来自复制时漏掉分号或括号。这类错误应以编译器给出的文件、行号和首个语法错误为起点修复,后续连锁错误往往会随之消失。
  • 编译通过不代表硬件通路正确。DMA 映射错误、ADC 时钟超限、GPIO 接线错误通常不会产生 C 编译错误,必须结合手册、寄存器配置和实际数据检查。
  • DMA 中断如果被 HAL 的 HAL_ADC_Start_DMA() 以中断方式启动,则 NVIC 和 ISR 要正确匹配;若不需要回调,也可以不启用 DMA 中断,但必须采用与 HAL 调用方式匹配的实现,不能留下一个启用却没有正确处理的 IRQ。

1.14 添加中断处理函数

  • 大容量 STM32F103 的正确函数名是 DMA2_Channel4_5_IRQHandler,不是 DMA1_Channel1_IRQHandler,也不是不存在的 DMA2_Channel5_IRQHandler
  • stm32f1xx_it.c 中需要 extern DMA_HandleTypeDef hdma_adc3;,中断服务函数中调用 HAL_DMA_IRQHandler(&hdma_adc3);。该调用会清除/分发 DMA 事件,并进一步触发 HAL 的 ADC DMA 回调。
  • 中断函数名必须与启动文件向量表中的符号完全相同;拼写不同、通道不同或漏掉 extern 都会导致中断落入默认处理函数或无法链接。

1.15 查看中断处理函数名称

  • 最可靠的查法是打开目标芯片对应启动文件的向量表。本工程 startup_stm32f103xe.s 在 DMA2 Channel3 后列出 DMA2_Channel4_5_IRQHandler,说明 Channel4/5 共用此入口。
  • Ctrl+H/全局搜索可辅助定位,但搜索结果必须与当前芯片的启动文件核对。互联型 F1 与大容量 F1 的 DMA2 Channel4/5 向量安排不同,不能跨型号复制 IRQ 名称。
  • NVIC 枚举 DMA2_Channel4_5_IRQn、启动文件函数名和 HAL_NVIC_EnableIRQ() 的参数三者必须成对出现。

1.16 编译成功

1.16.1 编译过程与文件概览
  • 无错误、仅有警告表示目标文件和链接产物已生成,但应消除与 DMA、格式串、未使用函数相关的重要警告。不能因程序“能烧写”而忽略警告。
  • HAL_DMA_IRQHandler()HAL_ADC_Start_DMA() 等 HAL 库函数提供通用状态处理;理解其职责有帮助,但应用层至少要确认句柄、通道、NVIC 和 DMA 请求映射正确。
1.16.2 DMA传输与中断管理
  • 视频中上电后无输出、复位后才打印,随后数据全为 0。这种现象不能仅凭一次复位归因,应该分别确认串口是否已经启动、DMA 是否有传输完成标志、ADC 是否在转换,以及输入引脚是否有有效电压。
  • DMA 传输本身由 ADC 请求驱动,不依赖 CPU 在主循环中手动搬运;中断用于在半传输/完成事件发生时通知 CPU 或清理 HAL 状态。若只打印循环缓冲区,仍需要保证 DMA 通路本身已正确配置。
1.16.3 代码追踪与问题排查
  • 排查顺序应从时钟、外设使能、DMA 映射、GPIO/Ranks、启动函数、缓冲区长度、IRQ 依次向下:
检查项ADC3-DMA 工程的正确值
ADC 时钟PCLK2/6 = 12 MHz
ADC 外设ADC3,时钟已使能
规则组Channel1/PA1 为 Rank1;Channel2/PA2 为 Rank2
DMA 请求DMA2 Channel5
数据宽度外设/内存均为 16 位半字
缓冲区和长度uint16_t AD_Buf[2] 与 Length=2
IRQDMA2_Channel4_5_IRQHandler 调用 HAL_DMA_IRQHandler(&hdma_adc3)
  • 逐行检查时不要跳过“看起来没改”的字段。视频最终发现的正是 DMA 通道仍保留为 ADC1 的 DMA1_Channel1,这类遗漏会让数据通路失效。
1.16.4 ADC与DMA配置问题
  • 不要把 USART1、ADC1、ADC2、ADC3 的初始化代码混在一起。串口仅负责输出文本,ADC3 决定采样,DMA2 Channel5 负责搬运;一个模块名字改对并不能修复另一个模块。
  • 如果 AD_Buf 始终为 0,先测 PA1/PA2 实际电压,再检查 ADC3 的 EOC/DMA 标志和 DMA2 Channel5 配置;不要先怀疑 printf 浮点显示。
  • 源码中遗留的 ADC1 注释、Init_ADC2() 函数和已注释的调用都是复制痕迹。学习时只追踪当前执行路径:Init_ADC3hadc3hdma_adc3DMA2_Channel5
1.16.5 DMA通道配置错误排查
  • ADC3 不能使用 DMA1 Channel1;那条通路是 ADC1 专用。把通道写错时,代码通常能通过编译,但 DMA 不会收到 ADC3 请求。
  • 应在三处交叉检查:hdma_adc3.Instance = DMA2_Channel5、DMA2 时钟使能、NVIC 的 DMA2_Channel4_5_IRQn。三处任何一处残留 DMA1 都是不完整移植。
  • ADC2 的旧配置即使仍存在于源码,若未调用 Init_ADC2() 不会参与当前工作;但建议清理无用初始化,减少后续排错范围。
1.16.6 DMA通道定义与参数设置
  • DMA2 Channel4 和 Channel5 的配置寄存器和通道实例彼此独立,ADC3 必须绑定 DMA2_Channel5
  • 两个通道共享的是中断向量,因此中断枚举和 ISR 名字包含 Channel4_5。不要从共享 IRQ 反推出共享同一个 DMA 通道。
  • 修正为 Channel5 后,串口读数随光敏传感器遮挡/照射改变,PA2 接 3.3 V 的读数接近 3.3 V,才能说明 ADC3→DMA2 Channel5→AD_Buf 的数据链路基本正确。

1.17 复位查看效果

  • 复位后观察 PA1(光敏 AO)和 PA2(3.3 V)是一个有用的端到端验证:遮挡或触摸传感器时 PA1 电压会随模块输出特性变化,PA2 应保持接近电源电压。
  • “摸传感器”只适合作为定性演示。若要验证精度,应用万用表测量 AO 和 VDDA,并比较 ADC 原始码与公式 Raw ≈ Vin / VDDA × 4096 的结果。
  • 参考手册中 UART4 的映射示例为:RX 可用 DMA2 Channel3,TX 可用 DMA2 Channel5。这是另一条固定请求通路,与 ADC3 同用 DMA2 Channel5 时不能同时配置并发使用;DMA 通道不能同时服务两个外设请求。

二、内容复盘

  • 模拟信号的幅值连续变化,例如传感器 AO 的电压;数字信号只在离散逻辑电平或离散数值间变化。ADC 的作用是以采样频率和量化位数把模拟电压转换成数字码。
  • STM32F103ZET6 有 ADC1、ADC2、ADC3 三个 ADC 控制器,但并非所有封装/容量的 F103 都有 ADC3。多 ADC 不代表同一引脚可被随意同时采样,也不代表每个 ADC 都有独立 DMA 请求。
  • 本课程使用规则组转换:通道根据 Rank 排队。扫描、连续、间断和 DMA 分别控制“序列怎么走”“是否自动重复”“一次触发走多少 Rank”“结果怎么搬运”,不要把它们混为同一个模式。

三、ADC控制器实验演示

  • 规则转换:规则组中 Rank1、Rank2 等决定转换顺序。本工程使用两路:PA1 先于 PA2。
  • 单次/间断查询:一次 HAL_ADC_Start() 可按间断转换配置完成一个或多个 Rank,CPU 再轮询等待结果;此前多通道查询实验设置每次一个 Rank。
  • 连续 DMA:本工程的 ADC3 设置 ContinuousConvMode=ENABLE,完成两路规则序列后自动重启;DMA 循环模式把每个结果写入数组,CPU 不必逐个等待。
  • ADC 时钟:ADC 时钟来自 PCLK2 的 2、4、6 或 8 分频,STM32F103 的 ADC 输入时钟不得超过 14 MHz。72 MHz PCLK2 下常选 /6 得到 12 MHz;/4 的 18 MHz 和 /2 的 36 MHz 都超限。
  • 转换时间Tconv = (Tsamp + 12.5) / fADC。采样时间可选 1.5、7.5、13.5、28.5、41.5、55.5、71.5 或 239.5 个 ADC 时钟周期;239.5 周期、12 MHz 时每路约 21 us,两路约 42 us。采样时间越长不等于量化位数更高,而是让采样保持电容有更充分的建立时间。
  • 量化码:12 位 ADC 有 4096 个量化等级,输出整数码为 0~4095,不是“精度为 4096 V”。理想最小分辨电压约为 VDDA / 4096,VDDA=3.3 V 时约 0.806 mV,但实际有效精度还受参考电压、噪声、源阻抗和 PCB 布局影响。

四、ADC理论知识

  • 12 位 ADC 的左/右对齐只是把 12 位结果放进 16 位数据寄存器的方式:右对齐时结果位于 bit[11:0],便于直接用 0~4095 参与计算;左对齐时结果左移 4 位,适合只读取高 8 位或高 12 位的场景。本课程使用右对齐。
  • 更高分辨率、采样速率或更低噪声要求可以选用外部 ADC,通过 I2C、SPI 等总线读取。选型时不能只看“位数”:还要看采样率、输入范围、参考源、INL/DNL、噪声、接口速率和前端驱动要求。
  • 初学应先掌握单通道/多通道查询的时钟、Rank、采样时间、换算和接线;中断与 DMA 是同一 ADC 数据通路的不同处理方式,理解后可逐步加入,不应以复制代码代替核对硬件映射。
  • ADC 中断模式需要启用 ADC 的转换完成中断,在对应 ISR 或 HAL 回调中读取/处理数据;DMA 模式则由 DMA 搬运结果,通常在半传输/全传输回调中处理一批数据。两者都要求中断向量、优先级和共享数据访问正确。