TaterLi's LazyBlog

自言自语,不喜绕路,科学上网,远离天国.

@TaterLi2月前

12/26
21:50
技术控

STM32定时器的PWM输入

PWM输入捕获是输入捕获的一个特例,他要捕获PWM的周期和脉宽.一个定时器刚好链接两个IC输入.

那么PWM捕获输入的原理,也就从这里打通经脉.

高深的不说,那是各种什么原理,没必要了解,我们最重要是目的.看看实际逻辑.

IC2就是占空比,IC1就是周期长度,这样PWM就捕获了,如果频率太高,估计还要DMA,但是至少不用进入后然后更换边沿再捕捉.配置方法也说了.

看这么多,晕了,其实就是说明,Cube里面这么配置,就OK了.记得改Period(ARR)是0xFFFF哦.

生成的定时器初始化代码如下(最后5行是后自己打上的):

void TIM21_PWMInput(void){

  LL_TIM_InitTypeDef TIM_InitStruct;

  LL_GPIO_InitTypeDef GPIO_InitStruct;

  /* Peripheral clock enable */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM21);
  
  /**TIM21 GPIO Configuration  
  PA3   ------> TIM21_CH2 
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_3;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_0;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* TIM21 interrupt Init */
  NVIC_SetPriority(TIM21_IRQn, 0);
  NVIC_EnableIRQ(TIM21_IRQn);

  TIM_InitStruct.Prescaler = 0;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 0xFFFF;
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  LL_TIM_Init(TIM21, &TIM_InitStruct);

  LL_TIM_SetClockSource(TIM21, LL_TIM_CLOCKSOURCE_INTERNAL);

  LL_TIM_SetTriggerInput(TIM21, LL_TIM_TS_TI2FP2);

  LL_TIM_SetSlaveMode(TIM21, LL_TIM_SLAVEMODE_RESET);

  LL_TIM_CC_DisableChannel(TIM21, LL_TIM_CHANNEL_CH2);

  LL_TIM_IC_SetFilter(TIM21, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);

  LL_TIM_IC_SetPolarity(TIM21, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);

  LL_TIM_DisableIT_TRIG(TIM21);

  LL_TIM_DisableDMAReq_TRIG(TIM21);

  LL_TIM_SetTriggerOutput(TIM21, LL_TIM_TRGO_RESET);

  LL_TIM_DisableMasterSlaveMode(TIM21);

  LL_TIM_IC_SetActiveInput(TIM21, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI);

  LL_TIM_IC_SetPrescaler(TIM21, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);

  LL_TIM_IC_SetFilter(TIM21, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);

  LL_TIM_IC_SetPolarity(TIM21, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING);

  LL_TIM_IC_SetActiveInput(TIM21, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);

  LL_TIM_IC_SetPrescaler(TIM21, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
	
	LL_TIM_EnableIT_CC1(TIM21);
	LL_TIM_EnableIT_CC2(TIM21);	
	
	LL_TIM_CC_EnableChannel(TIM21,LL_TIM_CHANNEL_CH1);
	LL_TIM_CC_EnableChannel(TIM21,LL_TIM_CHANNEL_CH2);
	LL_TIM_EnableCounter(TIM21);
}

注意,最后5行是自己打的,特缩进来大家看看,这就是开启时钟和CC捕获.然后数值计算的中断.

void TIM21_IRQHandler(void)
{
	if(LL_TIM_IsActiveFlag_CC1(TIM21)){
		LL_TIM_ClearFlag_CC1(TIM21);
	}
	
	if(LL_TIM_IsActiveFlag_CC2(TIM21)){
		LL_TIM_ClearFlag_CC2(TIM21);
	}
	
	uwIC2Value =  LL_TIM_OC_GetCompareCH2(TIM21);
	
	if (uwIC2Value != 0)
	{
		uwDutyCycle = ((LL_TIM_OC_GetCompareCH1(TIM21)) * 50) / uwIC2Value;
		uwFrequency = 16000000 / uwIC2Value;
	}
	else
	{
		uwDutyCycle = 0;
		uwFrequency = 0;
	}
}

我主频32MHz,定时器频率也是32MHz,设置公式写16MHz,而占空比部分放大了100倍才能观察,不然是float型的,所以乘以100除以2,反正什么都折半算就对了.但是官方也有PWM Input参考代码不用折半,但是我发现结果不对,后来折半后结果就对了.看效果.用的555生成点波形.看,这是25%占空比,245Hz.(此处25%指的是高电平的范围)

验证:

深入思考:

这个频率计,可以捕获的频率范围是多少呢?

答->可测得最小频率是 TIM时钟频率/65535 

精度又是多少呢?

答->精度就是 1/TIM时钟频率

怎么做一个自适应精度宽频率的测频计呢?

答->当频率过低的时候,增加TIM的分频系数,当频率过高时候,减少TIM的分频系数.

STM32L011K4-PWMInput

STM32定时器的PWM输入