使用定时器的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模式,还有计数模式也有影响.这两个模式都挺好理解,但是还有其他三个比较奇怪的.
- LL_TIM_COUNTERMODE_CENTER_UP
- LL_TIM_COUNTERMODE_CENTER_DOWN
- 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);
这次先到这里吧...