【FRDM-MCXN947测评】相移PWM输出测试
[复制链接]
本帖最后由 bigbat 于 2025-1-24 16:02 编辑
1、测试介绍
MCXN947系列MCU的FlexPWM 模块,可以通过独立的时钟控制两个通道输出:具有可控制相位输出的PWM波形。每个通道A相或B相可以独立的任意调节PWM相位。该功能可以实现:
中心对齐PWM:
边缘对齐PWM:
相移PWM:
双开关 PWM:
其中的相移输出是我工作中需要的输出,旧有的设计需要多个计数器相互配合实现复杂,而且输出精度和实时性较差,不能实现较为精准的输出和控制。我的要求是:两个通道,A先开启进行预充电,然后在开启输出功率管工作。原有的设计经常出现中断的现象。就是由于相位控制不精确造成的。所以该功能正是我需要的。本次测试也是对该功能进行测试。
硬件:
FRDM-MCXN947开发板一块
双通道100M示波器一台
USB TYPE-C电缆
软件:
GCC ARM Embedded 13.2.1编译器。
测试实现的效果如:
PWM_A相:输出
PWM_B相:输出
2、设置及程序分析
该测试程序参考MCXN947的SDK PWM程序。
NXP的配置工具MCUXpresso Config Tools v16.1除了可以配置芯片引脚路由外,其它的我到现在都没有发现其它的更详细的配置项目。
第一段:
程序的思路是通过MCU PWM的两个寄存器来控制PWM的脉宽位置。参考下面的初始化代码。
/*******************************************************************************
* Code
******************************************************************************/
static void PWM_DRV_Init3PhPwm(void)
{
uint16_t deadTimeVal;
pwm_signal_param_t pwmSignal[2];
uint32_t pwmSourceClockInHz;
uint32_t pwmFrequencyInHz = APP_DEFAULT_PWM_FREQUENCY;
pwmSourceClockInHz = PWM_SRC_CLK_FREQ;
/* Set deadtime count, we set this to about 650ns */
deadTimeVal = ((uint64_t)pwmSourceClockInHz * 650) / 1000000000;
pwmSignal[0].pwmChannel = kPWM_PwmA;
pwmSignal[0].level = kPWM_HighTrue;
pwmSignal[0].dutyCyclePercent = 50; /* 1 percent dutycycle */
pwmSignal[0].deadtimeValue = deadTimeVal;
pwmSignal[0].faultState = kPWM_PwmFaultState0;
pwmSignal[0].pwmchannelenable = true;
pwmSignal[1].pwmChannel = kPWM_PwmB;
pwmSignal[1].level = kPWM_HighTrue;
/* Dutycycle field of PWM B does not matter as we are running in PWM A complementary mode */
pwmSignal[1].dutyCyclePercent = 50;
pwmSignal[1].deadtimeValue = deadTimeVal;
pwmSignal[1].faultState = kPWM_PwmFaultState0;
pwmSignal[1].pwmchannelenable = true;
/*********** PWMA_SM0 - phase A, configuration, setup 2 channel as an example ************/
PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_0, pwmSignal, 2, kPWM_SignedCenterAligned, pwmFrequencyInHz,
pwmSourceClockInHz);
/*********** PWMA_SM1 - phase B configuration, setup PWM A channel only ************/
#ifdef DEMO_PWM_CLOCK_DEVIDER
PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_1, pwmSignal, 1, kPWM_SignedCenterAligned, pwmFrequencyInHz,
pwmSourceClockInHz / (1 << DEMO_PWM_CLOCK_DEVIDER));
#else
PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_1, pwmSignal, 1, kPWM_SignedCenterAligned, pwmFrequencyInHz,
pwmSourceClockInHz);
#endif
/*********** PWMA_SM2 - phase C configuration, setup PWM A channel only ************/
#ifdef DEMO_PWM_CLOCK_DEVIDER
PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_2, pwmSignal, 1, kPWM_SignedCenterAligned, pwmFrequencyInHz,
pwmSourceClockInHz / (1 << DEMO_PWM_CLOCK_DEVIDER));
#else
PWM_SetupPwm(BOARD_PWM_BASEADDR, kPWM_Module_2, pwmSignal, 1, kPWM_SignedCenterAligned, pwmFrequencyInHz,
pwmSourceClockInHz);
#endif
}
整个参数配置为
一个结构体 pwm_signal_param_t pwmSignal[2];
一个配置函数PWM_SetupPwm();
函数设计有点过于抽象,函数的设计思路是按照工程化的思维模式设计的,例如:占空比(pwmSignal[0].dutyCyclePercent = 50;)、相位角(pwmSignal[0].deadtimeValue = deadTimeVal;)等概念来输入。对于我这种嵌入式程序思维的人来说,总是觉得不够直接(好像没有掌控感,呵呵!好像NXP不能够让我装了,有点不爽呃)
第二段:
这个思路主要是控制PWM的输出。参考下面代码
/*!
* [url=home.php?mod=space&uid=159083]@brief[/url] Main function
*/
int main(void)
{
/* Structure of initialize PWM */
pwm_config_t pwmConfig;
pwm_fault_param_t faultConfig;
uint32_t pwmVal = 4;
/* Board pin, clock, debug console init */
/* attach FRO 12M to FLEXCOMM4 (debug console) */
CLOCK_SetClkDiv(kCLOCK_DivFlexcom4Clk, 1u);
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
BOARD_InitPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole();
/* Enable PWM1 SUB Clockn */
SYSCON->PWM1SUBCTL |=
(SYSCON_PWM1SUBCTL_CLK0_EN_MASK | SYSCON_PWM1SUBCTL_CLK1_EN_MASK | SYSCON_PWM1SUBCTL_CLK2_EN_MASK);
PRINTF("FlexPWM driver example\n");
/*
* pwmConfig.enableDebugMode = false;
* pwmConfig.enableWait = false;
* pwmConfig.reloadSelect = kPWM_LocalReload;
* pwmConfig.clockSource = kPWM_BusClock;
* pwmConfig.prescale = kPWM_Prescale_Divide_1;
* pwmConfig.initializationControl = kPWM_Initialize_LocalSync;
* pwmConfig.forceTrigger = kPWM_Force_Local;
* pwmConfig.reloadFrequency = kPWM_LoadEveryOportunity;
* pwmConfig.reloadLogic = kPWM_ReloadImmediate;
* pwmConfig.pairOperation = kPWM_Independent;
*/
PWM_GetDefaultConfig(&pwmConfig);
#ifdef DEMO_PWM_CLOCK_DEVIDER
pwmConfig.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_ComplementaryPwmA;
pwmConfig.enableDebugMode = true;
/* Initialize submodule 0 */
if (PWM_Init(BOARD_PWM_BASEADDR, kPWM_Module_0, &pwmConfig) == kStatus_Fail)
{
PRINTF("PWM initialization failed\n");
return 1;
}
/* Initialize submodule 1, make it use same counter clock as submodule 0. */
pwmConfig.clockSource = kPWM_Submodule0Clock;
pwmConfig.prescale = kPWM_Prescale_Divide_1;
pwmConfig.initializationControl = kPWM_Initialize_MasterSync;
if (PWM_Init(BOARD_PWM_BASEADDR, kPWM_Module_1, &pwmConfig) == kStatus_Fail)
{
PRINTF("PWM initialization failed\n");
return 1;
}
/* Initialize submodule 2 the same way as submodule 1 */
if (PWM_Init(BOARD_PWM_BASEADDR, kPWM_Module_2, &pwmConfig) == kStatus_Fail)
{
PRINTF("PWM initialization failed\n");
return 1;
}
/*
* config->faultClearingMode = kPWM_Automatic;
* config->faultLevel = false;
* config->enableCombinationalPath = true;
* config->recoverMode = kPWM_NoRecovery;
*/
PWM_FaultDefaultConfig(&faultConfig);
#ifdef DEMO_PWM_FAULT_LEVEL
faultConfig.faultLevel = DEMO_PWM_FAULT_LEVEL;
#endif
/* Sets up the PWM fault protection */
PWM_SetupFaults(BOARD_PWM_BASEADDR, kPWM_Fault_0, &faultConfig);
PWM_SetupFaults(BOARD_PWM_BASEADDR, kPWM_Fault_1, &faultConfig);
PWM_SetupFaults(BOARD_PWM_BASEADDR, kPWM_Fault_2, &faultConfig);
PWM_SetupFaults(BOARD_PWM_BASEADDR, kPWM_Fault_3, &faultConfig);
/* Set PWM fault disable mapping for submodule 0/1/2 */
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_faultchannel_0,
kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, kPWM_Module_1, kPWM_PwmA, kPWM_faultchannel_0,
kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, 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_DRV_Init3PhPwm();
/* Set the load okay bit for all submodules to load registers from their buffer */
PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0 | kPWM_Control_Module_1 | kPWM_Control_Module_2, true);
/* Start the PWM generation from Submodules 0, 1 and 2 */
PWM_StartTimer(BOARD_PWM_BASEADDR, kPWM_Control_Module_0 | kPWM_Control_Module_1 | kPWM_Control_Module_2);
while (1U)
{
/* Delay at least 100 PWM periods. */
SDK_DelayAtLeastUs((1000000U / APP_DEFAULT_PWM_FREQUENCY) * 100, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
pwmVal = pwmVal + 4;
/* Reset the duty cycle percentage */
if (pwmVal > 100)
{
pwmVal = 4;
}
/* Update duty cycles for all 3 PWM signals */
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_SignedCenterAligned, pwmVal);
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_1, kPWM_PwmA, kPWM_SignedCenterAligned, (pwmVal >> 1));
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_2, kPWM_PwmA, kPWM_SignedCenterAligned, (pwmVal >> 2));
/* Set the load okay bit for all submodules to load registers from their buffer */
PWM_SetPwmLdok(BOARD_PWM_BASEADDR, kPWM_Control_Module_0 | kPWM_Control_Module_1 | kPWM_Control_Module_2, true);
}
主要代码是:
(1)初始化模块(submodule 0/1/2)
/* Set PWM fault disable mapping for submodule 0/1/2 */
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_faultchannel_0,
kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, kPWM_Module_1, kPWM_PwmA, kPWM_faultchannel_0,
kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);
PWM_SetupFaultDisableMap(BOARD_PWM_BASEADDR, kPWM_Module_2, kPWM_PwmA, kPWM_faultchannel_0,
kPWM_FaultDisable_0 | kPWM_FaultDisable_1 | kPWM_FaultDisable_2 | kPWM_FaultDisable_3);
(2)开始定时器
PWM_StartTimer(BOARD_PWM_BASEADDR, kPWM_Control_Module_0 | kPWM_Control_Module_1 | kPWM_Control_Module_2);
(3)程序循环中控制PWM脉冲的量值
/* Update duty cycles for all 3 PWM signals */
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_0, kPWM_PwmA, kPWM_SignedCenterAligned, pwmVal);
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_1, kPWM_PwmA, kPWM_SignedCenterAligned, (pwmVal >> 1));
PWM_UpdatePwmDutycycle(BOARD_PWM_BASEADDR, kPWM_Module_2, kPWM_PwmA, kPWM_SignedCenterAligned, (pwmVal >> 2));
3、测试过程
(1) 将A0相输出和A1输出连接到示波器上去。
(2)将USB Type-C电缆连接到PC主机
(3)运行程序
调节示波器,可以查看输出波形。
波形输出为设计的方式,但是波形出现了很多的“过冲”现象,不知道是什么原因造成的。
4、总结
通过测试可以更加深入的了解NXP FlexPWM的控制原理,NXP使用较为工程化的函数封装来简化编程的复杂度。我个人觉得如果想很好的了解FlexPWM的模块工作原理还是需要了解各种寄存器和模块的设计原理。
|