牛卧堂MCU技术交流

标题: NUC240的PWM capture 最小输入频率 [打印本页]

作者: assman    时间: 2022-5-18 09:26
标题: NUC240的PWM capture 最小输入频率
本帖最后由 assman 于 2022-5-20 08:56 编辑

各位大大安安,我在学习NUC240 BSP中Smpl_Basic01_PWM_Capture范例,输入的PWM频率范围在0~100Hz区间,量测周期结果不是输入的PWM频率,是否该设定PWM_ConfigCaptureChannel中的参数才能正确量测?
  1. /**
  2. * @brief Configure PWM capture and get the nearest unit time.
  3. * @param[in] pwm The pointer of the specified PWM module
  4. * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~3
  5. * @param[in] u32UnitTimeNsec The unit time of counter
  6. * @param[in] u32CaptureEdge The condition to latch the counter. This parameter is not used
  7. * @return The nearest unit time in nano second.
  8. * @details This function is used to configure PWM capture and get the nearest unit time.
  9. */
  10. uint32_t PWM_ConfigCaptureChannel(PWM_T *pwm,
  11.                                   uint32_t u32ChannelNum,
  12.                                   uint32_t u32UnitTimeNsec,
  13.                                   uint32_t u32CaptureEdge)
  14. {
  15.     uint32_t u32Src;
  16.     uint32_t u32PWMClockSrc;
  17.     uint32_t u32PWMClkTbl[8] = {__HXT, __LXT, NULL, __HIRC, NULL, NULL, NULL, __LIRC};
  18.     uint32_t u32NearestUnitTimeNsec;
  19.     uint8_t  u8Divider = 1;
  20.     /* this table is mapping divider value to register configuration */
  21.     uint32_t u32PWMDividerToRegTbl[17] = {NULL, 4, 0, NULL, 1, NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3};
  22.     uint16_t u16Prescale = 2;
  23.     uint16_t u16CNR = 0xFFFF;

  24.     if(pwm == PWMA)
  25.     {
  26.         if(u32ChannelNum < 2)/* channel 0 and channel 1 */
  27.             u32Src = ((CLK->CLKSEL2 & (CLK_CLKSEL2_PWM01_S_EXT_Msk)) >> (CLK_CLKSEL2_PWM01_S_EXT_Pos - 2)) | (CLK->CLKSEL1 & (CLK_CLKSEL1_PWM01_S_Msk)) >> (CLK_CLKSEL1_PWM01_S_Pos);
  28.         else /* channel 2 and channel 3 */
  29.             u32Src = ((CLK->CLKSEL2 & (CLK_CLKSEL2_PWM23_S_EXT_Msk)) >> (CLK_CLKSEL2_PWM23_S_EXT_Pos - 2)) | (CLK->CLKSEL1 & (CLK_CLKSEL1_PWM23_S_Msk)) >> (CLK_CLKSEL1_PWM23_S_Pos);
  30.     }
  31.     else /*pwm == PWMB*/
  32.     {
  33.         if(u32ChannelNum < 2)/* channel 0 and channel 1 */
  34.             u32Src = ((CLK->CLKSEL2 & (CLK_CLKSEL2_PWM45_S_EXT_Msk)) >> (CLK_CLKSEL2_PWM45_S_EXT_Pos - 2)) | (CLK->CLKSEL2 & (CLK_CLKSEL2_PWM45_S_Msk)) >> (CLK_CLKSEL2_PWM45_S_Pos);
  35.         else /* channel 2 and channel 3 */
  36.             u32Src = ((CLK->CLKSEL2 & (CLK_CLKSEL2_PWM67_S_EXT_Msk)) >> (CLK_CLKSEL2_PWM67_S_EXT_Pos - 2)) | (CLK->CLKSEL2 & (CLK_CLKSEL2_PWM67_S_Msk)) >> (CLK_CLKSEL2_PWM67_S_Pos);
  37.     }

  38.     if(u32Src == 2)
  39.     {
  40.         SystemCoreClockUpdate();
  41.         u32PWMClockSrc = SystemCoreClock;
  42.     }
  43.     else
  44.     {
  45.         u32PWMClockSrc = u32PWMClkTbl[u32Src];
  46.     }

  47.     u32PWMClockSrc /= 1000;
  48.     for(; u16Prescale <= 0x100; u16Prescale++)
  49.     {
  50.         u32NearestUnitTimeNsec = (1000000 * u16Prescale * u8Divider) / u32PWMClockSrc;
  51.         if(u32NearestUnitTimeNsec < u32UnitTimeNsec)
  52.         {
  53.             if((u16Prescale == 0x100) && (u8Divider == 16))  //limit to the maximum unit time(nano second)
  54.                 break;
  55.             if(u16Prescale == 0x100)
  56.             {
  57.                 u16Prescale = 2;
  58.                 u8Divider <<= 1; // clk divider could only be 1, 2, 4, 8, 16
  59.                 continue;
  60.             }
  61.             if(!((1000000  * ((u16Prescale * u8Divider) + 1)) > (u32NearestUnitTimeNsec * u32PWMClockSrc)))
  62.                 break;
  63.             continue;
  64.         }
  65.         break;
  66.     }

  67.     // Store return value here 'cos we're gonna change u8Divider & u16Prescale & u16CNR to the real value to fill into register
  68.     u16Prescale -= 1;

  69.     // convert to real register value
  70.     u8Divider = u32PWMDividerToRegTbl[u8Divider];

  71.     // every two channels share a prescaler
  72.     (pwm)->PPR = ((pwm)->PPR & ~(PWM_PPR_CP01_Msk << ((u32ChannelNum >> 1) * 8))) | (u16Prescale << ((u32ChannelNum >> 1) * 8));
  73.     (pwm)->CSR = ((pwm)->CSR & ~(PWM_CSR_CSR0_Msk << (4 * u32ChannelNum))) | (u8Divider << (4 * u32ChannelNum));
  74.     // set PWM to edge aligned type
  75.     (pwm)->PCR &= ~(PWM_PCR_PWM01TYPE_Msk << (u32ChannelNum >> 1));
  76.     (pwm)->PCR |= PWM_PCR_CH0MOD_Msk << (8 * u32ChannelNum);
  77.     *((__IO uint32_t *)((((uint32_t) & ((pwm)->CNR0)) + (u32ChannelNum) * 12))) = u16CNR;

  78.     return (u32NearestUnitTimeNsec);
  79. }
