STM32的ADC模块中,**采样时机(Sampling Time)**和**转换时机(Conversion Time),获取数据的时机详解

在STM32的ADC模块中,**采样时机(Sampling Time)转换时机(Conversion Time)**是ADC工作流程中的两个关键阶段,直接影响采样精度和系统实时性。以下是详细解析:


1. 采样时机(Sampling Time)

(1)定义
  • 采样阶段:ADC对输入信号进行保持和稳定的过程。
  • 采样时间:由ADC_SMPRx寄存器配置,决定采样电容充电时间。
(2)配置参数

STM32F103的采样时间可设置为:

typedef enum {ADC_SampleTime_1Cycles5,    // 1.5周期ADC_SampleTime_7Cycles5,    // 7.5周期ADC_SampleTime_13Cycles5,   // 13.5周期ADC_SampleTime_28Cycles5,   // 28.5周期ADC_SampleTime_41Cycles5,   // 41.5周期ADC_SampleTime_55Cycles5,   // 55.5周期ADC_SampleTime_71Cycles5,   // 71.5周期ADC_SampleTime_239Cycles5   // 239.5周期(用于高阻抗信号)
} ADC_SampleTime;
(3)选择原则
信号类型推荐采样时间原因
低阻抗信号1.5~28.5周期信号源阻抗低(如运放输出),快速稳定。
高阻抗信号55.5~239.5周期信号源阻抗高(如温度传感器、分压电路),需更长时间充电。
内部通道≥239.5周期内部温度传感器和VREFINT阻抗极高,必须延长采样时间。
(4)代码示例
// 配置PA0(低阻抗)和温度传感器(高阻抗)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5);  // 快速采样
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_239Cycles5); // 慢速采样

2. 转换时机(Conversion Time)

(1)定义
  • 转换阶段:ADC核心将采样到的模拟量转换为数字量的过程。
  • 固定耗时:12位分辨率下恒定为12.5个ADC时钟周期(与采样时间无关)。
(2)总转换时间计算

[
T_{total} = (T_{sampling} + 12.5) \times \frac{1}{f_{ADC}}
]

  • 示例
    • ADC时钟 = 14 MHz,采样时间 = 55.5周期
    • 总时间 = (55.5 + 12.5) / 14MHz ≈ 4.86μs
(3)吞吐量限制
  • 理论最大采样率
    [
    f_{max} = \frac{1}{T_{total}}
    ]
    • T_total=4.86μs,则f_max≈205kHz(单通道)。

3. 采样与转换的时序图

采样阶段                       转换阶段
|-------- Sampling Time --------|-- 12.5 Cycles --|
|<----------- Total Conversion Time ------------>|

4. 关键影响因素

(1)ADC时钟频率
  • 最大14MHz(STM32F103),需分频APB2时钟:
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);  // 72MHz/6=12MHz
    
  • 更高时钟 → 更快转换,但可能降低精度。
(2)通道切换延迟
  • 多通道扫描时,每个通道需单独配置采样时间,切换通道会增加额外延迟。
(3)触发方式
  • 硬件触发(如定时器):精确控制采样间隔。
  • 软件触发:灵活性高但时序不易控制。

5. 优化策略

(1)动态调整采样时间

根据信号类型切换采样时间:

void Set_ADC_SampleTime(ADC_TypeDef* ADCx, uint8_t channel, uint8_t isHighImpedance) {ADC_SampleTime time = isHighImpedance ? ADC_SampleTime_239Cycles5 : ADC_SampleTime_28Cycles5;ADC_RegularChannelConfig(ADCx, channel, 1, time);
}
(2)使用注入组中断

高优先级信号通过注入组立即响应:

// 过压时紧急采样
void ADC1_2_IRQHandler(void) {if (ADC_GetITStatus(ADC1, ADC_IT_JEOC)) {uint16_t emergencyData = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);}
}
(3)DMA传输规则组数据

避免CPU轮询,提高效率:

uint16_t adcValues[3];
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adcValues;
ADC_DMACmd(ADC1, ENABLE);

6. 实际应用示例

电机电流监测系统
  • 规则组
    • 通道PA0(电流传感器),采样时间7.5周期(低阻抗)。
    • 定时器触发1kHz采样,DMA传输数据。
  • 注入组
    • 比较器检测过流时触发PA8中断,注入组采集PA1(备份传感器)。
  • 计算总延迟
    • 采样+转换时间 = (7.5 + 12.5) / 14MHz ≈ 1.43μs
    • 1kHz采样率下,CPU负载极低。

