Hello, I am trying to control the duty cycle of FlexPWM on the RT1060 using DMA. Could you please review my source code and check if there are any issues? The code is not working as expected.
void pwmInit(void)
{
uint32_t pwmFrequencyInHz = DEFAULT_PWM_FREQUENCY;
uint32_t pwmSourceClockInHz = PWM_SRC_CLK_FREQ;
/*===============================
1. Clock Setup (Reusing Previous Settings)
===============================*/
// CLOCK_SetDiv(kCLOCK_IpgDiv, 11); // IPG clock = 600MHz / (11+1) = 50MHz
// CLOCK_SetDiv(kCLOCK_AhbDiv, 0x2); // AHB clock = 600MHz / 3 = 200MHz
// CLOCK_SetDiv(kCLOCK_IpgDiv, 0x3); // IPG clock = 200MHz / 4 = 50MHz
/*===============================
2. Pin Configuration (IOMUXC_GPIO_AD_B0_10_FLEXPWM1_PWMA03 -> FlexPWM1_PWMA03)
===============================*/
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_10_FLEXPWM1_PWMA03, 0U);
IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_10_FLEXPWM1_PWMA03, 0x10B0U);
/*===============================
3. XBARA Configuration (Disable Fault Inputs)
===============================*/
XBARA_Init(XBARA1);
XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault0);
XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1Fault1);
XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1234Fault2);
XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm1234Fault3);
/*===============================
4. FlexPWM Configuration
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_config_t pwmConfig;
PWM_GetDefaultConfig(&pwmConfig);
/*===============================
PWM Configuration: Independent Mode, 50% Duty Cycle, 20kHz
===============================*/
pwmConfig.enableDebugMode = true; // Enable debug mode
pwmConfig.reloadLogic = kPWM_ReloadPwmFullCycle; // Reload after full cycle
pwmConfig.pairOperation = kPWM_Independent;
pwmConfig.clockSource = kPWM_BusClock; // Use IPG clock (150MHz)
pwmConfig.prescale = kPWM_Prescale_Divide_2; // If divide is 1, 150MHz/2000 results in 75000, which exceeds 2-byte 65535, causing incorrect frequency output.
/*===============================
Initialize FlexPWM1 Submodule 3
===============================*/
PWM_Init(PWM_BASEADDR, kPWM_Module_3, &pwmConfig);
/*===============================
config->faultClearingMode = kPWM_Automatic;
config->faultLevel = false;
config->enableCombinationalPath = true;
config->recoverMode = kPWM_NoRecovery;
===============================*/
pwm_fault_param_t faultConfig;
PWM_FaultDefaultConfig(&faultConfig);
PWM_SetupFaults(PWM_BASEADDR, kPWM_Fault_3, &faultConfig);
PWM_OutputTriggerEnable(PWM_BASEADDR, kPWM_Module_3, kPWM_ValueRegister_3, true);
/*===============================
===============================*/
pwm_signal_param_t pwmSignal;
pwmSignal.pwmChannel = kPWM_PwmA; // Use Channel A
pwmSignal.level = kPWM_HighTrue; // High True output
pwmSignal.dutyCyclePercent = 50; // 50% duty cycle
pwmSignal.faultState = kPWM_PwmFaultState0;
pwmSignal.pwmchannelenable = true;
PWM_SetupPwm(PWM_BASEADDR, kPWM_Module_3, &pwmSignal, 1, kPWM_SignedCenterAligned, pwmFrequencyInHz, pwmSourceClockInHz);
PWM_EnableDMAWrite(PWM_BASEADDR, kPWM_Module_3, true);
// Start PWM
PWM_SetPwmLdok(PWM_BASEADDR, kPWM_Control_Module_3, true);
PWM_StartTimer(PWM_BASEADDR, kPWM_Control_Module_3);
}
DMA Initialization and Callback Function
void dmaInit(void)
{
edma_config_t dmaConfig;
edma_transfer_config_t transferConfig;
// DMAMUX Initialization
DMAMUX_Init(DMAMUX);
DMAMUX_SetSource(DMAMUX, 0, kDmaRequestMuxFlexPWM1ValueSub3);
DMAMUX_EnableChannel(DMAMUX, 0);
// DMA Initialization
EDMA_GetDefaultConfig(&dmaConfig);
dmaConfig.enableContinuousLinkMode = true;
dmaConfig.enableRoundRobinArbitration = true;
EDMA_Init(DMA0, &dmaConfig);
EDMA_CreateHandle(&g_edmaHandle, DMA0, 0);
EDMA_SetCallback(&g_edmaHandle, DMA_Callback, NULL);
// DMA Transfer Configuration (Transfer data to PWM VALx register)
EDMA_PrepareTransfer(&transferConfig,
pwmDutyBuffer, 2,
(void *)&PWM_BASEADDR->SM[kPWM_Module_3].VAL3, 2,
2, sizeof(pwmDutyBuffer),
kEDMA_MemoryToPeripheral);
EDMA_SetModulo(DMA0, 0, kEDMA_Modulo32bytes, kEDMA_ModuloDisable);
EDMA_SubmitTransfer(&g_edmaHandle, &transferConfig);
EDMA_StartTransfer(&g_edmaHandle);
EDMA_EnableChannelRequest(DMA0, 0);
// Second DMA channel for LDOK setting (optional)
// Additional DMA channel for setting MCTRL[LDOK]
}
void DMA_Callback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{
if (transferDone)
{
// Set LDOK bit
PWM_SetPwmLdok(PWM_BASEADDR, kPWM_Control_Module_3, true);
}
}
Could you please review the source code? It is not working as expected.
Hello @pooisonner,
To better assist you, could you please provide the following details?
BR
Habib
1. PWM is operating.
However, it only operates once, and the DMA callback occurs once and does not occur again afterward.
There are no other error messages.
2. I am experimenting with PWM duty control using DMA.
3. PWM_Base is PWM1, and it is outputting through submodule 3.
4. The PWM duty buffer is
uint16_t pwmDutyBuffer[10] = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}; // Example: Duty cycle values
Please respond.
Hello @pooisonner,
It appears the problem stems from the way g_edmaHandle is currently being instantiated. Right now, you're creating g_edmaHandle inside the dmaInit function. This means that the memory allocated and configured for g_edmaHandle is local to that function and gets discarded once dmaInit completes execution. As a result, any values set within g_edmaHandle are lost.
This behavior is evident in the first two transfers:
First transfer (Works correctly):
Second transfer (Fails due to missing or incorrect parameters):
To resolve this, please declare g_edmaHandle as a global variable. This will ensure that its configuration persists beyond the scope of dmaInit, maintaining the integrity of the EDMA handle across multiple transfers.
BR
Habib