Hello,
In our project we have 20 PWM outputs that must be synchronized so only one interrupt updates all 20 VAL3 (or VAL5 for channel B) registers.
Base on AN12200, I've setup FlexPWM1, SM0 to be the "main" PWM to which the others shall be synchronized. This is done by enabling the generation of the output trigger upon counter value matching with VAL1 register. I've also setup a PWM reload interrupt on this "main" PWM which, as I said, updates all required VALx registers.
I've configured the XBAR1 like this:
PWM1_SM0_TRIG0 -> XBARA1_IN40 -> XBARA_OUT44 -> PWM2_EXT_SYNC0
-> XBARA_OUT45 -> PWM2_EXT_SYNC1
-> XBARA_OUT46 -> PWM2_EXT_SYNC2
-> XBARA_OUT47 -> PWM2_EXT_SYNC3
-> XBARA_OUT59 -> PWM3_EXT_SYNC0
-> XBARA_OUT60 -> PWM3_EXT_SYNC1
-> XBARA_OUT61 -> PWM3_EXT_SYNC2
-> XBARA_OUT63 -> PWM3_EXT_SYNC3
However, the synchronization is not working between FlexPWM modules.
Here is the code that initializes the PWM outputs:
void InitializePWMModules(void)
{
/* PWM Pin Config */
CLOCK_EnableClock(kCLOCK_Pwm1);
CLOCK_EnableClock(kCLOCK_Pwm2);
CLOCK_EnableClock(kCLOCK_Pwm3);
CLOCK_EnableClock(kCLOCK_Xbar1);
// XBARA1->SEL15 = XBARA_SEL15_SEL31(0x28U);
// XBARA1->SEL16 = XBARA_SEL16_SEL32(0x28U) | XBARA_SEL16_SEL33(0x28U);
XBARA1->SEL22 = XBARA_SEL22_SEL44(0x28U) | XBARA_SEL22_SEL45(0x28U);
XBARA1->SEL23 = XBARA_SEL23_SEL46(0x28U) | XBARA_SEL23_SEL47(0x28U);
XBARA1->SEL29 = XBARA_SEL29_SEL59(0x28U);
XBARA1->SEL30 = XBARA_SEL30_SEL60(0x28U) | XBARA_SEL30_SEL61(0x28U);
XBARA1->SEL31 = XBARA_SEL31_SEL62(0x28U);
/* PWM_MCTRL: IPOL = 0, RUN = 0,CLDOK = 0xF,LDOK = 0 */
PWM1->MCTRL = PWM_MCTRL_IPOL(0) | PWM_MCTRL_RUN(0) | PWM_MCTRL_CLDOK(0xFU) | PWM_MCTRL_LDOK(0);
/* PWM_FCTRL0: FLVL=0, FAUTO=0, FSAFE=0, FIE=0 */
PWM1->FCTRL = 0x00U;
/* SM0CTRL2: DBGEN=0, WAITEN=0, INDEP=1, PWM23_INIT=0, PWM45_INIT=0, PWMX_INIT=0, INIT_SEL=0, FRCEN=1, FORCE=0, FORCE_SEL=0, RELOAD_SEL=0, CLK_SEL=0 */
PWM1->SM[0].CTRL2 = PWM_CTRL2_FRCEN(1) | PWM_CTRL2_INDEP(1);
/* SM1-3 CTRL2: DBGEN=0, WAITEN=0, INDEP=1, PWM23_INIT=0, PWM45_INIT=0, PWMX_INIT=0, INIT_SEL=2, FRCEN=1, FORCE=0, FORCE_SEL=1, RELOAD_SEL=1, CLK_SEL=2 */
PWM1->SM[1].CTRL2 = PWM_CTRL2_INIT_SEL(2) | PWM_CTRL2_FRCEN(1) | PWM_CTRL2_FORCE_SEL(1) | PWM_CTRL2_RELOAD_SEL(1) | PWM_CTRL2_CLK_SEL(2) | PWM_CTRL2_INDEP(1);
PWM1->SM[2].CTRL2 = PWM_CTRL2_INIT_SEL(2) | PWM_CTRL2_FRCEN(1) | PWM_CTRL2_FORCE_SEL(1) | PWM_CTRL2_RELOAD_SEL(1) | PWM_CTRL2_CLK_SEL(2) | PWM_CTRL2_INDEP(1);
PWM1->SM[3].CTRL2 = PWM_CTRL2_INIT_SEL(2) | PWM_CTRL2_FRCEN(1) | PWM_CTRL2_FORCE_SEL(1) | PWM_CTRL2_RELOAD_SEL(1) | PWM_CTRL2_CLK_SEL(2) | PWM_CTRL2_INDEP(1);
/* PWM_SMaCTRL: LDFQ=0, HALF=0, FULL=1, DT=0, COMPMODE=0, PRSC=0, SPLIT=0, LDMOD=0, DBLX=0, DBLEN=0 */
PWM1->SM[0].CTRL = PWM_CTRL_FULL(1);
PWM1->SM[1].CTRL = PWM_CTRL_FULL(1);
PWM1->SM[2].CTRL = PWM_CTRL_FULL(1);
PWM1->SM[3].CTRL = PWM_CTRL_FULL(1);
PWM1->SM[0].INIT = 0x0000U;
PWM1->SM[0].VAL0 = 0x0800U;
PWM1->SM[0].VAL1 = 0x0FFFU;
PWM1->SM[0].VAL2 = 0x0000U;
PWM1->SM[0].VAL3 = 0x0000U;
PWM1->SM[0].VAL4 = 0x0000U;
PWM1->SM[0].VAL5 = 0x0000U;
PWM1->SM[1].INIT = 0x0000U;
PWM1->SM[1].VAL0 = 0x0800U;
PWM1->SM[1].VAL1 = 0x0FFFU;
PWM1->SM[1].VAL2 = 0x0000U;
PWM1->SM[1].VAL3 = 0x0000U;
PWM1->SM[1].VAL4 = 0x0000U;
PWM1->SM[1].VAL5 = 0x0000U;
PWM1->SM[2].INIT = 0x0000U;
PWM1->SM[2].VAL0 = 0x0800U;
PWM1->SM[2].VAL1 = 0x0FFFU;
PWM1->SM[2].VAL2 = 0x0000U;
PWM1->SM[2].VAL3 = 0x0000U;
PWM1->SM[2].VAL4 = 0x0000U;
PWM1->SM[2].VAL5 = 0x0000U;
PWM1->SM[3].INIT = 0x0000U;
PWM1->SM[3].VAL0 = 0x0800U;
PWM1->SM[3].VAL1 = 0x0FFFU;
PWM1->SM[3].VAL2 = 0x0000U;
PWM1->SM[3].VAL3 = 0x0000U;
PWM1->SM[3].VAL4 = 0x0000U;
PWM1->SM[3].VAL5 = 0x0000U;
/* PWM_MCTRL: IPOL = 0, RUN = 0,CLDOK = 0xF,LDOK = 0 */
PWM2->MCTRL = PWM_MCTRL_IPOL(0) | PWM_MCTRL_RUN(0) | PWM_MCTRL_CLDOK(0xFU) | PWM_MCTRL_LDOK(0);
/* PWM_FCTRL0: FLVL=0, FAUTO=0, FSAFE=0, FIE=0 */
PWM2->FCTRL = 0x00U;
/* SM0CTRL2: DBGEN=0, WAITEN=0, INDEP=1, PWM23_INIT=0, PWM45_INIT=0, PWMX_INIT=0, INIT_SEL=3, FRCEN=1, FORCE=0, FORCE_SEL=0, RELOAD_SEL=0, CLK_SEL=0 */
PWM2->SM[0].CTRL2 = PWM_CTRL2_INIT_SEL(3) | PWM_CTRL2_FRCEN(1) | PWM_CTRL2_INDEP(1);
/* SM1-3 CTRL2: DBGEN=0, WAITEN=0, INDEP=1, PWM23_INIT=0, PWM45_INIT=0, PWMX_INIT=0, INIT_SEL=2, FRCEN=1, FORCE=0, FORCE_SEL=1, RELOAD_SEL=1, CLK_SEL=2 */
PWM2->SM[1].CTRL2 = PWM_CTRL2_INIT_SEL(3) | PWM_CTRL2_FRCEN(1) | PWM_CTRL2_FORCE_SEL(1) | PWM_CTRL2_RELOAD_SEL(1) | PWM_CTRL2_CLK_SEL(2) | PWM_CTRL2_INDEP(1);
PWM2->SM[2].CTRL2 = PWM_CTRL2_INIT_SEL(3) | PWM_CTRL2_FRCEN(1) | PWM_CTRL2_FORCE_SEL(1) | PWM_CTRL2_RELOAD_SEL(1) | PWM_CTRL2_CLK_SEL(2) | PWM_CTRL2_INDEP(1);
PWM2->SM[3].CTRL2 = PWM_CTRL2_INIT_SEL(3) | PWM_CTRL2_FRCEN(1) | PWM_CTRL2_FORCE_SEL(1) | PWM_CTRL2_RELOAD_SEL(1) | PWM_CTRL2_CLK_SEL(2) | PWM_CTRL2_INDEP(1);
/* PWM_SMaCTRL: LDFQ=0, HALF=0, FULL=1, DT=0, COMPMODE=0, PRSC=0, SPLIT=0, LDMOD=0, DBLX=0, DBLEN=0 */
PWM1->SM[0].CTRL = PWM_CTRL_FULL(1);
PWM1->SM[1].CTRL = PWM_CTRL_FULL(1);
PWM1->SM[2].CTRL = PWM_CTRL_FULL(1);
PWM1->SM[3].CTRL = PWM_CTRL_FULL(1);
PWM2->SM[0].INIT = 0x0000U;
PWM2->SM[0].VAL0 = 0x0800U;
PWM2->SM[0].VAL1 = 0x0FFFU;
PWM2->SM[0].VAL2 = 0x0000U;
PWM2->SM[0].VAL3 = 0x0000U;
PWM2->SM[0].VAL4 = 0x0000U;
PWM2->SM[0].VAL5 = 0x0000U;
PWM2->SM[1].INIT = 0x0000U;
PWM2->SM[1].VAL0 = 0x0800U;
PWM2->SM[1].VAL1 = 0x0FFFU;
PWM2->SM[1].VAL2 = 0x0000U;
PWM2->SM[1].VAL3 = 0x0000U;
PWM2->SM[1].VAL4 = 0x0000U;
PWM2->SM[1].VAL5 = 0x0000U;
PWM2->SM[2].INIT = 0x0000U;
PWM2->SM[2].VAL0 = 0x0800U;
PWM2->SM[2].VAL1 = 0x0FFFU;
PWM2->SM[2].VAL2 = 0x0000U;
PWM2->SM[2].VAL3 = 0x0000U;
PWM2->SM[2].VAL4 = 0x0000U;
PWM2->SM[2].VAL5 = 0x0000U;
PWM2->SM[3].INIT = 0x0000U;
PWM2->SM[3].VAL0 = 0x0800U;
PWM2->SM[3].VAL1 = 0x0FFFU;
PWM2->SM[3].VAL2 = 0x0000U;
PWM2->SM[3].VAL3 = 0x0000U;
PWM2->SM[3].VAL4 = 0x0000U;
PWM2->SM[3].VAL5 = 0x0000U;
/* PWM_MCTRL: IPOL = 0, RUN = 0,CLDOK = 0xF,LDOK = 0 */
PWM3->MCTRL = PWM_MCTRL_IPOL(0) | PWM_MCTRL_RUN(0) | PWM_MCTRL_CLDOK(0xFU) | PWM_MCTRL_LDOK(0);
/* PWM_FCTRL0: FLVL=0, FAUTO=0, FSAFE=0, FIE=0 */
PWM3->FCTRL = 0x00U;
/* SM0CTRL2: DBGEN=0, WAITEN=0, INDEP=1, PWM23_INIT=0, PWM45_INIT=0, PWMX_INIT=0, INIT_SEL=3, FRCEN=1, FORCE=0, FORCE_SEL=0, RELOAD_SEL=0, CLK_SEL=0 */
PWM3->SM[0].CTRL2 = PWM_CTRL2_INIT_SEL(3) | PWM_CTRL2_FRCEN(1) | PWM_CTRL2_INDEP(1);
/* SM1-3 CTRL2: DBGEN=0, WAITEN=0, INDEP=1, PWM23_INIT=0, PWM45_INIT=0, PWMX_INIT=0, INIT_SEL=2, FRCEN=1, FORCE=0, FORCE_SEL=1, RELOAD_SEL=1, CLK_SEL=2 */
PWM3->SM[1].CTRL2 = PWM_CTRL2_INIT_SEL(3) | PWM_CTRL2_FRCEN(1) | PWM_CTRL2_FORCE_SEL(1) | PWM_CTRL2_RELOAD_SEL(1) | PWM_CTRL2_CLK_SEL(2) | PWM_CTRL2_INDEP(1);
PWM3->SM[2].CTRL2 = PWM_CTRL2_INIT_SEL(3) | PWM_CTRL2_FRCEN(1) | PWM_CTRL2_FORCE_SEL(1) | PWM_CTRL2_RELOAD_SEL(1) | PWM_CTRL2_CLK_SEL(2) | PWM_CTRL2_INDEP(1);
PWM3->SM[3].CTRL2 = PWM_CTRL2_INIT_SEL(3) | PWM_CTRL2_FRCEN(1) | PWM_CTRL2_FORCE_SEL(1) | PWM_CTRL2_RELOAD_SEL(1) | PWM_CTRL2_CLK_SEL(2) | PWM_CTRL2_INDEP(1);
/* PWM_SMaCTRL: LDFQ=0, HALF=0, FULL=1, DT=0, COMPMODE=0, PRSC=0, SPLIT=0, LDMOD=0, DBLX=0, DBLEN=0 */
PWM3->SM[0].CTRL = PWM_CTRL_FULL(1);
PWM3->SM[1].CTRL = PWM_CTRL_FULL(1);
PWM3->SM[2].CTRL = PWM_CTRL_FULL(1);
PWM3->SM[3].CTRL = PWM_CTRL_FULL(1);
PWM3->SM[0].INIT = 0x0000U;
PWM3->SM[0].VAL0 = 0x0800U;
PWM3->SM[0].VAL1 = 0x0FFFU;
PWM3->SM[0].VAL2 = 0x0000U;
PWM3->SM[0].VAL3 = 0x0000U;
PWM3->SM[0].VAL4 = 0x0000U;
PWM3->SM[0].VAL5 = 0x0000U;
PWM3->SM[1].INIT = 0x0000U;
PWM3->SM[1].VAL0 = 0x0800U;
PWM3->SM[1].VAL1 = 0x0FFFU;
PWM3->SM[1].VAL2 = 0x0000U;
PWM3->SM[1].VAL3 = 0x0000U;
PWM3->SM[1].VAL4 = 0x0000U;
PWM3->SM[1].VAL5 = 0x0000U;
PWM3->SM[2].INIT = 0x0000U;
PWM3->SM[2].VAL0 = 0x0800U;
PWM3->SM[2].VAL1 = 0x0FFFU;
PWM3->SM[2].VAL2 = 0x0000U;
PWM3->SM[2].VAL3 = 0x0000U;
PWM3->SM[2].VAL4 = 0x0000U;
PWM3->SM[2].VAL5 = 0x0000U;
PWM3->SM[3].INIT = 0x0000U;
PWM3->SM[3].VAL0 = 0x0800U;
PWM3->SM[3].VAL1 = 0x0FFFU;
PWM3->SM[3].VAL2 = 0x0000U;
PWM3->SM[3].VAL3 = 0x0000U;
PWM3->SM[3].VAL4 = 0x0000U;
PWM3->SM[3].VAL5 = 0x0000U;
/* Fault Disable Mapping: Disable all fault inputs */
PWM1->SM[0].DISMAP[0] = 0xF000U;
PWM1->SM[0].DISMAP[1] = 0xF000U;
PWM1->SM[1].DISMAP[0] = 0xF000U;
PWM1->SM[1].DISMAP[1] = 0xF000U;
PWM1->SM[2].DISMAP[0] = 0xF000U;
PWM1->SM[2].DISMAP[1] = 0xF000U;
PWM1->SM[3].DISMAP[0] = 0xF000U;
PWM1->SM[3].DISMAP[1] = 0xF000U;
PWM2->SM[0].DISMAP[0] = 0xF000U;
PWM2->SM[0].DISMAP[1] = 0xF000U;
PWM2->SM[1].DISMAP[0] = 0xF000U;
PWM2->SM[1].DISMAP[1] = 0xF000U;
PWM2->SM[2].DISMAP[0] = 0xF000U;
PWM2->SM[2].DISMAP[1] = 0xF000U;
PWM2->SM[3].DISMAP[0] = 0xF000U;
PWM2->SM[3].DISMAP[1] = 0xF000U;
PWM3->SM[0].DISMAP[0] = 0xF000U;
PWM3->SM[0].DISMAP[1] = 0xF000U;
PWM3->SM[1].DISMAP[0] = 0xF000U;
PWM3->SM[1].DISMAP[1] = 0xF000U;
PWM3->SM[2].DISMAP[0] = 0xF000U;
PWM3->SM[2].DISMAP[1] = 0xF000U;
PWM3->SM[3].DISMAP[0] = 0xF000U;
PWM3->SM[3].DISMAP[1] = 0xF000U;
/* Enable outputs */
PWM1->OUTEN = 0x0FF0U;
PWM2->OUTEN = 0x0FF0U;
PWM3->OUTEN = 0x0FF0U;
/* MCTRL:initial load OK */
PWM1->MCTRL |= PWM_MCTRL_LDOK(0xFU);
PWM2->MCTRL |= PWM_MCTRL_LDOK(0xFU);
PWM3->MCTRL |= PWM_MCTRL_LDOK(0xFU);
PWM_OutputTriggerEnable(PWM1, kPWM_Module_0, kPWM_ValueRegister_1, true);
EnableIRQ(PWM1_0_IRQn);
NVIC_SetPriority(PWM1_0_IRQn, 2);
PWM_EnableInterrupts(PWM1, kPWM_Module_0, kPWM_ReloadInterruptEnable);
}
The PWM timers are started later on and interrupt arise at defined period. Here is the interrupt handler:
void PWM1_0_IRQHandler(void)
{
static uint16_t index = 0U;
PWM_Type *pstPwmBase;
pwm_submodule_t eSubModule;
uint8_t u8Index;
GPIO_PinWrite(BOARD_GPIO_TEST4_BASE, BOARD_GPIO_TEST4_PIN, true);
/* Clear interrupt flag */
PWM_ClearStatusFlags(PWM1, kPWM_Module_0, PWM_STS_RF_MASK);
PWM1->SM[0].VAL3 = g_kau16TestBuffer[index];
PWM1->SM[0].VAL5 = g_kau16TestBuffer[index];
PWM1->SM[1].VAL3 = g_kau16TestBuffer[index];
PWM1->SM[1].VAL5 = g_kau16TestBuffer[index];
PWM1->SM[2].VAL3 = g_kau16TestBuffer[index];
PWM1->SM[2].VAL5 = g_kau16TestBuffer[index];
PWM1->SM[3].VAL3 = g_kau16TestBuffer[index];
PWM1->SM[3].VAL5 = g_kau16TestBuffer[index];
PWM2->SM[0].VAL3 = g_kau16TestBuffer[index];
PWM2->SM[0].VAL5 = g_kau16TestBuffer[index];
PWM2->SM[1].VAL3 = g_kau16TestBuffer[index];
PWM2->SM[1].VAL5 = g_kau16TestBuffer[index];
PWM2->SM[2].VAL3 = g_kau16TestBuffer[index];
PWM2->SM[2].VAL5 = g_kau16TestBuffer[index];
PWM2->SM[3].VAL3 = g_kau16TestBuffer[index];
PWM2->SM[3].VAL5 = g_kau16TestBuffer[index];
PWM3->SM[0].VAL3 = g_kau16TestBuffer[index];
PWM3->SM[1].VAL3 = g_kau16TestBuffer[index];
PWM3->SM[2].VAL3 = g_kau16TestBuffer[index];
PWM3->SM[3].VAL3 = g_kau16TestBuffer[index];
index = (index + 1) % 1000;
/* Set the load okay bit for all submodules to load registers from their buffer */
PWM1->MCTRL |= PWM_MCTRL_LDOK(0xFU);
PWM2->MCTRL |= PWM_MCTRL_LDOK(0xFU);
PWM3->MCTRL |= PWM_MCTRL_LDOK(0xFU);
}
Could someone help me on making the synchronization work between FlexPWM modules please?
Regards,
Hugo
Hello again,
Some more inputs on my problem. For submodule 0 of all FlexPWM modules used, when I update the VAL3 registers, the reload error flag is raised (REF bit set in PWMx->SM[0].STS register). However, no reload error raised for the other submodules in the same FlexPWM module. Reference manual says
If STS[RUF] is set and MCTRL[LDOK] is clear when the reload signal occurs, a reload error has taken place and STS[REF] is set.
In my interrupt function, I set the VALx registers which sets RUF flag while LDOK bit is cleared. I'm guessing that reload signal is also set because this interruption is trigger upon reload flag being set. How to avoid this to make submodule 0 work? Why only submodule 0 raises this error, the RF flag is also set in all other submodules of the FlexPWM module?
Besides, I have only the correct output in the FlexPWM1 for SM0 (channel B only), SM1 (channels A and B), SM2 (channels A and B) and SM3 (channels A and B). So 7 channels out of 20. What is the correct register setup to synchronize all PWM outputs?
I've attached a test project to recreate the problem. The pin setup is for my board therefore you will most probably have to change the pinout for your board.
Regards,
Hugo