总结

  • 采样时机:由信号源阻抗决定,需足够时间稳定信号。
  • 转换时机:固定12.5周期,与ADC时钟频率直接相关。
  • 优化核心:在精度和速度间平衡,优先保证信号完整性。

在STM32的ADC编程中,采样转换数据获取的流程分布在程序的不同位置,具体取决于触发方式(软件/硬件)和数据读取方式(轮询/DMA/中断)。以下是基于STM32F103的详细说明和代码示例:


1. 采样(Sampling)

触发采样的位置
  • 软件触发:在代码中显式调用启动转换函数。
    // 在需要采样的地方(如主循环或定时器回调中)
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);  // 启动规则组采样
    ADC_SoftwareStartInjectedConv(ADC1);     // 启动注入组采样
    
  • 硬件触发:由定时器、外部中断等自动触发,无需手动调用。
    // 配置TIM2触发规则组采样(无需在主循环中操作)
    TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
    
采样时间配置

在ADC初始化阶段设置:

// 配置规则组通道的采样时间(在ADC初始化时)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);

2. 转换(Conversion)

转换的触发
  • 转换由硬件自动完成,无需程序干预
  • 转换时间固定为12.5个ADC时钟周期(如14MHz时钟下约0.89μs)。
判断转换完成
  • 轮询方式:检查标志位。
    while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));  // 规则组
    while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_JEOC)); // 注入组
    
  • 中断方式:在中断服务函数中处理。
    void ADC1_2_IRQHandler(void) {if (ADC_GetITStatus(ADC1, ADC_IT_EOC)) {uint16_t data = ADC_GetConversionValue(ADC1);  // 规则组数据ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);}if (ADC_GetITStatus(ADC1, ADC_IT_JEOC)) {uint16_t data = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);}
    }
    

3. 获取数据(Data Readout)

规则组数据
  • 轮询读取
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
    uint16_t adcValue = ADC_GetConversionValue(ADC1);  // 读取规则组数据
    
  • DMA传输(推荐多通道):
    uint16_t adcValues[3];  // 存储3个通道的数据
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adcValues;
    ADC_DMACmd(ADC1, ENABLE);
    // 数据会自动更新到adcValues数组
    
注入组数据
  • 中断读取
    void ADC1_2_IRQHandler(void) {if (ADC_GetITStatus(ADC1, ADC_IT_JEOC)) {uint16_t injData = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);}
    }
    

4. 完整流程示例(规则组+注入组)

初始化配置
void ADC_Init(void) {// 1. 时钟和GPIO初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);// 2. 规则组配置(PA0)ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);ADC_InitStructure.ADC_ScanConvMode = DISABLE;ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;ADC_Init(ADC1, &ADC_InitStructure);// 3. 注入组配置(PA1)ADC_InjectedChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_Ext_IT15);// 4. 启用中断ADC_ITConfig(ADC1, ADC_IT_JEOC, ENABLE);NVIC_EnableIRQ(ADC1_2_IRQn);// 5. 校准和启动ADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1));
}
主程序中的采样与读取
int main(void) {ADC_Init();while (1) {// 规则组:手动触发并读取ADC_SoftwareStartConvCmd(ADC1, ENABLE);while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));uint16_t regularData = ADC_GetConversionValue(ADC1);// 注入组:由PA8按键触发(中断中自动处理)Delay_ms(100);  // 模拟其他任务}
}
中断服务函数
void ADC1_2_IRQHandler(void) {if (ADC_GetITStatus(ADC1, ADC_IT_JEOC)) {uint16_t injectedData = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);// 处理紧急数据(如过压保护)}
}

5. 关键总结

操作程序位置常用函数/配置
采样触发主循环/定时器回调/中断ADC_SoftwareStartConvCmd()
采样时间ADC初始化阶段ADC_RegularChannelConfig()
转换等待轮询/中断ADC_GetFlagStatus() 或中断标志
数据读取轮询/DMA/中断ADC_GetConversionValue() 或DMA数组
  • 规则组:适合周期性采样,配合DMA提高效率。
  • 注入组:用于紧急事件,中断中即时响应。
  • 硬件触发:精确控制时序,减少CPU干预。

在STM32的ADC配置中,**连续扫描(Scan Mode)连续转换(Continuous Conversion Mode)**是两个关键的工作模式,它们决定了ADC如何对多个通道进行采样和数据更新。以下是详细解释:


1. 连续扫描模式(Scan Mode)

功能
  • 多通道自动切换:ADC按预设顺序依次采样多个通道(规则组或注入组),无需手动切换。
  • 数据存储:需配合DMA或中断将数据存入数组(规则组数据寄存器ADC_DR会被覆盖)。
配置方法
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;  // 启用扫描模式
ADC_InitStructure.ADC_NbrOfChannel = 3;       // 通道数量
ADC_Init(ADC1, &ADC_InitStructure);// 设置通道顺序(规则组)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); // 第1通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); // 第2通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5); // 第3通道
典型应用
  • 同时监测多个传感器(如温度、电压、电流)。
  • 需配合DMA传输数据:
    uint16_t adcValues[3];  // 存储多通道数据
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adcValues;
    ADC_DMACmd(ADC1, ENABLE);
    

2. 连续转换模式(Continuous Conversion Mode)

功能
  • 自动重启转换:完成一次转换后立即开始下一次,无需手动触发。
  • 单通道/多通道:可与扫描模式组合使用。
配置方法
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  // 启用连续转换
ADC_Init(ADC1, &ADC_InitStructure);
典型应用
  • 实时监控信号变化(如音频采集)。
  • 单通道连续采样示例:
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);  // 启动后自动循环采样
    

