查看: 419|回复: 1

[评测分享] 【Avnet | NXP FRDM-MCXN947试用活动】定时器的PWM输出

[复制链接]
  • TA的每日心情
    开心
    2023-7-5 11:08
  • 签到天数: 120 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2024-11-17 22:08:05 | 显示全部楼层 |阅读模式
    分享到:
           本设备配备有两个PWM模块实例——PWM0与PWM1,遗憾的是,这两个实例均不支持NanoEdge放置功能。不过,通过精密的抖动处理技术,我们依然能够实现细腻的边缘控制效果。值得注意的是,本设备仅采用了PWMn_EXTA输入端口,而PWMn_EXTB输入端口则未被使用。PWM模块内部嵌有多个PWM子模块,每个子模块均可独立操控一个半桥功率级,为用户提供强大的故障通道支持。该模块能够生成多样化的开关模式,即便是高度复杂的波形也不在话下,因此,它无疑是控制各类开关电源(SMPS)拓扑结构的理想之选。
    1.png

    PWM的特点如下:
           •用于中心、边缘对齐和不对称PWM的16位分辨率
           •当精细边缘放置不可用时,抖动以模拟增强的分辨率
           •可以作为互补对或独立通道运行的PWM输出
           •能够接受PWM生成的带符号数字
           •独立控制每个PWM输出的两个边沿
           •持与外部硬件或其他PWM同步
           •双缓冲PWM寄存器
                  —从1到16的整体重新加载速率
                  —半周期重新加载能力
           •每个PWM周期可以通过硬件生成多个输出触发事件
           •支持双开关PWM输出
           •可以将故障输入分配给控制多个PWM输出
           •用于故障输入的可编程滤波器
           •独立可编程PWM输出极性
           •独立的顶部和底部死区插入
           •每个互补对可以使用其自己的PWM频率和死区时间值进行操作
           •每个PWM输出的单独软件控制
           •所有输出都可以编程为通过FORCE_OUT事件同时改变
           •PWM_X引脚可以选择性地从每个子模块输出第三PWM信号
           •未用于PWM生成的通道可用于缓冲输出比较功能和输入捕获功能
           •增强的双边缘捕获功能

    我们实验测试一下:
           本开发板载的是MCX N947,封装为184VFBGA,采用M33内核,主频可以达到150MHz,
    配置工具添加外设:
    2.png

    时钟配置:
           打开例程对应的时钟配置如下:
    3.png

           主频为150MHz,PRO_HF为48M,通过测试可以得知定时器所用的高频时钟为48M。
    引脚配置:
    4.png

           例程中使用的是P0_10作为PWM输出引脚,该引脚同时是LED的控制引脚。

    一、设置PWM功能流程(在例程中添加一路PWM输出)
           1、配置系统时钟
    1. /* attach FRO 12M to FLEXCOMM4 (debug console) */
    2.     CLOCK_SetClkDiv(kCLOCK_DivFlexcom4Clk, 1u);
    3.     CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);

    4.     /* Use FRO HF clock for some of the Ctimers */
    5.     CLOCK_SetClkDiv(kCLOCK_DivCtimer0Clk, 1u);
    6.     CLOCK_AttachClk(kFRO_HF_to_CTIMER0);
    复制代码
    2、配置引脚
           打开配置工具的查看引脚功能,P0_10配置为CTIMER0的PWM通道1;
    5.png

           来到代码中可以看到如下初始化:
    1. /* Enables the clock for PORT0 controller: Enables clock */
    2.     CLOCK_EnableClock(kCLOCK_Port0);
    3.    
    4.     const port_pin_config_t LED_RED = {/* Internal pull-up/down resistor is disabled */
    5.                                        kPORT_PullDisable,
    6.                                        /* Low internal pull resistor value is selected. */
    7.                                        kPORT_LowPullResistor,
    8.                                        /* Fast slew rate is configured */
    9.                                        kPORT_FastSlewRate,
    10.                                        /* Passive input filter is disabled */
    11.                                        kPORT_PassiveFilterDisable,
    12.                                        /* Open drain output is disabled */
    13.                                        kPORT_OpenDrainDisable,
    14.                                        /* Low drive strength is configured */
    15.                                        kPORT_LowDriveStrength,
    16.                                        /* Pin is configured as CT0_MAT0 */
    17.                                        kPORT_MuxAlt4,
    18.                                        /* Digital input enabled */
    19.                                        kPORT_InputBufferEnable,
    20.                                        /* Digital input is not inverted */
    21.                                        kPORT_InputNormal,
    22.                                        /* Pin Control Register fields [15:0] are not locked */
    23.                                        kPORT_UnlockRegister};
    24.     /* PORT0_10 (pin B12) is configured as CT0_MAT0 */
    25.     PORT_SetPinConfig(BOARD_INITPINS_LED_RED_PORT, BOARD_INITPINS_LED_RED_PIN, &LED_RED);

    26.     /* PORT0_11 (pin B11) is configured as CT0_MAT1 */
    27.     PORT_SetPinMux(PORT0, 11U, kPORT_MuxAlt4);

    28.     PORT0->PCR[11] = ((PORT0->PCR[11] &
    29.                        /* Mask bits to zero which are setting */
    30.                        (~(PORT_PCR_IBE_MASK)))

    31.                       /* Input Buffer Enable: Enables. */
    32.                       | PORT_PCR_IBE(PCR_IBE_ibe1));
    复制代码
    3、配置定时器及通道
           板级的初始化已经完成,我们需要通过将我们添加的引脚与对应的定时器添加上。
    1. /* Get the PWM period match value and pulse width match value of 20Khz PWM signal with 20% dutycycle */
    2.     CTIMER_GetPwmPeriodValue(20000, 20, timerClock);
    3.     CTIMER_SetupPwmPeriod(CTIMER, kCTIMER_Match_3, kCTIMER_Match_0, g_pwmPeriod, g_pulsePeriod, false);
    4.     CTIMER_GetPwmPeriodValue(20000, 50, timerClock);
    5.     CTIMER_SetupPwmPeriod(CTIMER, kCTIMER_Match_2, kCTIMER_Match_1, g_pwmPeriod, g_pulsePeriod , false);
    6.     CTIMER_StartTimer(CTIMER);
    复制代码
          测试结果正常。
           以上为NXP提供的例程中的Ctimer的PWM输出的例子,咱们将在一个空白的工程从0开始创建PWM输出。
    二、从零配置Ctimer的PWM输出
    1、添加外设
    6.png

           点击“Peripheral drivers”旁边的“+”,选择“Ctimer”,最后确定创建一个外设;
    7.png

           想要得到PWM,分别需要配置定时器和通道信息;
    8.png

           我们看到了一个警告信息,定时器通道没有关联对应的引脚,这种情况我们可以通右键这个警告信息,可以快速进行问题处理:
    9.png

           选择想要关联的引脚就可以了,非常方便。
           没有错误以及警告后,更新源代码就可以了,到这里我们就创建了新的外设,以及相关的引脚配置。
    2、在主程序中填写如下代码:
    1. srcClock_Hz = CLOCK_GetCTimerClkFreq(0U);
    2.     PRINTF("srcClock_Hz: %d\n",srcClock_Hz);
    3.     timerClock = 2000000;
    4.     /* Get the PWM period match value and pulse width match value of 20Khz PWM signal with 20% dutycycle */
    5.     CTIMER_GetPwmPeriodValue(20000, 20, timerClock);
    6.     CTIMER_SetupPwmPeriod(CTIMER0, kCTIMER_Match_3, kCTIMER_Match_0, g_pwmPeriod, g_pulsePeriod, false);
    7.     CTIMER_StartTimer(CTIMER0);
    复制代码
          单路PWM输出效果如下:
    10.png












           在本次的CTimer产生PWM的测试中,主要关注的数据是定时器中的prescaler参数(实际),PWM的pwmPeriod和pulsePeriod,其中定时器中的prescaler要设定的远大于pwmPeriod,理论上越大精度越高。三者关系如下:
    1. timerClock_Hz = srcClock_Hz/(prescaler + 1U);
    2. /* Calculate PWM period match value */
    3. pwmPeriod = (timerClock_Hz / pwmFreqHz) - 1U;

    4. /* Calculate pulse width match value */
    5. pulsePeriod = (pwmPeriod + 1U) * (100 - dutyCyclePercent) / 100;
    复制代码
    接下来再加一路:
    11.png

           添加代码如下:
    1. CTIMER_GetPwmPeriodValue(20000, 50, timerClock);
    2. CTIMER_SetupPwmPeriod(CTIMER0, kCTIMER_Match_2, kCTIMER_Match_1, g_pwmPeriod, g_pulsePeriod, f
    复制代码
          注意两路的的目标频率要一致。
           效果如下:
    12.png





           这里可以看到Ctimer输出的双路是边沿对齐的,也无法设置。

    三、从零配置SCtimer的PWM输出
           创建组件:
    13.png

           定时器基础配置:
    14.png

           需要配置的内容不多,只要配置时钟就好;
           通道配置:
    15.png

           可以看到配置你想要的若干通道,在这里遇到了两个问题,一个是配置SCTIMER out 0时连接到P1_4引脚无输出,连接到P2_2则输出正常;另一个就是上图的感叹号问题,要求PWM信号必须被分配到一个初始化的状态,可是并没有相关设置项,我们带着问题进行“更新源代码”,可以看到初始化内容("peripherals.C")中:
    1. const sctimer_config_t SCT0_initConfig = {
    2.   .enableCounterUnify = true,
    3.   .clockMode = kSCTIMER_System_ClockMode,
    4.   .clockSelect = kSCTIMER_Clock_On_Rise_Input_0,
    5.   .enableBidirection_l = false,
    6.   .enableBidirection_h = false,
    7.   .prescale_l = 0U,
    8.   .prescale_h = 0U,
    9.   .outInitState = (uint8_t)(SCT0_OUTPUT_1 | SCT0_OUTPUT_3),
    10.   .inputsync = 0U
    11. };
    12. const sctimer_pwm_signal_param_t SCT0_pwmSignalsConfig[2] = {
    13.   {
    14.     .output = kSCTIMER_Out_0,
    15.     .level = kSCTIMER_HighTrue,
    16.     .dutyCyclePercent = 50U
    17.   },
    18.   {
    19.     .output = kSCTIMER_Out_1,
    20.     .level = kSCTIMER_LowTrue,
    21.     .dutyCyclePercent = 20U
    22.   }
    23. };
    24. uint32_t SCT0_pwmEvent[2];

    25. static void SCT0_init(void) {
    26.   SCTIMER_Init(SCT0_PERIPHERAL, &SCT0_initConfig);
    27.   /* Initialization of state 0 */
    28.   SCTIMER_SetupPwm(SCT0_PERIPHERAL, &SCT0_pwmSignalsConfig[0], kSCTIMER_EdgeAlignedPwm, 20000U, SCT0_CLOCK_FREQ, &SCT0_pwmEvent[0]);
    29.   SCTIMER_StartTimer(SCT0_PERIPHERAL, kSCTIMER_Counter_U);
    30. }
    复制代码
          发现相关配置文件已经创建,只是在初始化(SCT0_init(void))中未对有问题的通达进行初始化配置,我们可以通过在外面添加相应的SCTIMER_SetupPwm()重新配置对应的PWM通道。
           接下来我们在外部进行对PWM通道进行从新配置:
    1. sctimer_config_t sctimerInfo;
    2. sctimer_pwm_signal_param_t pwmParam;
    3. uint32_t sctimerClock;
    4. uint32_t event;

    5. sctimerClock = CLOCK_GetFreq(kCLOCK_BusClk);
    6. SCTIMER_GetDefaultConfig(&sctimerInfo);
    7. SCTIMER_Init(SCT0, &sctimerInfo);

    8.     /* Configure first PWM with frequency 24kHZ from first output */
    9.     pwmParam.output           = kSCTIMER_Out_0;
    10.     pwmParam.level            = kSCTIMER_HighTrue;
    11.     pwmParam.dutyCyclePercent = 50;
    12.     if (SCTIMER_SetupPwm(SCT0, &pwmParam, kSCTIMER_CenterAlignedPwm, 20000U, sctimerClock, &event) == kStatus_Fail)
    13.     {
    14.     return -1;
    15.     }

    16.     /* Configure second PWM with different duty cycle but same frequency as before */
    17.     pwmParam.output           = kSCTIMER_Out_1;
    18.     pwmParam.level            = kSCTIMER_HighTrue;
    19.     pwmParam.dutyCyclePercent = 20;
    20.     if (SCTIMER_SetupPwm(SCT0, &pwmParam, kSCTIMER_CenterAlignedPwm, 20000U, sctimerClock, &event) == kStatus_Fail)
    21.     {
    22.     return -1;
    23.     }

    24.     SCTIMER_StartTimer(SCT0, kSCTIMER_Counter_U);
    复制代码
          效果如下:
    16.png










    回复

    使用道具 举报

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

    本版积分规则

    关闭

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

    手机版|小黑屋|与非网

    GMT+8, 2024-12-24 01:54 , Processed in 0.123521 second(s), 18 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.