在上一篇帖子中,我们使用systick与软件触发的方式实现周期采样,简单并且易于实现。不过,软件触发有一个弊端。它的周期性精度不足,受中断打断影响及其他任务执行时长阻塞,ADC采样周期在中心点或多或少有偏差。如果我们需要稳定周期的ADC采样周期,则需要本帖所述的定时器触发的方式实现。
ADC外设通道采样触发,除了软件触发,还可以通过定时器事件触发。在使用定时器的事件触发时,整个触发过程逻辑与信号均在MCU内部,全部无软件干预。正是由于软件全程无干预,再加上硬件定时器的触发时间精度保证,这样也就保证了ADC定时周期采样的高精度。
使用STM32CubeMX来实现的配置也非常简单

我这里使用的是寄存器实现的方法,和现在的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而已;二来,代码也没有那么难,是吧!
我要赚赏金
