RT1010 PWM 组成配置和 PWMX 的使用

1. 前言

        本篇博文将着眼于 i.MX RT1010 内部的 eFlexPWM,介绍其各个功能模块,以及 PWM 产生的原理。

2. 功能模块组成

        以下是 RT1010 内部 PWM 的一个 Submoudle 的组成框图,从框图中我们可以看到:

  1. 自左向右依次有 Prescaler 对时钟进行预分频,分频之后的时钟作为 16 bit counter 计数器的时钟,驱动其计数,计数的初始值由 Counter preload mux 根据不同的同步方式进行选择重载;
  2. 接下来将由 3 组 6 个比较器对 0-5 Campare value 寄存器的数值与 16 bit counter 的计数值进行比较:
    1. 对由 Compare 0 value 寄存器和 Compare 1 value 寄存器为一组的数值与当前的计数值进行比较,输出的比较值连接到 Reload Logic 进行 Master Reload 控制;另外通过 Compare 0 value 寄存器与当前计数值的值进行比较,通过 Pin Mux 可将四个 GPIO 端口复用为 PWMX 进行 PWM 的输出。
    2. 对由 Compare 2 value 寄存器和 Compare 3 value 寄存器为一组的数值与当前的计数值进行比较,分别控制一个 SR 触发的 S 端和 R 端,当 S 为 1 时 PWM 输出高电平,R 为 1 时,PWM 输出低电平,这样通过  Compare 2 value 寄存器和 Compare 3 value 寄存器即可输出一路 PWM23;
    3. 类似的可以通过 Compare 4 value 寄存器和 Compare 5 value 寄存器输出另一路 PWM45;
  3. PWM23 和 PWM45 经过 Comp. vs Indep. 模块的处理用于控制输出独立的或互补的 PWM 的波形,通过 Deed Time Cenerator 的处理用以插入死区时间,通过 Mux Select Logic 用以选择 GPIO 的引脚复用;最后一级的模块将对这两路 PWM 由 Fault protection 和 Output override control  进行出错保护。

 图 1. PWM 子模块框图

        通过 MCUXpresso Config Tools 可以看到芯片内部只有一个 PWM1,PWM1 下面有 4 个 Submoudles,每个 Submodle 可以控制 A 和 B 两个可互补的通道和一个 X  通道,可以针对有相应复用功能的引脚进行 PWM 功能复用。

        参考 SDK 的 PWM 例程,在枚举 _pwm_init_source 中对 counter 的重载方式进行了定义,可选的方式有本地同步(LocalSync)、主模块重载同步(MasterReload)、主模块同步(MasterSync)、外部同步(ExtSync)。不同的同步方式将影响到模块 4 个 Submodle 的计数器同步方式。例如对于选用主模块同步 MasterSync 这种方式将导致四个模块的计数器重载与 Submoulde 0 的重载时机一样,而采用本地同步(LocalSync)则不会受到主模块同步的影响。

/*! @brief PWM counter initialization options */
typedef enum _pwm_init_source
{kPWM_Initialize_LocalSync = 0U, /*!< Local sync causes initialization */kPWM_Initialize_MasterReload,   /*!< Master reload from submodule 0 causes initialization */kPWM_Initialize_MasterSync,     /*!< Master sync from submodule 0 causes initialization */kPWM_Initialize_ExtSync         /*!< EXT_SYNC causes initialization */
} pwm_init_source_t;

        PWM 模式,在枚举 _pwm_mode 中定义,kPWM_SignedCenterAligned 和 kPWM_CenterAligned 是中央对其方式,kPWM_SignedEdgeAligned 和 kPWM_EdgeAligned 是边缘对齐方式,Signed 和 Unsigned 指的是 INIT 计数器的初始值是有符号数和无符号数。

        由于 SDK 中没有 PWMX PWM 生成的代码例程,本篇博文将添加一种 kPWM_NonAligned 方式进行测试。 

/*! @brief PWM operation mode */
typedef enum _pwm_mode
{kPWM_SignedCenterAligned = 0U, /*!< Signed center-aligned */kPWM_CenterAligned,            /*!< Unsigned cente-aligned */kPWM_SignedEdgeAligned,        /*!< Signed edge-aligned */kPWM_EdgeAligned,               /*!< Unsigned edge-aligned */kPWM_NonAligned               //added by joey wang 2023.2.8
} pwm_mode_t;

