查看: 4419|回复: 1

[原创] FlexIO PWM 输出频率变化不规律

[复制链接]

该用户从未签到

715

主题

6374

帖子

0

超级版主

Rank: 8Rank: 8

积分
25213
最后登录
2025-8-18
发表于 2021-4-28 13:46:04 | 显示全部楼层 |阅读模式
问题简介:
客户使用RT1020开发项目,需要RT1020按此顺序(300kHz,333kHz,375kHz,400kHz)生成频率变化的PWM,为了实现此目标,客户选择使用FlexIO模块,并且利用PIT生成10Hz的中断来更改输出的PWM频率,所以客户使用evkmimxrt1020_flexio_pwm工程进行验证,却在示波器观察到PWM的频率是在两个频率(333kHz和400kHz或300kHz和375kHz)之间切换,而并非预想中的四个。
当PIT的频率降低到1Hz时,虽然会观察到四个频率会依此出现,但偶尔也会跳过333 kHz。
在测试过程中,示波器上显示的频率改变的时间始终遵循着PIT周期。
具体代码如下:
  1. const int Periods[4] = {300000, 333333, 375000, 400000};
  2. const int DutyCycle[4] = {50, 50, 50, 50};
  3. int pwmDitheringIdx = 0;

  4. void PWMModule_InitPWM()
  5. {
  6.   // Init mux pin
  7.   IOMUXC_SetPinMux(IOMUXC_PIN_PAD, 0U);
  8.   IOMUXC_SetPinConfig(IOMUXC_PIN_PAD, 0x10B0u);
  9.   
  10.   // Init flexio
  11.   CLOCK_SetMux(kCLOCK_Flexio1Mux, 3); // Select USB1 PLL CLK
  12.   CLOCK_SetDiv(kCLOCK_Flexio1PreDiv, 1);
  13.   CLOCK_SetDiv(kCLOCK_Flexio1Div, 1);
  14.   
  15.   flexio_config_t fxioUserConfig;
  16.   FLEXIO_GetDefaultConfig(&fxioUserConfig);
  17.   fxioUserConfig.enableFastAccess = true;
  18.   FLEXIO_Init(PWM_FLEXIO, &fxioUserConfig);
  19.   
  20.   flexio_timer_config_t fxioTimerConfig;
  21.   fxioTimerConfig.triggerSelect   = FLEXIO_TIMER_TRIGGER_SEL_SHIFTnSTAT(0U);
  22.   fxioTimerConfig.triggerSource   = kFLEXIO_TimerTriggerSourceInternal;
  23.   fxioTimerConfig.triggerPolarity = kFLEXIO_TimerTriggerPolarityActiveLow;
  24.   fxioTimerConfig.pinConfig       = kFLEXIO_PinConfigOutput;
  25.   fxioTimerConfig.pinPolarity     = kFLEXIO_PinActiveHigh;
  26.   fxioTimerConfig.pinSelect       = PWM_PIN; /* Set pwm output */
  27.   fxioTimerConfig.timerMode       = kFLEXIO_TimerModeDisabled;
  28.   fxioTimerConfig.timerOutput     = kFLEXIO_TimerOutputOneNotAffectedByReset;
  29.   fxioTimerConfig.timerDecrement  = kFLEXIO_TimerDecSrcOnFlexIOClockShiftTimerOutput;
  30.   fxioTimerConfig.timerDisable    = kFLEXIO_TimerDisableNever;
  31.   fxioTimerConfig.timerEnable     = kFLEXIO_TimerEnabledAlways;
  32.   fxioTimerConfig.timerReset      = kFLEXIO_TimerResetNever;
  33.   fxioTimerConfig.timerStart      = kFLEXIO_TimerStartBitDisabled;
  34.   fxioTimerConfig.timerStop       = kFLEXIO_TimerStopBitDisabled;
  35.   fxioTimerConfig.timerCompare = computeTimerCompare(frequency, dutyCycle);
  36.   FLEXIO_SetTimerConfig(PWM_FLEXIO, 0, &fxioTimerConfig);

  37.   // Init Timer
  38.   PIT_SetTimerPeriod(PIT, kPIT_Chnl_1, (24000000 / DITHERING_FREQUENCY) - 1);
  39.   PIT_EnableInterrupts(PIT, kPIT_Chnl_1, kPIT_TimerInterruptEnable);
  40.   EnableIRQ(PIT_IRQn);
  41. }

  42. void PIT_IRQHandler(void)
  43. {
  44.   // Stop pwm
  45.   PWM_FLEXIO->TIMCTL[0] &= 0xFFFFFFFC;
  46.   // Update the frequency of the flexio.
  47.   PWM_FLEXIO->TIMCMP[0] = FLEXIO_TIMCMP_CMP(computeTimerCompare(Periods[pwmDitheringIdx], DutyCycle[pwmDitheringIdx]));
  48.   
  49.   // Start pwm
  50.   PWM_FLEXIO->TIMCTL[0] |= FLEXIO_TIMCTL_TIMOD(kFLEXIO_TimerModeDual8BitPWM);
  51.   
  52.   pwmDitheringIdx = (pwmDitheringIdx + 1) % NUMBER_OF_FREQUENCY;
  53.   
  54.   // Clear IRQ flags
  55.   PIT_ClearStatusFlags(PIT, kPIT_Chnl_1, kPIT_TimerFlag);
  56. }

  57. int computeTimerCompare(unsigned int frequency, unsigned int dutyCycle)
  58. {
  59.   //The clock of the FLEXIO Timer[0] is the PLL3 (USB1) clock divided by the predivisor+1 (4+1) and the divisor (7+1)
  60.   //The number of timer count (sum) required to generate the desired period is Timer[0]_Clock/[Desired Frequency]
  61.   unsigned int sum = ((CLOCK_GetFreq(kCLOCK_Usb1PllClk) / (1 + 1U) / (1 + 1U)) * 2 / frequency + 1) / 2;        //...*2+1)/2 -> Round to nearest
  62.   
  63.   //In 8-bit PWM high mode, the lower 8-bits configure the high period of the output to (CMP[7:0] + 1) and
  64.   //the upper 8-bits configure the low period of the output to (CMP[15:8] + 1).
  65.   //low/high period is the number of timer count where to output is low/high.
  66.   //PWM output is hardware inverted...
  67.   unsigned int lowerValue = (sum * dutyCycle / 50 + 1) / 2;
  68.   unsigned int upperValue = sum - lowerValue;
  69.   return ((upperValue -1) <<  | (lowerValue - 1);
  70. }
复制代码


问题解决:
分析上述描述,可得PIT中断周期的长短可能是主要原因,而为了更好的观测波形变化规律,小编使用逻辑分析仪而不是示波器来观察PWM波,确实复现了客户所说的现象。
在经过多次测试和代码浏览后发现,此现象是跟PIT中断函数未及时清零中断符号位有关,CPU时钟频率相比于IP总线时钟要大得多,CPU可能会在清除中断标志之前就已运行完中断处理程序了,从而导致CPU一次又一次进入处理程序。
所以需要在进入PIT中断函数后,跟着调用PIT_ClearStatusFlags(PIT, kPIT_Chnl_1, kPIT_TimerFlag);来清零中断符号位或者在上述函数调用后,执行_DSB()以保证在退出中断函数之前,中断符号位已清零,具体实现代码可参考如下。

  1. /*
  2. * Copyright (c) 2013 - 2015, Freescale Semiconductor, Inc.
  3. * Copyright 2016-2017 NXP
  4. * All rights reserved.
  5. *
  6. * SPDX-License-Identifier: BSD-3-Clause
  7. */

  8. #include "fsl_device_registers.h"
  9. #include "fsl_debug_console.h"
  10. #include "fsl_flexio.h"
  11. #include "board.h"
  12. #include "fsl_pit.h"

  13. #include "pin_mux.h"
  14. #include "clock_config.h"
  15. /*******************************************************************************
  16. * Definitions
  17. ******************************************************************************/
  18. #define DEMO_TIME_DELAY_FOR_DUTY_CYCLE_UPDATE (2000000U)
  19. #define DEMO_FLEXIO_BASEADDR FLEXIO1
  20. #define DEMO_FLEXIO_OUTPUTPIN (5U) /* Select FLEXIO1_FLEXIO05 as PWM output */
  21. #define DEMO_FLEXIO_TIMER_CH (0U)  /* Flexio timer0 used */

  22. /* Select USB1 PLL (480 MHz) as flexio clock source */
  23. #define FLEXIO_CLOCK_SELECT (3U)
  24. /* Clock pre divider for flexio clock source */
  25. #define FLEXIO_CLOCK_PRE_DIVIDER (4U)
  26. /* Clock divider for flexio clock source */
  27. #define FLEXIO_CLOCK_DIVIDER (7U)
  28. #define DEMO_FLEXIO_CLOCK_FREQUENCY \
  29.     (CLOCK_GetFreq(kCLOCK_Usb1PllClk) / (FLEXIO_CLOCK_PRE_DIVIDER + 1U) / (FLEXIO_CLOCK_DIVIDER + 1U))
  30. /* FLEXIO output PWM frequency */
  31. #define DEMO_FLEXIO_FREQUENCY (48000U)
  32. #define FLEXIO_MAX_FREQUENCY (DEMO_FLEXIO_CLOCK_FREQUENCY / 2U)
  33. #define FLEXIO_MIN_FREQUENCY (DEMO_FLEXIO_CLOCK_FREQUENCY / 256U)

  34. /* Get source clock for PIT driver */
  35. #define PIT_SOURCE_CLOCK CLOCK_GetFreq(kCLOCK_OscClk)
  36. /*******************************************************************************
  37. * Prototypes
  38. ******************************************************************************/
  39. /*!
  40. * @brief Configures the timer as a 8-bits PWM mode to generate the PWM waveform
  41. *
  42. * @param freq_Hz PWM frequency in hertz, range is [FLEXIO_MIN_FREQUENCY, FLEXIO_MAX_FREQUENCY]
  43. * @param duty Specified duty in unit of %, with a range of [1, 99]
  44. */
  45. static void flexio_pwm_init(uint32_t freq_Hz, uint32_t duty);

  46. /*!
  47. * @brief Enables the timer by setting TIMOD to 8-bits PWM and start generating the PWM
  48. */
  49. static void flexio_pwm_start(void);

  50. /*******************************************************************************
  51. * Variables
  52. *******************************************************************************/
  53. unsigned int idx = 0;
  54. uint32_t freq[4] = {50000,48000,300000,400000};
  55. /*******************************************************************************
  56. * Code
  57. ******************************************************************************/
  58. static void flexio_pwm_init(uint32_t freq_Hz, uint32_t duty)
  59. {
  60.     assert((freq_Hz < FLEXIO_MAX_FREQUENCY) && (freq_Hz > FLEXIO_MIN_FREQUENCY));

  61.     uint32_t lowerValue = 0; /* Number of clock cycles in high logic state in one period */
  62.     uint32_t upperValue = 0; /* Number of clock cycles in low logic state in one period */
  63.     uint32_t sum        = 0; /* Number of clock cycles in one period */
  64.     flexio_timer_config_t fxioTimerConfig;

  65.     /* Check parameter */
  66.     if ((duty > 99) || (duty == 0))
  67.     {
  68.         duty = 50;
  69.     }

  70.     /* Configure the timer DEMO_FLEXIO_TIMER_CH for generating PWM */
  71.     fxioTimerConfig.triggerSelect   = FLEXIO_TIMER_TRIGGER_SEL_SHIFTnSTAT(0U);
  72.     fxioTimerConfig.triggerSource   = kFLEXIO_TimerTriggerSourceInternal;
  73.     fxioTimerConfig.triggerPolarity = kFLEXIO_TimerTriggerPolarityActiveLow;
  74.     fxioTimerConfig.pinConfig       = kFLEXIO_PinConfigOutput;
  75.     fxioTimerConfig.pinPolarity     = kFLEXIO_PinActiveHigh;
  76.     fxioTimerConfig.pinSelect       = DEMO_FLEXIO_OUTPUTPIN; /* Set pwm output */
  77.     fxioTimerConfig.timerMode       = kFLEXIO_TimerModeDisabled;
  78.     fxioTimerConfig.timerOutput     = kFLEXIO_TimerOutputOneNotAffectedByReset;
  79.     fxioTimerConfig.timerDecrement  = kFLEXIO_TimerDecSrcOnFlexIOClockShiftTimerOutput;
  80.     fxioTimerConfig.timerDisable    = kFLEXIO_TimerDisableNever;
  81.     fxioTimerConfig.timerEnable     = kFLEXIO_TimerEnabledAlways;
  82.     fxioTimerConfig.timerReset      = kFLEXIO_TimerResetNever;
  83.     fxioTimerConfig.timerStart      = kFLEXIO_TimerStartBitDisabled;
  84.     fxioTimerConfig.timerStop       = kFLEXIO_TimerStopBitDisabled;

  85.     /* Calculate timer lower and upper values of TIMCMP */
  86.     /* Calculate the nearest integer value for sum, using formula round(x) = (2 * floor(x) + 1) / 2 */
  87.     /* sum = DEMO_FLEXIO_CLOCK_FREQUENCY / freq_H */

  88.     sum = (DEMO_FLEXIO_CLOCK_FREQUENCY * 2 / freq_Hz + 1) / 2;
  89.     PRINTF("\r\n freq_Hz is %d,sum is %d\r\n",freq_Hz,sum);
  90.     /* Calculate the nearest integer value for lowerValue, the high period of the pwm output */
  91.     /* lowerValue = sum * duty / 100 */
  92.     lowerValue = (sum * duty / 50 + 1) / 2;
  93.     /* Calculate upper value, the low period of the pwm output */
  94.     upperValue                   = sum - lowerValue;
  95.     fxioTimerConfig.timerCompare = ((upperValue - 1) << 8U) | (lowerValue - 1);
  96.     PRINTF("\r\n fxioTimerConfig.timerCompare is %d\r\n",fxioTimerConfig.timerCompare);

  97.     FLEXIO_SetTimerConfig(DEMO_FLEXIO_BASEADDR, DEMO_FLEXIO_TIMER_CH, &fxioTimerConfig);

  98. }

  99. static void flexio_pwm_start(void)
  100. {
  101.     /* Set Timer mode to kFLEXIO_TimerModeDual8BitPWM to start timer */
  102.     DEMO_FLEXIO_BASEADDR->TIMCTL[DEMO_FLEXIO_TIMER_CH] |= FLEXIO_TIMCTL_TIMOD(kFLEXIO_TimerModeDual8BitPWM);
  103. }

  104. /*!
  105. * @brief Main function
  106. */
  107. int main(void)
  108. {
  109.     uint32_t i;
  110.     uint32_t duty = 100;
  111.     flexio_config_t fxioUserConfig;

  112.     /* Init board hardware */
  113.     BOARD_ConfigMPU();
  114.     BOARD_InitPins();
  115.     BOARD_BootClockRUN();
  116.     BOARD_InitDebugConsole();

  117.     /* Clock setting for Flexio */
  118.     CLOCK_SetMux(kCLOCK_Flexio1Mux, FLEXIO_CLOCK_SELECT);
  119.     CLOCK_SetDiv(kCLOCK_Flexio1PreDiv, FLEXIO_CLOCK_PRE_DIVIDER);
  120.     CLOCK_SetDiv(kCLOCK_Flexio1Div, FLEXIO_CLOCK_DIVIDER);

  121.     /* Init flexio, use default configure
  122.      * Disable doze and fast access mode
  123.      * Enable in debug mode
  124.      */
  125.     FLEXIO_GetDefaultConfig(&fxioUserConfig);
  126.     FLEXIO_Init(DEMO_FLEXIO_BASEADDR, &fxioUserConfig);

  127.     // Init Timer
  128.     CLOCK_SetMux(kCLOCK_PerclkMux, 1U); // Take the osc clock source 24MHz
  129.     CLOCK_SetDiv(kCLOCK_PerclkDiv, 0U); // divide the clock by 1
  130.     pit_config_t pitConfig;
  131.     PIT_GetDefaultConfig(&pitConfig);
  132.     PIT_Init(PIT, &pitConfig);
  133.     PIT_SetTimerPeriod(PIT, kPIT_Chnl_1, USEC_TO_COUNT(1000000U, PIT_SOURCE_CLOCK));
  134.     PIT_EnableInterrupts(PIT, kPIT_Chnl_1, kPIT_TimerInterruptEnable);
  135.     EnableIRQ(PIT_IRQn);
  136.     PIT_StartTimer(PIT, kPIT_Chnl_1);

  137.     PRINTF("\r\nFLEXIO_PWM demo start.\r\n");
  138.     flexio_pwm_init(freq[idx], 50);
  139.     flexio_pwm_start();
  140.     while (1)
  141.     {
  142. //        flexio_pwm_init(DEMO_FLEXIO_FREQUENCY, --duty);
  143. //        flexio_pwm_start();

  144. //        for (i = 0; i < DEMO_TIME_DELAY_FOR_DUTY_CYCLE_UPDATE; i++)
  145. //        {
  146. //            __NOP();
  147. //        }

  148. //        if (duty == 0)
  149. //        {
  150. //            duty = 100;
  151. //        }
  152.     }
  153. }




  154. void PIT_IRQHandler(void)
  155. {
  156.   PIT_ClearStatusFlags(PIT, kPIT_Chnl_1, kPIT_TimerFlag);
  157.   idx = (idx + 1) % 4;
  158.   PRINTF("\r\n idx is %d\r\n",idx);
  159.   flexio_pwm_init(freq[idx], 50);
  160.   flexio_pwm_start();

  161.   /* Added for, and affects, all PIT handlers. For CPU clock which is much larger than the IP bus clock,
  162.    * CPU can run out of the interrupt handler before the interrupt flag being cleared, resulting in the
  163.    * CPU's entering the handler again and again. Adding DSB can prevent the issue from happening.
  164.    */
  165.   //__DSB();
  166. }
复制代码


回复

使用道具 举报

  • TA的每日心情
    开心
    2023-7-27 16:05
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    17

    主题

    81

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    1311
    最后登录
    2025-6-6
    发表于 2021-11-2 10:20:56 | 显示全部楼层
    请问下这个案例的PWM输出频率能降低到1K以下吗,我测试了好像不行
    哎...今天够累的,签到来了~
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-8-20 03:44 , Processed in 0.073294 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表