3. 组合使用场景

模式对比
模式组合行为适用场景
扫描+单次转换按顺序采样所有通道后停止定时触发多通道采样(如每1秒1次)
扫描+连续转换循环采样所有通道,数据持续更新实时多通道监测(如电机控制)
单通道+连续转换重复采样同一通道高速信号捕获(如示波器)
代码示例(扫描+连续转换)
// 初始化ADC(多通道循环采样)
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 3;
ADC_Init(ADC1, &ADC_InitStructure);// 配置DMA自动传输数据
uint16_t adcValues[3];
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adcValues;
ADC_DMACmd(ADC1, ENABLE);// 启动ADC
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

4. 关键注意事项

  1. 数据覆盖问题

    • 规则组扫描时,ADC_DR寄存器会被新数据覆盖,必须使用DMA或中断及时读取。
  2. 触发源选择

    • 连续转换模式下,硬件触发(如定时器)可精确控制采样间隔。
  3. 功耗权衡

    • 连续转换模式功耗较高,电池供电设备建议使用单次转换+定时触发。
  4. 注入组优先级

    • 即使规则组处于连续转换中,注入组触发仍会立即中断当前转换。

5. 常见问题解答

Q1:扫描模式必须用DMA吗?
A1:不是必须,但强烈推荐。DMA可避免数据丢失,若用中断读取,需确保处理速度够快。

Q2:如何暂停连续转换?
A2:调用ADC_ContinuousConvModeCmd(ADC1, DISABLE),或直接关闭ADC:

ADC_Cmd(ADC1, DISABLE);  // 完全停止ADC

Q3:规则组和注入组能同时用扫描模式吗?
A3:可以!规则组和注入组的扫描相互独立,但注入组会打断规则组当前转换。


总结

  • 扫描模式:解决多通道自动切换问题,需注意数据存储。
  • 连续转换:实现无缝采样,适合实时性要求高的场景。
  • 组合使用:灵活应对复杂需求(如实时监测多个传感器)。

通过合理配置这两种模式,可以充分发挥STM32 ADC的性能。

在STM32的ADC模块中,**连续转换模式(Continuous Conversion Mode)单次转换模式(Single Conversion Mode)**的选择直接影响系统的实时性、功耗和采样精度。以下是针对您提到的两点的详细解释:


1. 连续转换模式 + 硬件触发的精确控制

工作原理
  • 连续转换模式:ADC完成一次转换后,立即自动启动下一次转换,形成不间断的采样循环
  • 硬件触发(如定时器TIM2):通过外部信号(如定时器更新事件)控制ADC的启动时机,实现固定间隔采样。
为何能精确控制采样间隔?
  • 硬件同步:定时器的时钟源稳定(如内部72MHz晶振),触发信号的间隔误差极小(纳秒级)。
  • 规避软件延迟:相比软件触发(需CPU干预),硬件触发完全由外设自动完成,无调度延迟。
配置示例
// 配置TIM2触发ADC(1kHz采样率,即1ms间隔)
TIM_TimeBaseInitTypeDef TIM_InitStructure;
TIM_InitStructure.TIM_Period = 7200 - 1;  // 72MHz / 7200 = 10kHz
TIM_InitStructure.TIM_Prescaler = 0;
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);  // 更新事件触发ADC// 配置ADC为连续转换+硬件触发
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
ADC_Init(ADC1, &ADC_InitStructure);

效果:ADC以精确的1ms间隔采样,适合需要严格周期性的应用(如数字滤波器、PWM控制)。


2. 连续转换模式的功耗问题