中央对齐模式:

        在此模式下,INIT 将向上计数,由 VAL2 通过比较器与计数值进行比较打开 PWMA 的上升边沿,VAL3 打开 PWMA 的下降边沿; 由 VAL4 打开 PWMB 的上升边沿,VAL5 打开 PWMB 的下降边沿,不难看出 Singed 模式下数值对称,有利于计算。

 边沿对齐模式:

        在边沿对齐模式下,INIT 的取值和 VAL2 和 VAL4 的取值一样,即 PWMA 和 PWMB 上升沿 PWM 打开的时机一样,即达到了边沿对齐的目的,只需要指定 VAL3 和 VAL5 的数值,即可改变 PWMA 或 PWMB 的占空比。

实验现象:

在 PWM_SetupPwm 中增加 kPWM_NonAligned 的 case 判断:

status_t PWM_SetupPwm(PWM_Type *base,pwm_submodule_t subModule,const pwm_signal_param_t *chnlParams,uint8_t numOfChnls,pwm_mode_t mode,uint32_t pwmFreq_Hz,uint32_t srcClock_Hz)
{assert(chnlParams);assert(pwmFreq_Hz);assert(numOfChnls);assert(srcClock_Hz);uint32_t pwmClock;uint16_t pulseCnt = 0, pwmHighPulse = 0;uint16_t modulo = 0;uint8_t i, polarityShift = 0, outputEnableShift = 0;if (numOfChnls > 3U){/* Each submodule has 3 signals; PWM A & PWM B & PWM X*/return kStatus_Fail;}/* Divide the clock by the prescale value */pwmClock = (srcClock_Hz / (1UL << ((base->SM[subModule].CTRL & PWM_CTRL_PRSC_MASK) >> PWM_CTRL_PRSC_SHIFT)));pulseCnt = (uint16_t)(pwmClock / pwmFreq_Hz);/* Setup each PWM channel */for (i = 0; i < numOfChnls; i++){/* Calculate pulse width */pwmHighPulse = (pulseCnt * chnlParams->dutyCyclePercent) / 100U;/* Setup the different match registers to generate the PWM signal */switch (mode){case kPWM_SignedCenterAligned:/* Setup the PWM period for a signed center aligned signal */if (i == 0U){modulo = (pulseCnt >> 1U);/* Indicates the start of the PWM period */base->SM[subModule].INIT = PWM_GetComplementU16(modulo);/* Indicates the center value */base->SM[subModule].VAL0 = 0;/* Indicates the end of the PWM period *//* The change during the end to start of the PWM period requires a count time */base->SM[subModule].VAL1 = modulo - 1U;}/* Setup the PWM dutycycle */if (chnlParams->pwmChannel == kPWM_PwmA){base->SM[subModule].VAL2 = PWM_GetComplementU16(pwmHighPulse / 2U);base->SM[subModule].VAL3 = (pwmHighPulse / 2U);}else if(chnlParams->pwmChannel == kPWM_PwmB){base->SM[subModule].VAL4 = PWM_GetComplementU16(pwmHighPulse / 2U);base->SM[subModule].VAL5 = (pwmHighPulse / 2U);}break;case kPWM_CenterAligned:/* Setup the PWM period for an unsigned center aligned signal *//* Indicates the start of the PWM period */if (i == 0U){base->SM[subModule].INIT = 0;/* Indicates the center value */base->SM[subModule].VAL0 = (pulseCnt / 2U);/* Indicates the end of the PWM period *//* The change during the end to start of the PWM period requires a count time */base->SM[subModule].VAL1 = pulseCnt - 1U;}/* Setup the PWM dutycycle */if (chnlParams->pwmChannel == kPWM_PwmA){base->SM[subModule].VAL2 = ((pulseCnt - pwmHighPulse) / 2U);base->SM[subModule].VAL3 = ((pulseCnt + pwmHighPulse) / 2U);}else{base->SM[subModule].VAL4 = ((pulseCnt - pwmHighPulse) / 2U);base->SM[subModule].VAL5 = ((pulseCnt + pwmHighPulse) / 2U);}break;case kPWM_SignedEdgeAligned:/* Setup the PWM period for a signed edge aligned signal */if (i == 0U){modulo = (pulseCnt >> 1U);/* Indicates the start of the PWM period */base->SM[subModule].INIT = PWM_GetComplementU16(modulo);/* Indicates the center value */base->SM[subModule].VAL0 = 0;/* Indicates the end of the PWM period *//* The change during the end to start of the PWM period requires a count time */base->SM[subModule].VAL1 = modulo - 1U;}/* Setup the PWM dutycycle */if (chnlParams->pwmChannel == kPWM_PwmA){base->SM[subModule].VAL2 = PWM_GetComplementU16(modulo);base->SM[subModule].VAL3 = PWM_GetComplementU16(modulo) + pwmHighPulse;}else{base->SM[subModule].VAL4 = PWM_GetComplementU16(modulo);base->SM[subModule].VAL5 = PWM_GetComplementU16(modulo) + pwmHighPulse;}break;case kPWM_EdgeAligned:/* Setup the PWM period for a unsigned edge aligned signal *//* Indicates the start of the PWM period */if (i == 0U){base->SM[subModule].INIT = 0;/* Indicates the center value */base->SM[subModule].VAL0 = (pulseCnt / 2U);/* Indicates the end of the PWM period *//* The change during the end to start of the PWM period requires a count time */base->SM[subModule].VAL1 = pulseCnt - 1U;}/* Setup the PWM dutycycle */if (chnlParams->pwmChannel == kPWM_PwmA){base->SM[subModule].VAL2 = 0;base->SM[subModule].VAL3 = pwmHighPulse;}else{base->SM[subModule].VAL4 = 0;base->SM[subModule].VAL5 = pwmHighPulse;}break;/*added by joey wang at 2023.2.14*/case kPWM_NonAligned:/* Setup the PWM period for a signed center aligned signal */if (i == 0U){/* Setup the PWM period for a PWM_X non-aligned signal */pulseCnt = base->SM[subModule].VAL1;/* Indicates the start of the PWM period */base->SM[subModule].INIT = 0;/* Indicates the center value */base->SM[subModule].VAL0 = (pulseCnt / 2);/* Indicates the end of the PWM period */base->SM[subModule].VAL1 = pulseCnt;}/*initialize duty circle, VAL0/VAL1 is the duty circle, note by joey 2023.2.16*/if(chnlParams->pwmChannel == kPWM_PwmX){PRINTF("kPWM_PwmX duty is set\t\n");base->SM[subModule].VAL0 = pwmHighPulse;base->SM[subModule].VAL1 = pwmHighPulse*2;/*PRINTF("VAL0 = %d\r\n",base->SM[subModule].VAL0);PRINTF("VAL1 = %d\r\n",base->SM[subModule].VAL1);*/}break;default:assert(false);break;}/* Setup register shift values based on the channel being configured.* Also setup the deadtime value*/if (chnlParams->pwmChannel == kPWM_PwmA){polarityShift              = PWM_OCTRL_POLA_SHIFT;outputEnableShift          = PWM_OUTEN_PWMA_EN_SHIFT;base->SM[subModule].DTCNT0 = PWM_DTCNT0_DTCNT0(chnlParams->deadtimeValue);}else if(chnlParams->pwmChannel == kPWM_PwmB){polarityShift              = PWM_OCTRL_POLB_SHIFT;outputEnableShift          = PWM_OUTEN_PWMB_EN_SHIFT;base->SM[subModule].DTCNT1 = PWM_DTCNT1_DTCNT1(chnlParams->deadtimeValue);}else{		polarityShift              = PWM_OCTRL_POLX_SHIFT;outputEnableShift          = PWM_OUTEN_PWMX_EN_SHIFT;base->SM[subModule].DTCNT0 = PWM_DTCNT1_DTCNT1(chnlParams->deadtimeValue);}/* Set PWM output fault status */switch (chnlParams->pwmChannel){case kPWM_PwmA:base->SM[subModule].OCTRL &= ~((uint16_t)PWM_OCTRL_PWMAFS_MASK);base->SM[subModule].OCTRL |= (((uint16_t)(chnlParams->faultState) << (uint16_t)PWM_OCTRL_PWMAFS_SHIFT) &(uint16_t)PWM_OCTRL_PWMAFS_MASK);break;case kPWM_PwmB:base->SM[subModule].OCTRL &= ~((uint16_t)PWM_OCTRL_PWMBFS_MASK);base->SM[subModule].OCTRL |= (((uint16_t)(chnlParams->faultState) << (uint16_t)PWM_OCTRL_PWMBFS_SHIFT) &(uint16_t)PWM_OCTRL_PWMBFS_MASK);break;case kPWM_PwmX:base->SM[subModule].OCTRL &= ~((uint16_t)PWM_OCTRL_PWMXFS_MASK);base->SM[subModule].OCTRL |= (((uint16_t)(chnlParams->faultState) << (uint16_t)PWM_OCTRL_PWMXFS_SHIFT) &(uint16_t)PWM_OCTRL_PWMXFS_MASK);break;default:assert(false);break;}/* Setup signal active level */if ((bool)chnlParams->level == kPWM_LowTrue)   // in this setting to set output level high or low, note by joey at 2023.2.16{base->SM[subModule].OCTRL &= ~((uint16_t)1U << (uint16_t)polarityShift);}else{base->SM[subModule].OCTRL |= ((uint16_t)1U << (uint16_t)polarityShift);}/* Enable PWM output */base->OUTEN |= ((uint16_t)1U << ((uint16_t)outputEnableShift + (uint16_t)subModule));/* Get the next channel parameters */chnlParams++;}return kStatus_Success;
}

再更新 PWM_UpdatePwmDutycycleHighAccuracy 函数

void PWM_UpdatePwmDutycycleHighAccuracy(PWM_Type *base, pwm_submodule_t subModule, pwm_channels_t pwmSignal, pwm_mode_t currPwmMode, uint16_t dutyCycle)
{assert((uint16_t)pwmSignal < 3U);uint16_t pulseCnt = 0, pwmHighPulse = 0;uint16_t modulo = 0;switch (currPwmMode){case kPWM_SignedCenterAligned:modulo   = base->SM[subModule].VAL1 + 1U;pulseCnt = modulo * 2U;/* Calculate pulse width */pwmHighPulse = (pulseCnt * dutyCycle) / 65535U;/* Setup the PWM dutycycle */if (pwmSignal == kPWM_PwmA){base->SM[subModule].VAL2 = PWM_GetComplementU16(pwmHighPulse / 2U);base->SM[subModule].VAL3 = (pwmHighPulse / 2U);}else{base->SM[subModule].VAL4 = PWM_GetComplementU16(pwmHighPulse / 2U);base->SM[subModule].VAL5 = (pwmHighPulse / 2U);}break;case kPWM_CenterAligned:pulseCnt = base->SM[subModule].VAL1 + 1U;/* Calculate pulse width */pwmHighPulse = (pulseCnt * dutyCycle) / 65535U;/* Setup the PWM dutycycle */if (pwmSignal == kPWM_PwmA){base->SM[subModule].VAL2 = ((pulseCnt - pwmHighPulse) / 2U);base->SM[subModule].VAL3 = ((pulseCnt + pwmHighPulse) / 2U);}else{base->SM[subModule].VAL4 = ((pulseCnt - pwmHighPulse) / 2U);base->SM[subModule].VAL5 = ((pulseCnt + pwmHighPulse) / 2U);}break;case kPWM_SignedEdgeAligned:modulo   = base->SM[subModule].VAL1 + 1U;pulseCnt = modulo * 2U;/* Calculate pulse width */pwmHighPulse = (pulseCnt * dutyCycle) / 65535U;/* Setup the PWM dutycycle */if (pwmSignal == kPWM_PwmA){base->SM[subModule].VAL2 = PWM_GetComplementU16(modulo);base->SM[subModule].VAL3 = PWM_GetComplementU16(modulo) + pwmHighPulse;}else{base->SM[subModule].VAL4 = PWM_GetComplementU16(modulo);base->SM[subModule].VAL5 = PWM_GetComplementU16(modulo) + pwmHighPulse;}break;case kPWM_EdgeAligned:pulseCnt = base->SM[subModule].VAL1 + 1U;/* Calculate pulse width */pwmHighPulse = (pulseCnt * dutyCycle) / 65535U;/* Setup the PWM dutycycle */if (pwmSignal == kPWM_PwmA){base->SM[subModule].VAL2 = 0;base->SM[subModule].VAL3 = pwmHighPulse;}else{base->SM[subModule].VAL4 = 0;base->SM[subModule].VAL5 = pwmHighPulse;}break;case kPWM_NonAligned:pulseCnt = base->SM[subModule].VAL1 + 1U;/* Calculate pulse width */pwmHighPulse = (pulseCnt * dutyCycle) / 65535U;/* Setup the PWM dutycycle */if (pwmSignal == kPWM_PwmA){base->SM[subModule].VAL2 = 0;base->SM[subModule].VAL3 = pwmHighPulse;}else if (pwmSignal == kPWM_PwmB){base->SM[subModule].VAL4 = 0;base->SM[subModule].VAL5 = pwmHighPulse;}//add PWMX duty update hereelse{base->SM[subModule].VAL0 = pwmHighPulse;//base->SM[subModule].VAL1 = 0;PRINTF("pwmHighPulse = %d\r\n",pwmHighPulse);PRINTF("VAL1 = %d\r\n",base->SM[subModule].VAL1);}break;default:assert(false);break;}
}

        通过添加以上对 kPWM_NonAligned 输出 PWMX 的代码,再通过 PWM configure 函数,对 PWMX 的输出方式进行配置,代码如下,即可输出指定占空比的 PWMX 的 PWM 波形。

//PWM
void pwm_config(pwm_no_t pwm_no, uint32 frq ,gpio_no_t gpio_no)       //Configure PWM initial settings, frq: pwm frquency, 1 per 1Hz, 6 PWM channels share the same SCT, the frq value should be the same.  
{/* Structure of initialize PWM */pwm_config_t pwmConfig;pwm_fault_param_t faultConfig;CLOCK_SetDiv(kCLOCK_IpgDiv, 0x3); /* Set IPG PODF to 3, divede by 4 *//* Set the PWM Fault inputs to a low value */XBARA_Init(XBARA);XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault0);XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault1);XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault2);XBARA_SetSignalsConnection(XBARA, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault3);PWM_GetDefaultConfig(&pwmConfig);#ifdef DEMO_PWM_CLOCK_DEVIDERpwmConfig.prescale = DEMO_PWM_CLOCK_DEVIDER;
#endif/* Use full cycle reload */pwmConfig.reloadLogic = kPWM_ReloadPwmFullCycle;/* PWM A & PWM B form a complementary PWM pair */pwmConfig.pairOperation   = kPWM_Independent;pwmConfig.prescale        = kPWM_Prescale_Divide_4;pwmConfig.enableDebugMode = true;/* Initialize submodule 0 */if (PWM_Init(PWM1, kPWM_Module_0, &pwmConfig) == kStatus_Fail){PRINTF("PWM initialization failed\r\n");}pwmConfig.clockSource           = kPWM_BusClock;pwmConfig.prescale              = kPWM_Prescale_Divide_4;pwmConfig.initializationControl = kPWM_Initialize_LocalSync;	//initializationControlif (PWM_Init(PWM1, kPWM_Module_1, &pwmConfig) == kStatus_Fail){PRINTF("PWM initialization failed\r\n");}/* Initialize submodule 2 the same way as submodule 1 */if (PWM_Init(PWM1, kPWM_Module_2, &pwmConfig) == kStatus_Fail){PRINTF("PWM initialization failed\r\n");}/* Initialize submodule 3 the same way as submodule 1 */if (PWM_Init(PWM1, kPWM_Module_3, &pwmConfig) == kStatus_Fail){PRINTF("PWM initialization failed\r\n");}/**   config->faultClearingMode = kPWM_Automatic;*   config->faultLevel = false;*   config->enableCombinationalPath = true;*   config->recoverMode = kPWM_NoRecovery;*/PWM_FaultDefaultConfig(&faultConfig);#ifdef DEMO_PWM_FAULT_LEVELfaultConfig.faultLevel = DEMO_PWM_FAULT_LEVEL;
#endif/* Sets up the PWM fault protection */PWM_SetupFaults(PWM1, kPWM_Fault_0, &faultConfig);PWM_SetupFaults(PWM1, kPWM_Fault_1, &faultConfig);PWM_SetupFaults(PWM1, kPWM_Fault_2, &faultConfig);PWM_SetupFaults(PWM1, kPWM_Fault_3, &faultConfig);/* Set PWM fault disable mapping for submodule 0/1/2/3 */PWM_SetupFaultDisableMap(PWM1, kPWM_Module_0, kPWM_PwmA, kPWM_faultchannel_0,kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);PWM_SetupFaultDisableMap(PWM1, kPWM_Module_1, kPWM_PwmA, kPWM_faultchannel_0,kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);PWM_SetupFaultDisableMap(PWM1, kPWM_Module_2, kPWM_PwmA, kPWM_faultchannel_0,kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);/* Call the init function with demo configuration */pwm_set_param(pwm_no, frq, gpio_no, pwm_mode);/* Set the load okay bit for all submodules to load registers from their buffer */PWM_SetPwmLdok(PWM1, kPWM_Control_Module_0 | kPWM_Control_Module_1 | kPWM_Control_Module_2 | kPWM_Control_Module_3, true);/* Start the PWM generation from Submodules 0, 1 and 2 */PWM_StartTimer(PWM1, kPWM_Control_Module_0 | kPWM_Control_Module_1 | kPWM_Control_Module_2 | kPWM_Control_Module_3);
}static PWM_Type *const s_pwmBases[] = PWM_BASE_PTRS;static uint32_t PWM_GetInstance(PWM_Type *base)
{uint32_t instance;/* Find the instance index from base address mappings. */for (instance = 0; instance < ARRAY_SIZE(s_pwmBases); instance++){if (s_pwmBases[instance] == base){break;}}assert(instance < ARRAY_SIZE(s_pwmBases));return instance;
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部