复制代码

实际输入PWM频率100Hz,量测得到Total Period = 60399,利用下面公式计算频率=12000000/2/60399=99.36Hz
假设输入PWM频率为50Hz,(CNR+1)=120000000/2/50=120000 大于65536,所以必须增大prescaler值,请问该如何调整prescaler值?

擷取.PNG (27.37 KB, 下载次数: 415)

擷取.PNG

作者: assman    时间: 2022-5-19 09:52
另外还想问CalPeriodTime这函数,一开始是先清除下降沿指示,然后等下降沿信号,再清除下降沿指示,这里为何要连清二次下降沿指示,
不是已经下降完成了,接着肯定要上升沿了,也就是上升信号?

另外在循环里,一开始等下降沿信号,然后清除上升/下降沿指示,这里为何不是只清除下降沿指示即可,而是二者皆清除?

  1. void CalPeriodTime(PWM_T *PWM, uint32_t u32Ch)
  2. {
  3.     uint16_t u32Count[4];
  4.     uint32_t u32i;
  5.     uint16_t u16RisingTime, u16FallingTime, u16HighPeroid, u16LowPeroid, u16TotalPeroid;

  6.     /* Clear Capture Falling Indicator (Time A) */
  7.     PWM_ClearCaptureIntFlag(PWM, u32Ch, PWM_CAPTURE_INT_FALLING_LATCH);

  8.     /* Wait for Capture Falling Indicator  */
  9.     while(PWM_GetCaptureIntFlag(PWM, u32Ch) < 2);

  10.     /* Clear Capture Falling Indicator (Time B)*/
  11.     PWM_ClearCaptureIntFlag(PWM, u32Ch, PWM_CAPTURE_INT_FALLING_LATCH);

  12.     u32i = 0;

  13.     while(u32i < 4)
  14.     {
  15.         /* Wait for Capture Falling Indicator */
  16.         while(PWM_GetCaptureIntFlag(PWM, u32Ch) < 2);

  17.         /* Clear Capture Falling and Rising Indicator */
  18.         PWM_ClearCaptureIntFlag(PWM, u32Ch, PWM_CAPTURE_INT_FALLING_LATCH | PWM_CAPTURE_INT_RISING_LATCH);

  19.         /* Get Capture Falling Latch Counter Data */
  20.         u32Count[u32i++] = PWM_GET_CAPTURE_FALLING_DATA(PWM, u32Ch);

  21.         /* Wait for Capture Rising Indicator */
  22.         while(PWM_GetCaptureIntFlag(PWM, u32Ch) < 2);

  23.         /* Clear Capture Rising Indicator */
  24.         PWM_ClearCaptureIntFlag(PWM, u32Ch, PWM_CAPTURE_INT_RISING_LATCH);

  25.         /* Get Capture Rising Latch Counter Data */
  26.         u32Count[u32i++] = PWM_GET_CAPTURE_RISING_DATA(PWM, u32Ch);
  27.     }

  28.     u16RisingTime = u32Count[1];

  29.     u16FallingTime = u32Count[0];

  30.     u16HighPeroid = u32Count[1] - u32Count[2];

  31.     u16LowPeroid = 0x10000 - u32Count[1];

  32.     u16TotalPeroid = 0x10000 - u32Count[2];

  33.     printf("\nCapture Result: Rising Time = %d, Falling Time = %d \nHigh Period = %d, Low Period = %d, Total Period = %d.\n\n",
  34.            u16RisingTime, u16FallingTime, u16HighPeroid, u16LowPeroid, u16TotalPeroid);
  35. }
