定时器(PWM输出)触发ADC采样(DMA)——STM32CubeMX

文章介绍了如何在STM32F103单片机上使用定时器TIM1的PWM模式控制单通道ADC转换,强调了DMA的重要性。随后扩展到双通道转换时的注意事项,包括扫描模式和连续转换模式的禁用。还讨论了PWM频率和占空比的计算以及TIMx寄存器的作用。
摘要由CSDN通过智能技术生成

ADC基础知识http://t.csdnimg.cn/x6PhU

一、(单通道)使用定时器触发的ADC单通道转换

我用的单片机是STM32F103CBTX

  • 定时器:使用PWM输出的模式
  • ADC:使用DMA的模式(在不使用DMA的情况下,定时器控制ADC进行数据采集只能是单通道!如果开启了多通道,读取到的ADC采集值只会是最后一个通道的值!所以,要想使用定时器控制ADC采集多通道必须使用DMA!)

定时器中cubemx配置:

 看STM32F103数据手册可以知道定时器TIM1是挂在APB1总线上。即定时器 1 的频率是 72MHZ。

c50fe6156bd84ffba36b089adc5eb16e.png

我使用的是TIM1的通道2的上升沿(即PWM输出)触发ADC采样 

2ccd60693a12402bae46a6c9b258b518.png

PWM模式1和模式2区别:

以下是使用逻辑分析仪观察到的PWM输出情况

c665e23e417947b4a7376cd3a067a2f8.png

137b6339728a4e51b11a9c8b6a2d94fe.png

ADC中cubemx配置: 

注意:此时不再需要ADC进行转换,把连续转换模式设为关闭!!!

(我自己踩的坑,我试过开启的时候程序死掉了)

a2554d931b4f447e994f58c0fad6cd46.png

DMA模式选择: 

Circular模式:

就是ADC连续采样的意思,也就是HAL_ADC_Start_DMA(&hadc1, (uint32_t *)ADC_Value, 4)这句代码在程序中开启一次即可,不需要每次都重新打开。

Normal模式:

HAL_ADC_Start_DMA(&hadc1, (uint32_t *)ADC_Value, 4)执行完一次之后,如果想进行下一轮ADC采样,必须要重新开启,所以每一轮采样都要用一次这句代码,采样一次开启一次采样一次开启一次。

参考别人的试了用了Circular模式,完全没问题,可以成功测出电压值。

我自己也试了Normal模式,不行,只能测一次,后面没反应了。

a8d2ba26f6304b67b830a6bc98196a48.png

 代码:

uint8_t AdcConvEnd=0;

int main()
{
  uint16_t ADC_Value[4]= 0;

  HAL_TIM_Base_Start(&htim1);
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);

  HAL_ADCEx_Calibration_Start(&hadc1);//自动校准
  HAL_ADC_Start_DMA(&hadc1, (uint32_t *)ADC_Value, 4);


  while(1)
  {
      if(AdcConvEnd)
      {
          uint16_t averageValue = 0;
          for(int i = 0; i < 4; i++)
          {
              averageValue += ADC_Value[i];
              printf("ADC_Value[%d]:%.3f\r\n",i,(float)ADC_Value[i]*3.3f/4096);
          }
          AdcConvEnd=0;

          averageValue/=4;
          printf("averageValue:%d\r\n",averageValue);
      }
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc==&hadc1)
    {
        AdcConvEnd=1;
    }
}

结果:

b14f3b94fe984e4cb342b1e8ca014931.png

二、(双通道)使用定时器触发的ADC通道转换 

注意:因为我们设置的是 2 个通道的采集,所以应该使能 ADC 的扫描模式,另一方面,我们采用的是 TIM 产生 pwm 触发 adc 进行采集,所以要禁止 ADC 的连续转换模式,这就是两个需要注意的地方。

ADC中cubemx配置: 

2bbbb2326c00463286845cbe99cb6a6c.png

三、关于PWM的频率或周期的计算

ef4c8d32cb554af9918b6697581516ac.png

*注 :* ARR和PSC都是16位的寄存器, 数值一定要在0~65535这个范围。

当我们设置了当前机器设置的是 72MHZ和 72分频,定时器输入时钟频率是72MHz / 72 = 1MHz:

  • 1MHZ ,即1秒计数 1M 次=1000000 次(1s产生1000000个脉冲方波)。
  • 1MHZ ,也就是1us产生1个脉冲方波。

若重装载值 (Period 或 ARR)设置为 500。也就是0.1us*500=500us,对于当前 PWM 周期为 500us,也就是500us发生一次中断。

当重装载值(Period 或 ARR)设置为 500 时,意味着定时器需要计数 500 个时钟周期才会触发中断。因此,中断发生的周期是 500us(即500 * 1us

若Pulse设为250,即占空比=Pulse/ARR=250/500=50%

四、寄存器TIMx_ARR、TIMx_CCRx 、TIMx_CNT

向上计数为例,重载值为ARR,比较值为CRRx

8e27aba90c9a4ff18d20c25dda041fb2.png

上图可以看出:

  • 0-t1段,定时器计数器TIMx_CNT值小于CCRx值,输出低电平
  • t1-t2段,定时器计数器TIMx_CNT值大于CCRx值,输出高电平

TIMx_CNT值达到ARR时,定时器溢出,重新向上计数...循环此过程至此一个PWM周期完成

上图更加形象的说明了

信号频率由 TIMx_ARR 寄存器值决定。

占空比则由 TIMx_CCRx 寄存器值决定。

参考:

STM32 HAL库:ADC+DMA应用(连续采样、触发采样)_hal adc dma-CSDN博客

【STM32】定时器TIM触发ADC采样,DMA搬运到内存(超详细讲解)-CSDN博客

STM32上,可以通过PWM触发ADC采样。具体步骤如下: 1. 配置TIMxPWM模式,设置相关参数,如频率、占空比等。 2. 配置ADC采样模式,设置相关参数,如采样时间、采样通道等。 3. 配置ADC触发源为TIMx的TRGO事件。 4. 启动TIMxADC。 下面是一个示例代码,用于实现PA0通道的ADC采样,使用TIM3的PWM触发: ``` #include "stm32f10x.h" void TIM3_PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period = 999; // PWM周期为1ms TIM_TimeBaseStructure.TIM_Prescaler = 71; // PWM时钟频率为72MHz/72=1MHz TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 499; // PWM占空比为50% TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM3, &TIM_OCInitStructure); TIM_Cmd(TIM3, ENABLE); } void ADC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; // 触发源为TIM3的TRGO事件 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); // 采样时间为239.5个时钟周期 ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); } int main(void) { uint16_t adc_value; TIM3_PWM_Init(); ADC_Init(); while(1) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); adc_value = ADC_GetConversionValue(ADC1); } } ``` 在上面的示例代码中,TIM3被配置为PWM模式,频率为1kHz,占空比为50%。ADC1被配置为单次转换模式,采样时间为239.5个时钟周期,触发源为TIM3的TRGO事件。 在主循环中,通过ADC_SoftwareStartConvCmd()函数启动ADC转换,并使用ADC_GetFlagStatus()函数等待转换完成。转换完成后,使用ADC_GetConversionValue()函数获取采样值。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值