高功耗原因
  • ADC持续工作:连续转换模式下,ADC核心和模拟电路(如采样保持电路)始终处于活动状态,电流消耗较大(STM32F103典型值约1mA@14MHz ADC时钟)。
  • 频繁数据更新:即使无实际信号变化,ADC仍持续转换,浪费功耗。
单次转换+定时触发的低功耗方案
(1)工作流程
  1. 单次转换模式:每次触发后只采样一次,然后自动停止。
  2. 定时器触发:按需唤醒ADC(如每秒1次),其余时间ADC处于低功耗状态。
(2)配置代码
// 配置ADC为单次转换+硬件触发
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 单次模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
ADC_Init(ADC1, &ADC_InitStructure);// 配置TIM2低速触发(如1Hz)
TIM_InitStructure.TIM_Period = 72000000 - 1;  // 72MHz / 72000000 = 1Hz
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
(3)功耗对比
模式平均电流(示例)适用场景
连续转换(1kHz)~1.2mA实时控制、高速信号处理
单次转换+定时(1Hz)~50μA电池供电的传感器监测

3. 如何选择?

选择连续转换模式当:
  • 需要高实时性(如电机控制、音频采样)。
  • 系统供电充足(如插电设备)。
选择单次转换+定时触发当:
  • 设备由电池供电(如IoT传感器)。
  • 信号变化缓慢(如温度、湿度监测)。
折中方案
  • 动态切换模式:根据任务需求灵活调整。
    void Set_ADCMode(uint8_t isHighSpeed) {ADC_ContinuousConvModeCmd(ADC1, isHighSpeed ? ENABLE : DISABLE);TIM_SetAutoreload(TIM2, isHighSpeed ? 7200 - 1 : 72000000 - 1); // 切换采样率
    }
    

4. 实测建议

  • 测量电流:用万用表对比两种模式的功耗差异(注意关闭未用外设)。
  • 唤醒时间:单次模式首次转换可能有额外延迟(需重新校准)。

通过合理选择转换模式,可平衡性能与功耗,满足不同应用场景需求。

在STM32的ADC应用中,定时器触发ADC采样通常不需要在定时器中断服务函数(如TIMx_IRQHandler)中编写代码,而是直接通过定时器的硬件触发输出(如TRGO)自动控制ADC启动。以下是具体说明和代码示例:


1. 硬件触发ADC的配置流程

(1)定时器配置
  • 不依赖中断:仅用定时器的更新事件(UEV)触发ADC,无需进入中断。
  • 关键步骤
    • 设置定时器周期(决定采样间隔)。
    • 配置定时器触发输出(TRGO)。
// 配置TIM2触发ADC(1kHz采样率,72MHz主频)
TIM_TimeBaseInitTypeDef TIM_InitStructure;
TIM_InitStructure.TIM_Period = 72000 - 1;  // 72000 / 72MHz = 1ms (1kHz)
TIM_InitStructure.TIM_Prescaler = 0;       // 不分频
TIM_InitStructure.TIM_ClockDivision = 0;
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);// 配置TIM2更新事件触发TRGO
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
TIM_Cmd(TIM2, ENABLE);  // 启动定时器
(2)ADC配置
  • 设置为硬件触发模式,选择定时器作为触发源。
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; // TIM2触发
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 单次转换模式(推荐低功耗)
ADC_Init(ADC1, &ADC_InitStructure);

2. 何时需要定时器中断?

如果需要在定时器事件中执行其他任务(如数据处理、状态检查),才需启用中断。此时需:

