[STM32定时器学习-003]OC的计数模式和中断

/ 0评 / 1

使用定时器的OC模式,可以把定时器分成5个小定时器使用,其中一个是TimeBase,另外其他都可以用作小定时器.同样要满足OCx 的时间会比ARR的短.同时,这不是一个周期定时器.他会走完最长的那个周期,然后才能回去.实现这个又简单又有陷阱.初始化方法.

    /* Count Up */
    LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP);
    /* 1 kHz */
    LL_TIM_SetPrescaler(TIM1, __LL_TIM_CALC_PSC(SystemCoreClock, 1000));
    /* 0.01 Hz */
    InitialAutoreload = __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), 0.01);
    LL_TIM_SetAutoReload(TIM1, InitialAutoreload);

然后配置OC的时间,OC的模式可以随意(按照实际配置).

    LL_TIM_OC_SetCompareCH1(TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), 0.2));
    LL_TIM_OC_SetCompareCH2(TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), 0.5));
    LL_TIM_OC_SetCompareCH3(TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), 2));
    LL_TIM_OC_SetCompareCH4(TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), 100));
    /* Enable TIM2_CCR1 register preload. Read/Write operations access the      */
    /* preload register. TIM2_CCR1 preload value is loaded in the active        */
    /* at each update event.                                                    */
    LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1);
    LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH2);
    LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3);
    LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH4);
    /**************************/
    /* TIM2 interrupts set-up */
    /**************************/
    /* Enable the capture/compare interrupt for channel 1*/
    LL_TIM_EnableIT_CC1(TIM1);
    LL_TIM_EnableIT_CC2(TIM1);
    LL_TIM_EnableIT_CC3(TIM1);
    LL_TIM_EnableIT_CC4(TIM1);
    /**********************************/
    /* Start output signal generation */
    /**********************************/
    /* Enable output channel 1 */
    LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1);
    LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH2);
    LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH3);
    LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH4);

中断处理是陷阱,无论是否开启了对应的CC中断,标志位依然会置位,所以进入中断时候还要判断是不是已经使能了对应中断.

void TIM1_CC_IRQHandler(void){
	  /* Check whether CC1 interrupt is pending */
  if(LL_TIM_IsEnabledIT_CC1(TIM1) == 1 && LL_TIM_IsActiveFlag_CC1(TIM1) == 1)
  {
    /* Clear the update interrupt flag*/
    LL_TIM_ClearFlag_CC1(TIM1);
  }
  if(LL_TIM_IsEnabledIT_CC2(TIM1) == 1 && LL_TIM_IsActiveFlag_CC2(TIM1) == 1)
  {
    /* Clear the update interrupt flag*/
    LL_TIM_ClearFlag_CC2(TIM1);
  }
  if(LL_TIM_IsEnabledIT_CC3(TIM1) == 1 && LL_TIM_IsActiveFlag_CC3(TIM1) == 1)
  {
    /* Clear the update interrupt flag*/
    LL_TIM_ClearFlag_CC3(TIM1);
  }
  if(LL_TIM_IsEnabledIT_CC4(TIM1) == 1 && LL_TIM_IsActiveFlag_CC4(TIM1) == 1)
  {
    /* Clear the update interrupt flag*/
    LL_TIM_ClearFlag_CC4(TIM1);
  }
}

然后就可以用了.开始说说计数模式.我们一直在测试LL_TIM_COUNTERMODE_UP模式.我们代码初始化是这样的.

    /* Count Up */
    LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP);
    /* 1 MHz */
    LL_TIM_SetPrescaler(TIM1, __LL_TIM_CALC_PSC(SystemCoreClock, 1000000));
    /* 1 kHz */
    InitialAutoreload = __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), 1000);
    LL_TIM_SetAutoReload(TIM1, InitialAutoreload);
    LL_TIM_EnableARRPreload(TIM1);
    LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1);
    LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH2, LL_TIM_OCMODE_PWM1);
    LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_PWM1);
    LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH4, LL_TIM_OCMODE_PWM1);
    LL_TIM_OC_SetCompareCH1(TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), 8000));
    LL_TIM_OC_SetCompareCH2(TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), 6000));
    LL_TIM_OC_SetCompareCH3(TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), 4000));
    LL_TIM_OC_SetCompareCH4(TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), 2000));

时序图如下.

然后只要把LL_TIM_COUNTERMODE_UP改成LL_TIM_COUNTERMODE_DOWN,那么整个波形就非常像PWM2了,因为从ARR一直减到比较匹配才翻转为高.

所以是先低后高还是先高后低,除了PWM模式,还有计数模式也有影响.这两个模式都挺好理解,但是还有其他三个比较奇怪的.

  1. LL_TIM_COUNTERMODE_CENTER_UP
  2. LL_TIM_COUNTERMODE_CENTER_DOWN
  3. LL_TIM_COUNTERMODE_CENTER_UP_DOWN

这三个都是向上,然后向下计数,他们都会触发电平翻转,而且波形就是UP+DOWN周期反复,但是,中断的时机就不一样了.这三个波形都这样,轻易想象吧,先UP后DOWN计数然后反复.

本文开头把中断和这个一起说,因为现在该翻IO验证中断发生时机了.把PC6,PC7,PC8,PC9都用起来,TimeBase的不观察了,因为没那么多通道.

LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_6, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_7, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_8, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_9, LL_GPIO_MODE_OUTPUT);

中断服务函数如下.

void TIM1_CC_IRQHandler(void)
{
    if(LL_TIM_IsEnabledIT_CC1(TIM1) == 1 && LL_TIM_IsActiveFlag_CC1(TIM1) == 1)
    {
        LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_6);
        LL_TIM_ClearFlag_CC1(TIM1);
        LL_GPIO_ResetOutputPin(GPIOC, LL_GPIO_PIN_6);
    }
    if(LL_TIM_IsEnabledIT_CC2(TIM1) == 1 && LL_TIM_IsActiveFlag_CC2(TIM1) == 1)
    {
        LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_7);
        LL_TIM_ClearFlag_CC2(TIM1);
        LL_GPIO_ResetOutputPin(GPIOC, LL_GPIO_PIN_7);
    }
    if(LL_TIM_IsEnabledIT_CC3(TIM1) == 1 && LL_TIM_IsActiveFlag_CC3(TIM1) == 1)
    {
        LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_8);
        LL_TIM_ClearFlag_CC3(TIM1);
        LL_GPIO_ResetOutputPin(GPIOC, LL_GPIO_PIN_8);
    }
    if(LL_TIM_IsEnabledIT_CC4(TIM1) == 1 && LL_TIM_IsActiveFlag_CC4(TIM1) == 1)
    {
        LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_9);
        LL_TIM_ClearFlag_CC4(TIM1);
        LL_GPIO_ResetOutputPin(GPIOC, LL_GPIO_PIN_9);
    }
}

比如LL_TIM_COUNTERMODE_CENTER_UP的中断时机我们就看到了.(PWM1模式下,都在上升沿中断.PWM2模式相反,或者LL_TIM_COUNTERMODE_CENTER_DOWN相反.)

还有个UP_DOWN模式,就是两个时机都会中断.

如果时间宽度看不出区别,看看普通的UP模式,就知道了,时间差距还是挺大的.(可以用3ms - 4ms刻度位这些来判断.)

如果要发生7路PWM,只需要添加复用和使能就可以.

LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH2);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH3);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH4);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH2N);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH3N);

这次先到这里吧...

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注