这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » ADC周期采样之定时器周期采样

共2条 1/1 1 跳转至

ADC周期采样之定时器周期采样

院士
2026-05-20 09:03:38     打赏

在上一篇帖子中,我们使用systick与软件触发的方式实现周期采样,简单并且易于实现。不过,软件触发有一个弊端。它的周期性精度不足,受中断打断影响及其他任务执行时长阻塞,ADC采样周期在中心点或多或少有偏差。如果我们需要稳定周期的ADC采样周期,则需要本帖所述的定时器触发的方式实现。

ADC外设通道采样触发,除了软件触发,还可以通过定时器事件触发。在使用定时器的事件触发时,整个触发过程逻辑与信号均在MCU内部,全部无软件干预。正是由于软件全程无干预,再加上硬件定时器的触发时间精度保证,这样也就保证了ADC定时周期采样的高精度。

使用STM32CubeMX来实现的配置也非常简单

ADC周期采样01.jpg

我这里使用的是寄存器实现的方法,和现在的HAL库是一样的。

void ADCInit_GPIO(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOF时钟

    //ADC通道初始化
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;  //模拟输入
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  //不带上下拉
    GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化     
}

void ADCInit_ADC(void)
{
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
    ADC_InitTypeDef       ADC_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC3时钟
    
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);      //ADC3复位
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE); //复位结束     

    ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式
    ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; //两个采样阶段之间的延迟5个时钟(三重模式或双重模式下使用)
    ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //DMA失能(对于多个ADC模式)
    ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//预分频4分频。ADCCLK=PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz 
    ADC_CommonInit(&ADC_CommonInitStructure);//初始化
    
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位模式
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描模式(多通道ADC采集要用扫描模式)    
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; //上升沿触发
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;  //定时器事件2触发ADC
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐    
    ADC_InitStructure.ADC_NbrOfConversion = ADC_CHANNEL;    //8个转换在规则序列中
    ADC_Init(ADC3, &ADC_InitStructure);//ADC初始化
    
    //连续模式下,通道的配置
    ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 1, ADC_SampleTime_15Cycles);
    ADC_Cmd(ADC1, ENABLE);//开启AD转换器    
}

void ADCInit_Timer(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);          

    //失能定时器
    TIM_Cmd(TIM2, DISABLE);
    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); //初始化定时器
    
    //定时器设置,T=(84 * 625) / 84M = 625us = 20ms/32,50Hz信号,每周期采样32个点,采样频率1600
    TIM_TimeBaseStructure.TIM_Prescaler = 84-1; //分频系数,TIM2时钟在分频系数不为1时,时钟是APB1时钟的两倍84M
    TIM_TimeBaseStructure.TIM_Period = 312500/CurrentFreq - 1; //周期值1M/32=31250,CurrentFreq单位为0.1hz,因此需扩大10倍
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频因子
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up ; //向上计数模式
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //初始化定时器2
    
    //使能定时器中断    
    TIM_ARRPreloadConfig(TIM2, ENABLE); //允许TIM2定时重载
    TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);  //选择TIM2的UPDATA事件更新为触发源
    
    //使能TIM2
    TIM_Cmd(TIM2, ENABLE); 
}

对于ADC的应用,我建议大家如果需要周期采样则尽量使用Timer的方式。一来周期精度可以满足,也仅仅多使用一个Timer而已;二来,代码也没有那么难,是吧!





关键词: ADC     Timer    

专家
2026-05-20 11:26:13     打赏
2楼

谢谢分享


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]