F429-HAL-ADC-DMA单通道/多通道(2026/7/25) 📅 2026/7/26 0:27:04 目录一、ADC Init 字段逐个解析句柄 Init通道配置最后启动① Instance — 用哪个 ADC② ClockPrescaler — ADC 时钟分频③ Resolution — 采样精度④ ScanConvMode — 多通道自动扫描⑤ ContinuousConvMode — 连续转换⑥⑦ DiscontinuousConvMode — 间断模式⑧ ExternalTrigConvEdge — 外部触发⑨ DataAlign — 结果对齐⑩ NbrOfConversion — 转换通道数⑪ DMAContinuousRequests — DMA 持续请求⑫ EOCSelection — 转换结束标志⑬ Channel — 通道号⑭ Rank — 转换顺序⑮ SamplingTime — 采样时间⑯ Offset — 偏移量跟已学外设对比二、ADC 通道 vs DMA 通道两个概念别混ADC 通道 模拟引脚编号DMA Stream Channel 数据搬运路由一张图分清楚HAL_ADC_Start_DMA 形参三、单通道 多通道 DMA 流水线三个变量一条流水线单通道数据流多通道数据流单通道 → 多通道Init 只改三处跟 UART/SPI/I2C DMA 对比四、为什么 ADC DMA 不需要回调五、初始化顺序六步走一、ADC Init 字段逐个解析ADC 有两层配置句柄 Init管 ADC 硬件 通道 ConfigChannel管每个引脚。句柄 InitADC_Handle.Instance ADC1; // ① 硬件编号 ADC_Handle.Init.ClockPrescaler ADC_CLOCKPRESCALER_PCLK_DIV4; // ② 时钟分频 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.DataAlign ADC_DATAALIGN_RIGHT; // ⑨ 数据对齐 ADC_Handle.Init.NbrOfConversion 1; // ⑩ 通道数量 ADC_Handle.Init.DMAContinuousRequests ENABLE; // ⑪ DMA 持续 ADC_Handle.Init.EOCSelection DISABLE; // ⑫ 转换结束标志 HAL_ADC_Init(ADC_Handle);通道配置ADC_ChannelConfTypeDef ADC_Config; ADC_Config.Channel ADC_CHANNEL_13; // ⑬ 通道号 ADC_Config.Rank 1; // ⑭ 转换顺序 ADC_Config.SamplingTime ADC_SAMPLETIME_56CYCLES;// ⑮ 采样时间 ADC_Config.Offset 0; // ⑯ 偏移量 HAL_ADC_ConfigChannel(ADC_Handle, ADC_Config);最后启动HAL_ADC_Start_DMA(ADC_Handle, adc_buf, 1);① Instance — 用哪个 ADCADC1。F429 有 3 个 ADCADC1/2/3电位器接 PC3 → ADC1_CH13。② ClockPrescaler — ADC 时钟分频DIV4。ADC 挂 APB290MHz÷4 22.5MHz。手册建议不超过 36MHz。③ Resolution — 采样精度12B12 位。转换结果 0~4095存uint16_t。选项范围12B0~409510B0~10238B0~255④ ScanConvMode — 多通道自动扫描DISABLE 单通道。ENABLE 多通道模式ADC 按 Rank 1→2→3 轮着采DMA 把结果依次搬到数组buf[0]/buf[1]/buf[2]。这是单/多通道唯一要改的字段。⑤ ContinuousConvMode — 连续转换ENABLE 持续采采完一轮接着下一轮。配合 DMA CIRCULAR永不停机。⑥⑦ DiscontinuousConvMode — 间断模式关掉用不到。每次触发只采 N 个通道等下次触发再采下一批。⑧ ExternalTrigConvEdge — 外部触发NONE 不用外部触发。ContinuousConvMode 开着ADC 内部一直自己跑。⑨ DataAlign — 结果对齐RIGHT右对齐。12 位数据放 16 位寄存器右对齐直接当 0~4095 整数用。⑩ NbrOfConversion — 转换通道数单通道填1多通道填通道个数如3。⑪ DMAContinuousRequests — DMA 持续请求ENABLE。不开则 DMA 搬完一轮就停开了才能配合 CIRCULAR 模式永续工作。⑫ EOCSelection — 转换结束标志DISABLE。DMA 模式下不用关心此标志HAL 内部处理。⑬ Channel — 通道号每个引脚对应固定通道号。PC3 CH13, PA4 CH4, PA6 CH6。⑭ Rank — 转换顺序单通道永远1。多通道分别给 Rank 1/2/3ADC 按顺序扫描。⑮ SamplingTime — 采样时间56CYCLES56 个 ADC 时钟周期。采样越长越准但越慢。可选 3/15/28/56/84/112/144/480 个周期。⑯ Offset — 偏移量填0不偏移。如需结果减掉固定值再用。跟已学外设对比UARTI2CSPIADC方向收发收发收发只收发命令不要要要不要配置层InitInitInitInit ConfigChannel特有模式——CPOL/CPHAScanConv/ContinuousConv选芯片不选设备地址CS 引脚不选ADC 是最简单的外设——不发命令、不选芯片、不擦写。配好 Init → 开 DMA → 读数组。二、ADC 通道 vs DMA 通道两个概念别混ADC 通道 模拟引脚编号F429 有 3 个 ADC每个有 16 个外部通道CH0~CH15PC3 → ADC1/2/3 都能用 → 选 ADC1 → CH13芯片硬件固定——PC3 只能连到 CH13不能改成 CH5。DMA Stream Channel 数据搬运路由ADC1 的转换结果 → 通过 DMA2_Stream0_Channel0 → 搬到 SRAM同样是硬件固化的DMA2_Stream0 这台车配 Channel 0 Channel 0 → 连的是 ADC1写死在芯片设计里 Channel 1 → 连的是别的外设一张图分清楚ADC 通道DMA Stream/Channel选什么CH13 (PC3)DMA2_Stream0, Ch0谁定的芯片引脚布局芯片总线矩阵能改吗不能不能代码里ch.Channel ADC_CHANNEL_13Instance Init.Channel一个是采哪个引脚一个是结果搬到哪。HAL_ADC_Start_DMA 形参HAL_StatusTypeDef HAL_ADC_Start_DMA( ADC_HandleTypeDef *hadc, // ① ADC 句柄指针 uint32_t *pData, // ② 存放 ADC 数据的缓冲区地址 uint32_t Length // ③ 要传输的数据长度单位次 );形参单通道多通道pDataadc_buf[0]单个 uint16_t 的地址adc_buf[0]数组首地址Length1DMA CIRCULAR 模式下搬完 1 个从头循环3搬完 3 个从头循环buf[0]→buf[1]→buf[2]→buf[0]…调用后立刻返回——ADC 和 DMA 在后台循环工作main 任何时候读adc_buf都是最新值。不需要等回调、不需要清标志这是所有外设里最省心的一键启动。三、单通道 多通道 DMA 流水线三个变量一条流水线ADC_HandleTypeDef hadc1; // ① ADC 句柄12位/连续/单通道/右对齐/22.5MHz... DMA_HandleTypeDef hdma_adc; // ② DMA 句柄外设→内存CIRCULAR 循环 uint16_t adc_buf[N]; // ③ 唯一的数据缓冲区N1 或 N3没有 callback、没有 flag、没有 len、不用清零。跟 UART/I2C/SPI 那一堆 buf/len/flag/rx_byte 比ADC 简单到只有一个数组。单通道数据流电位器电压 → PC3 引脚 → ADC1 采样 → DR 寄存器 │ DMA2_Stream0 自动搬CIRCULAR 循环 │ ▼ adc_buf[0] ← 永远是最新一次采样值 │ ▼ main 读 adc_buf[0] → 转电压 → printf多通道数据流Rank 1: PC3 → ADC1 → DR → DMA 搬到 adc_buf[0] Rank 2: PA4 → ADC1 → DR → DMA 搬到 adc_buf[1] Rank 3: PA6 → ADC1 → DR → DMA 搬到 adc_buf[2] │ 一轮 3 次搞完CIRCULAR 回到 Rank 1 重来 │ ▼ main 读 adc_buf[0/1/2] → 三个通道最新值永远在手单通道 → 多通道Init 只改三处字段单通道多通道ScanConvModeDISABLEENABLENbrOfConversion13ConfigChannel调用1 次3 次Rank 1/2/3其余 Init DMA 配置一行不变。HAL_ADC_Start_DMALength 从 1 变成 3DMA 就知道一轮搬三个。跟 UART/SPI/I2C DMA 对比UART DMAI2C DMASPI DMAADC DMAbuf 大小128 字节128 字节256 字节2~6 字节flag1 个2 个2 个0 个len 变量有有无无清标志要要要不要重启 DMAIDLE 后手动手动手动不要CIRCULAR 自动循环回调TxCplt/RxCpltMemTx/MemRxTxRxCplt无四、为什么 ADC DMA 不需要回调UART/SPI/I2C DMA 搬的是离散数据包——一帧发完必须立刻处理错过就丢了所以需要NORMAL模式 回调通知整包到了。ADC 搬的是连续模拟量——电位器电压一直在变CPU 不需要知道哪一轮搬完了main 每隔 500ms 扫一眼adc_buf[0]拿到此刻的最新值就够了。UART [数据帧] ---空闲--- [数据帧] → 离散包必须回调及时收 ADC ~~~~~~~电压连续变化~~~~~~~ → 连续量随取随用这就是为什么 ADC DMA 用CIRCULAR模式永不停止、永无回调而其他外设用NORMAL模式搬完停 回调通知。五、初始化顺序六步走① 开时钟 ├─ GPIO 时钟GPIOC / GPIOA ├─ ADC1 时钟 └─ DMA2 时钟 ② GPIO 配置模拟模式 └─ Mode ANALOG, Pull NOPULL ③ DMA 配置外设→内存CIRCULAR ├─ Instance DMA2_Stream0 ├─ Channel CH0 ├─ Direction PERIPH_TO_MEMORY ├─ PeriphInc DISABLE外设地址固定 ADC_DR 寄存器 ├─ MemInc ENABLEbuf[0]→buf[1]→buf[2] ├─ Alignment HALFWORD16 位跟 uint16_t 匹配 ├─ Mode CIRCULAR └─ __HAL_LINKDMA → 挂到 ADC 句柄上 ④ ADC 句柄配置 ├─ ClockPrescaler DIV422.5MHz ├─ Resolution 12B ├─ ScanConvMode ENABLE多通道 / DISABLE单通道← 唯一区别 ├─ ContinuousConv ENABLE连续采 ├─ DataAlign RIGHT ├─ NbrOfConversion 3 / 1 └─ DMAContinuousReq ENABLE ⑤ 通道配置调用 N 次N 通道数 └─ Channel Rank SamplingTime Offset ⑥ 启动 └─ HAL_ADC_Start_DMA(hadc1, adc_buf, N)时钟 → GPIO → DMA → ADC 句柄 → 通道 → 启动。