(1)启用定时器中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);  // 允许更新中断
NVIC_EnableIRQ(TIM2_IRQn);                 // 启用NVIC中断
(2)中断服务函数示例
void TIM2_IRQHandler(void) {if (TIM_GetITStatus(TIM2, TIM_IT_Update)) {// 此处可添加其他任务(如读取ADC数据、控制逻辑)// 注意:ADC采样已由硬件自动触发,无需在此启动!TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  // 清除中断标志}
}

3. 完整代码示例(无中断方案)

目标:用TIM2以1kHz触发ADC采样,DMA传输数据。
#include "stm32f10x.h"uint16_t adcValue;  // 存储ADC数据void ADC_TIM_Config(void) {// 1. 启动时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);// 2. 配置GPIO(PA0为ADC输入)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);// 3. 配置TIM2(1kHz触发)TIM_TimeBaseInitTypeDef TIM_InitStructure;TIM_InitStructure.TIM_Period = 72000 - 1;  // 1ms间隔TIM_InitStructure.TIM_Prescaler = 0;TIM_TimeBaseInit(TIM2, &TIM_InitStructure);TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);  // 更新事件触发ADCTIM_Cmd(TIM2, ENABLE);// 4. 配置ADC1(单次转换+定时器触发)ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;ADC_InitStructure.ADC_ScanConvMode = DISABLE;ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 单次转换ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_NbrOfChannel = 1;ADC_Init(ADC1, &ADC_InitStructure);// 5. 配置ADC通道ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);// 6. 启用ADCADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1));
}int main(void) {ADC_TIM_Config();while (1) {// 数据通过DMA或中断自动更新(此处为轮询示例)if (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) {adcValue = ADC_GetConversionValue(ADC1);ADC_ClearFlag(ADC1, ADC_FLAG_EOC);}}
}

4. 关键注意事项

  1. 无需中断触发ADC:硬件触发本身不需要CPU干预,直接由定时器信号启动ADC。
  2. 中断的用途:仅当需要在定时器事件中执行额外逻辑(如数据处理、状态机更新)时才启用中断。
  3. DMA推荐:高频采样时,务必使用DMA传输数据,避免CPU轮询开销。

总结

  • 纯硬件触发:配置定时器TRGO + ADC外部触发,无需中断,效率最高。
  • 中断的适用场景:同步执行其他任务(如报警检查、数据打包)。
  • 低功耗设计:单次转换模式 + 定时器触发,适合电池供电设备。
    在STM32的ADC应用中,定时器触发的是ADC的启动时机,即控制何时开始一次完整的“采样+转换”过程。以下是关键概念的分步解释和代码实现:

1. 定时器触发的本质

  • 触发的内容:定时器触发的是ADC的完整转换流程,包括:
    1. 采样阶段(Sampling Phase):ADC对输入信号进行采样保持(时间由ADC_SampleTime决定)。
    2. 转换阶段(Conversion Phase):ADC将采样值转换为数字量(固定12.5个ADC时钟周期)。
  • 定时器的作用:精确控制每次“采样+转换”的启动时刻,而非单独控制采样或转换。

2. 数据获取的位置

(1)DMA自动传输(推荐方式)
  • 配置步骤
    uint16_t adcValue;  // 存储ADC数据// 启用ADC DMA
    DMA_InitTypeDef DMA_InitStructure;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adcValue;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 1;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    ADC_DMACmd(ADC1, ENABLE);
    
  • 数据获取
    定时器触发ADC后,转换结果会自动通过DMA传输到adcValue变量,无需程序干预
(2)中断中读取
  • 配置步骤
    // 启用ADC转换完成中断
    ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
    NVIC_EnableIRQ(ADC1_2_IRQn);// 中断服务函数
    void ADC1_2_IRQHandler(void) {if (ADC_GetITStatus(ADC1, ADC_IT_EOC)) {adcValue = ADC_GetConversionValue(ADC1);ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);}
    }
    
  • 数据获取
    每次转换完成后,在中断中读取ADC_DR寄存器。
(3)轮询方式(低效,仅用于测试)
// 主循环中等待转换完成
while (1) {ADC_SoftwareStartConvCmd(ADC1, ENABLE);  // 定时器触发时无需此句while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));adcValue = ADC_GetConversionValue(ADC1);
}

3. 完整代码示例(定时器触发+DMA)

