在STM32的ADC模块中,**采样时机(Sampling Time)和转换时机(Conversion Time)**是ADC工作流程中的两个关键阶段,直接影响采样精度和系统实时性。以下是详细解析:
ADC_SMPRx
寄存器配置,决定采样电容充电时间。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;
信号类型 | 推荐采样时间 | 原因 |
---|---|---|
低阻抗信号 | 1.5~28.5周期 | 信号源阻抗低(如运放输出),快速稳定。 |
高阻抗信号 | 55.5~239.5周期 | 信号源阻抗高(如温度传感器、分压电路),需更长时间充电。 |
内部通道 | ≥239.5周期 | 内部温度传感器和VREFINT阻抗极高,必须延长采样时间。 |
// 配置PA0(低阻抗)和温度传感器(高阻抗)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5); // 快速采样
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_239Cycles5); // 慢速采样
[
T_{total} = (T_{sampling} + 12.5) \times \frac{1}{f_{ADC}}
]
T_total=4.86μs
,则f_max≈205kHz
(单通道)。采样阶段 转换阶段
|-------- Sampling Time --------|-- 12.5 Cycles --|
|<----------- Total Conversion Time ------------>|
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 72MHz/6=12MHz
根据信号类型切换采样时间:
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);
}
高优先级信号通过注入组立即响应:
// 过压时紧急采样
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);
}
}
避免CPU轮询,提高效率:
uint16_t adcValues[3];
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adcValues;
ADC_DMACmd(ADC1, ENABLE);
在STM32的ADC编程中,采样、转换和数据获取的流程分布在程序的不同位置,具体取决于触发方式(软件/硬件)和数据读取方式(轮询/DMA/中断)。以下是基于STM32F103的详细说明和代码示例:
// 在需要采样的地方(如主循环或定时器回调中)
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);
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);
}
}
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
uint16_t adcValue = ADC_GetConversionValue(ADC1); // 读取规则组数据
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);
}
}
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);
// 处理紧急数据(如过压保护)
}
}
操作 | 程序位置 | 常用函数/配置 |
---|---|---|
采样触发 | 主循环/定时器回调/中断 | ADC_SoftwareStartConvCmd() |
采样时间 | ADC初始化阶段 | ADC_RegularChannelConfig() |
转换等待 | 轮询/中断 | ADC_GetFlagStatus() 或中断标志 |
数据读取 | 轮询/DMA/中断 | ADC_GetConversionValue() 或DMA数组 |
在STM32的ADC配置中,**连续扫描(Scan Mode)和连续转换(Continuous Conversion Mode)**是两个关键的工作模式,它们决定了ADC如何对多个通道进行采样和数据更新。以下是详细解释:
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通道
uint16_t adcValues[3]; // 存储多通道数据
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adcValues;
ADC_DMACmd(ADC1, ENABLE);
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 启用连续转换
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 启动后自动循环采样
模式组合 | 行为 | 适用场景 |
---|---|---|
扫描+单次转换 | 按顺序采样所有通道后停止 | 定时触发多通道采样(如每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);
数据覆盖问题
ADC_DR
寄存器会被新数据覆盖,必须使用DMA或中断及时读取。触发源选择
功耗权衡
注入组优先级
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)**的选择直接影响系统的实时性、功耗和采样精度。以下是针对您提到的两点的详细解释:
// 配置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控制)。
// 配置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);
模式 | 平均电流(示例) | 适用场景 |
---|---|---|
连续转换(1kHz) | ~1.2mA | 实时控制、高速信号处理 |
单次转换+定时(1Hz) | ~50μA | 电池供电的传感器监测 |
void Set_ADCMode(uint8_t isHighSpeed) {
ADC_ContinuousConvModeCmd(ADC1, isHighSpeed ? ENABLE : DISABLE);
TIM_SetAutoreload(TIM2, isHighSpeed ? 7200 - 1 : 72000000 - 1); // 切换采样率
}
通过合理选择转换模式,可平衡性能与功耗,满足不同应用场景需求。
在STM32的ADC应用中,定时器触发ADC采样通常不需要在定时器中断服务函数(如TIMx_IRQHandler
)中编写代码,而是直接通过定时器的硬件触发输出(如TRGO)自动控制ADC启动。以下是具体说明和代码示例:
// 配置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); // 启动定时器
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; // TIM2触发
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 单次转换模式(推荐低功耗)
ADC_Init(ADC1, &ADC_InitStructure);
如果需要在定时器事件中执行其他任务(如数据处理、状态检查),才需启用中断。此时需:
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 允许更新中断
NVIC_EnableIRQ(TIM2_IRQn); // 启用NVIC中断
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update)) {
// 此处可添加其他任务(如读取ADC数据、控制逻辑)
// 注意:ADC采样已由硬件自动触发,无需在此启动!
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中断标志
}
}
#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); // 更新事件触发ADC
TIM_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. 启用ADC
ADC_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);
}
}
}
ADC_SampleTime
决定)。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);
adcValue
变量,无需程序干预。// 启用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
寄存器。// 主循环中等待转换完成
while (1) {
ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 定时器触发时无需此句
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
adcValue = ADC_GetConversionValue(ADC1);
}
#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 = 1kHz
TIM_InitStructure.TIM_Prescaler = 0;
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // 更新事件触发ADC
TIM_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_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. 配置ADC
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和DMA
ADC_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; // 转换为电压值
}
}
问题 | 答案 |
---|---|
定时器触发的内容 | 触发完整的ADC转换流程(采样+转换) |
采样时刻控制 | 由定时器的触发信号决定 |
数据获取位置 | DMA自动传输到变量 / 中断中读取 / 轮询ADC_DR 寄存器 |
最优方案 | 定时器硬件触发 + DMA传输(无CPU干预,高效低功耗) |
通过这种方式,STM32可以实现精确的定时采样,同时最小化CPU负载。
在STM32的ADC应用中,定时器触发ADC采样时,选择单次转换模式(Single Conversion)还是连续转换模式(Continuous Conversion),取决于具体的应用场景和需求。以下是两者的对比分析和配置建议:
// 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);
// 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);
特性 | 单次转换模式 | 连续转换模式 |
---|---|---|
触发后行为 | 采样一次后停止 | 持续采样,直到手动停止 |
功耗 | 低(适合电池供电) | 高(适合持续供电场景) |
数据更新频率 | 由定时器触发频率决定 | 由定时器触发频率决定 |
硬件资源占用 | ADC间歇工作 | ADC持续占用 |
典型应用 | 温度传感器、低速监测 | 电机控制、音频采集 |
单次转换模式:
连续转换模式:
// 运行时切换为单次模式
ADC_ContinuousConvModeCmd(ADC1, DISABLE);
// 运行时切换为连续模式
ADC_ContinuousConvModeCmd(ADC1, ENABLE);
#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 = 100Hz
TIM_InitStructure.TIM_Prescaler = 0;
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // 更新事件触发ADC
TIM_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和DMA
ADC_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;
}
}