复制代码



作者: admin    时间: 2022-5-19 16:35
1、设置捕获的时钟预分频值请修改下面函数的第二个参数,
/* If input minimum frequency is 250Hz, user can calculate capture settings by follows.
           Capture clock source frequency = __HXT = 12000000 in the sample code.
           (CNR+1) = Capture clock source frequency/prescaler/clock source divider/minimum input frequency
                   = 12000000/2/1/250 = 24000
           (Note: CNR is 16 bits, so if calculated value is larger than 65536, user should increase prescale value.)
           CNR = 0xFFFF
           (Note: In capture mode, user should set CNR to 0xFFFF to increase capture frequency range.)
        */
Capture unit time = 1/(Capture clock source frequency/prescaler)
           166ns = 1/(12000000/2)
PWM_ConfigCaptureChannel(PWMB, PWM_CH2, 166, 0);
2、为何CalPeriodTime函数里连清两次下降沿中断?
程序设置的是下降沿捕获时会清除计数器重新计数。
两次清除下降沿是为了定位一个测量的起点。比如刚进入此函数时有可能刚发生下降沿中断,也可能发生完下降沿+上升沿,所以这个中断忽略对应A段;
等到下一个下降沿发生时,把它当作测量的起点,所以B点的中断标志也清除掉,开始正事测试。
作者: admin    时间: 2022-5-19 16:36
3、循环里发生下降沿中断后,为何同时清除上下沿中断标志?
因为例程是选择的先记录下降沿,从B处的下降沿到0位置时,中间必定会先发生上升沿,所以此时应该要将上下沿标志都清除。
作者: admin    时间: 2022-5-19 16:36


作者: assman    时间: 2022-5-23 15:28
admin 发表于 2022-5-19 16:36

感谢版大讲说,另外想问这四对PWM可以将每一对分别设定输出模式和补获模式使用嘛,比方说PWM0是补获模式,PWM1是输出模式,这样用好像补获模式数值会怪怪的,谢谢!
作者: admin    时间: 2022-5-23 16:24
assman 发表于 2022-5-23 07:28
感谢版大讲说,另外想问这四对PWM可以将每一对分别设定输出模式和补获模式使用嘛,比方说PWM0是补获模式 ...

可以每一组分开设置是作为PWM输出还是捕获输入
作者: assman    时间: 2022-5-23 22:39
admin 发表于 2022-5-23 16:24
可以每一组分开设置是作为PWM输出还是捕获输入

版大抱歉我的意思是同一组中输出模式和补获模式同时使用,这样补获模式数值似乎会怪怪的,谢谢!




欢迎光临 牛卧堂MCU技术交流 (http://www.nuvoton-mcu.com/) Powered by Discuz! X3.2