目标:TIM2以1kHz触发ADC1采样PA0,DMA自动传输数据。
#include "stm32f10x.h"uint16_t adcValue;  // 存储ADC数据void ADC_Config(void) {// 1. 启用时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);// 2. 配置PA0为模拟输入GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);// 3. 配置TIM2(1kHz触发)TIM_TimeBaseInitTypeDef TIM_InitStructure;TIM_InitStructure.TIM_Period = 72000 - 1;  // 72MHz / 72000 = 1kHzTIM_InitStructure.TIM_Prescaler = 0;TIM_TimeBaseInit(TIM2, &TIM_InitStructure);TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);  // 更新事件触发ADCTIM_Cmd(TIM2, ENABLE);// 4. 配置DMADMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adcValue;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;DMA_InitStructure.DMA_BufferSize = 1;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  // 循环模式DMA_Init(DMA1_Channel1, &DMA_InitStructure);DMA_Cmd(DMA1_Channel1, ENABLE);// 5. 配置ADCADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;ADC_InitStructure.ADC_ScanConvMode = DISABLE;ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 单次转换ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_NbrOfChannel = 1;ADC_Init(ADC1, &ADC_InitStructure);// 6. 配置ADC通道ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);// 7. 启用ADC和DMAADC_DMACmd(ADC1, ENABLE);ADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1));
}int main(void) {ADC_Config();while (1) {// adcValue已通过DMA自动更新,可直接使用float voltage = adcValue * 3.3f / 4095;  // 转换为电压值}
}

4. 关键总结

问题答案
定时器触发的内容触发完整的ADC转换流程(采样+转换)
采样时刻控制由定时器的触发信号决定
数据获取位置DMA自动传输到变量 / 中断中读取 / 轮询ADC_DR寄存器
最优方案定时器硬件触发 + DMA传输(无CPU干预,高效低功耗)

通过这种方式,STM32可以实现精确的定时采样,同时最小化CPU负载。

在STM32的ADC应用中,定时器触发ADC采样时,选择单次转换模式(Single Conversion)还是连续转换模式(Continuous Conversion),取决于具体的应用场景和需求。以下是两者的对比分析和配置建议:


1. 单次转换模式(Single Conversion)

特点
  • 工作流程
    每次定时器触发后,ADC执行一次完整的采样+转换,然后自动停止,等待下一次触发。
  • 功耗:较低(仅在触发时激活ADC)。
  • 数据更新:每次触发后数据更新一次。
适用场景
  • 低频采样:如温度监测(每秒几次)。
  • 电池供电设备:需要最小化功耗。
  • 事件触发采样:如按键按下后采集一次数据。
配置代码
// ADC初始化配置
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; // TIM2触发
ADC_Init(ADC1, &ADC_InitStructure);// 启动ADC(首次启动后,后续由定时器自动触发)
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

2. 连续转换模式(Continuous Conversion)

特点
  • 工作流程
    定时器首次触发后,ADC持续循环执行采样+转换,直到手动停止。
    (注:在硬件触发下,连续转换模式仍依赖定时器的周期性触发。)
  • 功耗:较高(ADC持续运行)。
  • 数据更新:数据持续刷新,速率由定时器触发频率决定。
适用场景
  • 高频实时采样:如音频信号处理(>1kHz)。
  • 控制环路:如电机PID控制,需要严格周期性的数据更新。
  • 多通道扫描:配合DMA实现无缝数据流。
配置代码
// ADC初始化配置
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  // 连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; // TIM2触发
ADC_Init(ADC1, &ADC_InitStructure);// 启动ADC(首次启动后,ADC会持续运行,但每次转换仍由定时器触发)
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

3. 关键区别与选择建议

特性单次转换模式连续转换模式
触发后行为采样一次后停止持续采样,直到手动停止
功耗低(适合电池供电)高(适合持续供电场景)
数据更新频率由定时器触发频率决定由定时器触发频率决定
硬件资源占用ADC间歇工作ADC持续占用
典型应用温度传感器、低速监测电机控制、音频采集
选择原则
  1. 单次转换模式

    • 需要低功耗非连续采样时使用。
    • 即使定时器触发频率很高(如1kHz),每次触发仍只采样一次。
  2. 连续转换模式

    • 需要无缝数据流严格实时性时使用。
    • 注意:在硬件触发下,连续转换模式的实际采样率仍由定时器决定,但ADC核心会保持活跃状态。

4. 常见问题解答

Q1:连续转换模式下,定时器触发还有意义吗?
  • 有意义!连续转换模式只是保持ADC核心处于工作状态,但每次转换的启动时刻仍由定时器精确控制。
  • 若禁用硬件触发(如用软件触发),ADC会以最大速度连续采样(不推荐,可能丢失时序精度)。
Q2:单次模式是否会漏掉定时器触发信号?
  • 不会。每次定时器触发都会启动一次新的转换,即使前一次转换已完成。
  • 但需确保两次触发的间隔大于单次转换的总时间(采样时间 + 12.5周期)。
Q3:如何动态切换模式?
// 运行时切换为单次模式
ADC_ContinuousConvModeCmd(ADC1, DISABLE);// 运行时切换为连续模式
ADC_ContinuousConvModeCmd(ADC1, ENABLE);

5. 完整示例(单次转换 + 定时器触发 + DMA)

#include "stm32f10x.h"uint16_t adcValue;void ADC_Config(void) {// 1. 启用时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);// 2. 配置PA0为模拟输入GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);// 3. 配置TIM2(100Hz触发)TIM_TimeBaseInitTypeDef TIM_InitStructure;TIM_InitStructure.TIM_Period = 720000 - 1;  // 72MHz / 720000 = 100HzTIM_InitStructure.TIM_Prescaler = 0;TIM_TimeBaseInit(TIM2, &TIM_InitStructure);TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);  // 更新事件触发ADCTIM_Cmd(TIM2, ENABLE);// 4. 配置DMA(单次传输)DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adcValue;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;DMA_InitStructure.DMA_BufferSize = 1;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  // 循环模式DMA_Init(DMA1_Channel1, &DMA_InitStructure);DMA_Cmd(DMA1_Channel1, ENABLE);// 5. 配置ADC(单次转换 + TIM2触发)ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;ADC_InitStructure.ADC_ScanConvMode = DISABLE;ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 单次模式ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;ADC_InitStructure.ADC_NbrOfChannel = 1;ADC_Init(ADC1, &ADC_InitStructure);// 6. 配置ADC通道ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);// 7. 启用ADC和DMAADC_DMACmd(ADC1, ENABLE);ADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1));ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1));
}int main(void) {ADC_Config();while (1) {// adcValue每10ms自动更新一次(由TIM2触发)float voltage = adcValue * 3.3f / 4095;}
}

总结

  • 单次转换模式:节能,适合低频或事件触发场景。
  • 连续转换模式:实时性强,适合高频或控制环路。
  • 硬件触发:无论哪种模式,定时器均可精确控制采样间隔。
  • 推荐组合
    • 低功耗应用:单次转换 + 定时器触发 + DMA。
    • 实时控制:连续转换 + 定时器触发 + DMA。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/83319.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pageassist安装(ollama+deepseek-r1)

page-assist网站&#xff1a;https://github.com/n4ze3m/page-assist 首先电脑配置node.js&#xff0c;管理员打开命令窗口输入下面命令下载bun npm install -g buncd 到你想要安装page-assist的地方&#xff08;推荐桌面&#xff09; 输入下列命令 git clone https://gith…

APC 荧光通道专用!Elabscience® CD11b 抗体激发 / 发射光谱精准匹配流式检测

内容概要 Elabscience APC Anti-Mouse/Human CD11b Antibody [M1/70]&#xff08;货号&#xff1a;E-AB-F1081E&#xff09;是一款高特异性荧光标记抗体&#xff0c;适用于流式细胞术&#xff08;FCM&#xff09;&#xff0c;可精准检测小鼠和人类样本中的 CD11b 髓系细胞&…

entity线段材质设置

在cesium中,我们可以改变其entity线段材质,这里以直线为例. 首先我们先创建一条直线 const redLine viewer.entities.add({polyline: {positions: Cesium.Cartesian3.fromDegreesArray([-75,35,-125,35,]),width: 5,material:material, 保存后可看到在地图上创建了一条线段…

大模型数据分析破局之路20250512

大模型数据分析破局之路 本文面向 AI 初学者、数据分析从业者与企业技术负责人&#xff0c;围绕大模型如何为数据分析带来范式转变展开&#xff0c;从传统数据分析困境谈起&#xff0c;延伸到 LLM MCP 的协同突破&#xff0c;最终落脚在企业实践建议。 &#x1f30d; 开篇导语…

【MySQL】索引太多会怎样?

在 MySQL 中&#xff0c;虽然索引可以显著提高查询效率&#xff0c;但过多的索引&#xff08;如超过 5-6 个&#xff09;会带来以下弊端&#xff1a; 1. 存储空间占用增加 每个索引都需要额外的磁盘空间存储索引树&#xff08;BTree&#xff09;。对于大表来说&#xff0c;多个…

使用PocketFlowSharp创建一个Human_Evaluation示例

效果 实践 有时候AI生成的结果我们并不满意在进入下一步之前&#xff0c;我们需要对AI生成的结果进行人工审核&#xff0c;同意了才能进入下一个流程。 Human_Evaluation就是人工判断的一个简单示例。 internal class Program{static async Task Main(string[] args){// Load…

【项目】自主实现HTTP服务器:从Socket到CGI全流程解析

00 引言 ​ 在构建高效、可扩展的网络应用时&#xff0c;理解HTTP服务器的底层原理是一项必不可少的技能。现代浏览器与移动应用大量依赖HTTP协议完成前后端通信&#xff0c;而这一过程的背后&#xff0c;是由网络套接字驱动的请求解析、响应构建、数据传输等一系列机制所支撑…

SQL练习(6/81)

目录 1.寻找连续值 方法一&#xff1a;使用自连接&#xff08;Self-Join&#xff09; 方法二&#xff1a;使用窗口函数&#xff08;Window Functions&#xff09; 2.寻找有重复的值 GROUP BY子句 HAVING子句 常用聚合函数&#xff1a; 3.找不存在某属性的值 not in no…

【流程控制结构】

流程控制结构 流程控制结构1、顺序结构2、选择结构if基本选择结构if else语法多重if语法嵌套if语法switch选择结构 3、循环结构循环结构while循环结构程序调试for循环跳转语句区别 流程控制结构 1、顺序结构 流程图 优先级 2、选择结构 if基本选择结构 单if 语法 if&…

【机器人】复现 UniGoal 具身导航 | 通用零样本目标导航 CVPR 2025

UniGoal的提出了一个通用的零样本目标导航框架&#xff0c;能够统一处理多种类型的导航任务。 支持 对象类别导航、实例图像目标导航和文本目标导航&#xff0c;而无需针对特定任务进行训练或微调。 本文分享UniGoal复现和模型推理的过程&#xff5e; 查找沙发&#xff0c;模…

python + flask 做一个图床

1. 起因&#xff0c; 目的: 对这个网站&#xff1a;https://img.vdoerig.com/ &#xff0c; 我也想实现这种效果。做一个简单的图床&#xff0c;后面&#xff0c;可以结合到其他项目中。 2. 先看效果 实际效果。 3. 过程: Grok 聊天&#xff1a; https://img.vdoerig.co…

Java生产环境设限参数教学

哈哈&#xff0c;这个问题问得好&#xff01;咱们用开餐厅的比喻来理解生产环境的四大必须设限参数&#xff0c;保证你听完再也不会忘&#xff01;&#xff08;搓手手&#xff09; 1. 堆内存上限&#xff1a;-Xmx&#xff08;厨房的最大容量&#xff09; 问题&#xff1a;想象…

电脑出故障驱动装不上?试试驱动人生的远程服务支持

在日常工作或学习中&#xff0c;驱动问题时常成为电脑用户的一大困扰。尤其是在更换硬件、重装系统、驱动冲突等情况下&#xff0c;许多用户往往手足无措&#xff0c;不知道从何下手。而“驱动人生”作为国内领先的驱动管理工具&#xff0c;一直以高效、便捷、智能著称。现在&a…

JS手写代码篇---手写 instanceof 方法

2、手写 instanceof 方法 instancecof用于检测一个对象是否是某个构造函数的实例。它通常用于检查对象的类型&#xff0c;尤其是在处理继承关系时。 eg: const arr [1,2,3,4,5]console.log(arr instanceof Array); // trueconsole.log(arr instanceof Object); // true那这是…

使用exceljs将excel文件转化为html预览最佳实践(完整源码)

前言 在企业应用中&#xff0c;我们时常会遇到需要上传并展示 Excel 文件的需求&#xff0c;以实现文件内容的在线预览。经过一番探索与尝试&#xff0c;笔者最终借助 exceljs 这一库成功实现了该功能。本文将以 Vue 3 为例&#xff0c;演示如何实现该功能&#xff0c;代码示例…

PMP-第十二章 项目采购管理

项目采购管理核心概念 项目采购管理包括从项目团队外部采购或获取所需产品、服务或成果的各个过程项目组织既可以是买方&#xff08;甲方&#xff09; &#xff0c;也可以是卖方&#xff08;乙 方&#xff09;项目采购管理过程围绕协议来进行&#xff0c;协议是买卖双方之间具…

maven和npm区别是什么

这是一个很容易搞糊涂新手的问题&#xff0c;反正我刚开始从课堂的知识转向项目网站开发时&#xff0c;被这些问题弄得晕头转向&#xff0c;摸不着头脑&#xff0c;学的糊里糊涂&#xff0c;所以&#xff0c;写了这么久代码&#xff0c;也总结一下&#xff0c;为后来者传授下经…

Leetcode76覆盖最小子串

覆盖最小子串 代码来自b站左程云 class Solution {public String minWindow(String str, String tar) {char[] s str.toCharArray();char[] t tar.toCharArray();int[] cnt new int[256];for (char cha : t) { cnt[cha]--;}int len Integer.MAX_VALUE;int debt t.length…

Linux du 命令终极指南:从基础到精通

文章目录 Linux du 命令终极指南&#xff1a;从基础到精通du 命令简介常用参数详解常见用法示例查看当前目录总大小查看当前目录及其子目录占用空间只显示当前目录总占用空间查看目录下每个文件和子目录的大小查看某目录深度为 1 的大小分布查看某目录并排除日志文件查看多个目…

sychronized原理(嚼碎了喂版)

先说一下心得吧&#xff0c;我们知道硬软不分家&#xff0c;在学习底层原理的时候我们不需要死扣到底&#xff0c;没必要把硬件方面全吃透&#xff0c;点到为止&#xff0c;学到能够帮助理解代码即可&#xff0c;我们的目标是写出高性能的代码&#xff0c;而不是创造出硬软一体…