TMR时钟模块初探 Ø 问题 客户想用i.MX RT1050输出PWM波形,以SDK库中的inputcapture_outputpwm工程作为模板工程进行测试,发现从MIMXRT1050EVKB评估板上J23-5脚输出的PWM波并不符合readme描述的频率:50 KHz和占空比:50%,PWM真实的频率略低一点,约为49.7 KHz,刚开始的时候还以为是示波器的原因,换了一台新的示波器后,测出来的频率值还是跟原来的一样,即基本能够确定问题出在代码本身。
图 1 Ø 错误分析 TMR时钟模块能够实现频率固定和可调的PWM,具体的实现机制和步骤建议请浏览参考手册中的52.6.5.13Fixed-Frequency PWM Mode和52.6.5.14 Variable-FrequencyPWM Mode章节来了解详情,在这里就不做过多的介绍了。
inputcapture_outputpwm工程采用的是Variable-Frequency PWM Mode来输出PWM波的,该机制输出的PWM占空比和频率由比较寄存器COMP1和COMP2的值决定(如下图所示)。但是有个小细节容易被忽略,就是OFLAG输出引脚电平的改变是在时钟计数与比较寄存器发生比较事件后的下一个时钟时刻,这就需要在计算比较寄存器COMP1和COMP2的值时要格外注意。 图 2 检查inputcapture_outputpwm工程中产生PWM的函数: status_t QTMR_SetupPwm(
TMR_Type *base,qtmr_channel_selection_t channel, uint32_t pwmFreqHz, uint8_t dutyCyclePercent,bool outputPolarity, uint32_t srcClock_Hz);就会发现该函数在配置比寄存器和比较加载寄存器时未减1,这将导致PWM周期变大,而使得频率比预期的降低了些,这就是问题所在。 - status_t QTMR_SetupPwm(TMR_Type *base,
- qtmr_channel_selection_t channel,
- uint32_t pwmFreqHz,
- uint8_t dutyCyclePercent,
- bool outputPolarity,
- uint32_t srcClock_Hz)
- {
- uint32_t periodCount, highCount, lowCount, reg;
- if (dutyCyclePercent > 100)
- {
- /* Invalid dutycycle */
- return kStatus_Fail;
- }
- /* Set OFLAG pin for output mode and force out a low on the pin */
- base->CHANNEL[channel].SCTRL |= (TMR_SCTRL_FORCE_MASK | TMR_SCTRL_OEN_MASK);
- /* Counter values to generate a PWM signal */
- periodCount = (srcClock_Hz / pwmFreqHz);
- highCount = (periodCount * dutyCyclePercent) / 100;
- lowCount = periodCount - highCount;
- /* Setup the compare registers for PWM output */
- // base->CHANNEL[channel].COMP1 = lowCount;
- // base->CHANNEL[channel].COMP2 = highCount;
- /* Setup the pre-load registers for PWM output */
- // base->CHANNEL[channel].CMPLD1 = lowCount;
- // base->CHANNEL[channel].CMPLD2 = highCount;
- /* Setup the compare registers for PWM output */
- /*根据上面的提示,在实际配置比寄存器和比较加载寄存器值,需要减1*/
- base->CHANNEL[channel].COMP1 = lowCount-1;
- base->CHANNEL[channel].COMP2 = highCount-1;
- /* Setup the pre-load registers for PWM output */
- base->CHANNEL[channel].CMPLD1 = lowCount-1;
- base->CHANNEL[channel].CMPLD2 = highCount-1;
- reg = base->CHANNEL[channel].CSCTRL;
- /* Setup the compare load control for COMP1 and COMP2.
- * Load COMP1 when CSCTRL[TCF2] is asserted, load COMP2 when CSCTRL[TCF1] is asserted
- */
- reg &= ~(TMR_CSCTRL_CL1_MASK | TMR_CSCTRL_CL2_MASK);
- reg |= (TMR_CSCTRL_CL1(kQTMR_LoadOnComp2) | TMR_CSCTRL_CL2(kQTMR_LoadOnComp1));
- base->CHANNEL[channel].CSCTRL = reg;
- if (outputPolarity)
- {
- /* Invert the polarity */
- base->CHANNEL[channel].SCTRL |= TMR_SCTRL_OPS_MASK;
- }
- else
- {
- /* True polarity, no inversion */
- base->CHANNEL[channel].SCTRL &= ~TMR_SCTRL_OPS_MASK;
- }
- reg = base->CHANNEL[channel].CTRL;
- reg &= ~(TMR_CTRL_OUTMODE_MASK);
- /* Count until compare value is reached and re-initialize the counter, toggle OFLAG output
- * using alternating compare register
- */
- reg |= (TMR_CTRL_LENGTH_MASK | TMR_CTRL_OUTMODE(kQTMR_ToggleOnAltCompareReg));
- base->CHANNEL[channel].CTRL = reg;
- return kStatus_Success;
- }
复制代码 |