使用 LPC5536 的 eFlexPWM 外設生成用於步進電機的 PWM 斬波

1. 用於步進電機的 PWM 斬波介紹

    兩相四線步進電機驅動中使用的 PWM 斬波主要有:

    1.ON(打開)+FastDecay(快衰減)

    2.ON(打開)+SlowDecay(慢衰減)

    3.ON(打開)+SlowDecay(慢衰減) +ON(打開)+SlowDecay(慢衰減)

    4.ON(打開)+SlowDecay(慢衰減) +FastDecay(快衰減) +SlowDecay(慢衰減)

    以上斬波模式在 H 橋中的上橋 PWM 信號和其生成的電流波形分別如下圖所示:









    從上圖可以看出採用 ON(打開)+SlowDecay(慢衰減) +ON(打開)+SlowDecay(慢衰減) 模式時電流紋波最小,步進電機的電流會有比較好的正弦度,可以使電機超靜音運行,對提高細分數有很大的比重。

2
. LPC5536 eFlexPWM 外設特性介紹和初始化代碼

    eFlexPWM 是一個很靈活的定時器外設,其包含 4 個子模塊,每個子模塊可以分別控制一個半橋,每個半橋的死區大小任意設置,可以實現中心對齊工作模式和邊緣對齊工作模式,上升沿和下降沿的位置可以任意設置,子模塊之間可以觸發同步等等,具體可以查看 RM 對應的章節,本文主要使用其可以在 counter 計數到模值和在半周期時刻時可以重載寄存器的功能實現 ON(打開)+SlowDecay(慢衰減) +ON(打開)+SlowDecay(慢衰減)  的功能,如下圖是 eFlexPWM 子模塊的框圖。



    比較寄存器 VAL2 定義每路 PWM 的上升沿,VAL3 定義 PWM 的下升沿,INIT 寄存器和 VAL1 寄存器定義 counter 計數的範圍,由 PWM 的頻率計算得出,VAL0 定義半周期時重載的時刻,另一個時刻是 counter 計數到 VAL1 時發生重載, ON(打開)+SlowDecay(慢衰減) +ON(打開)+SlowDecay(慢衰減) PWM 斬波生成過程為:

counter 計數到 VAL1 時發生中斷,計算下半周的 PWM 占空比,counter 計數到 VAL0 時再次發生中斷,計算上半周的 PWM 占空比,占空比根據實際需要來計算,各寄存器影響 PWM 波形如下圖所示:

    初始化代碼:

    其中主要是設置各個子模塊的 CTRL 寄存器的 11 位為 1,即使能 HalfReload。

/*

disable FRAC

PWM_A and PWM_B outputs are independent PWMs.
*/
void eFlexPWM0_Init(void)
{
PWM_Type *PWMBase = (PWM_Type *)PWM0;

/*eFlexPWM0 init*/
SYSCON->PWM0SUBCTL = (SYSCON_PWM0SUBCTL_CLK0_EN_MASK | SYSCON_PWM0SUBCTL_CLK1_EN_MASK | SYSCON_PWM0SUBCTL_CLK2_EN_MASK | SYSCON_PWM0SUBCTL_CLK3_EN_MASK); //Enable Sub-module0 clock
CLOCK_EnableClock(kCLOCK_Pwm0);

/* value register initial values, duty cycle 50% */
int32_t temp = (int32_t)((MCU_CLOCK_FREQ / M1_PWM_FREQ) / 2UL);
temp = -temp;
PWMBase->SM[0].INIT = (uint16_t)temp;
PWMBase->SM[1].INIT = (uint16_t)temp;
PWMBase->SM[2].INIT = (uint16_t)temp;
PWMBase->SM[3].INIT = (uint16_t)temp;

PWMBase->SM[0].VAL1 = (uint16_t)(((MCU_CLOCK_FREQ / M1_PWM_FREQ) / 2UL) - 1UL);
PWMBase->SM[1].VAL1 = (uint16_t)(((MCU_CLOCK_FREQ / M1_PWM_FREQ) / 2UL) - 1UL);
PWMBase->SM[2].VAL1 = (uint16_t)(((MCU_CLOCK_FREQ / M1_PWM_FREQ) / 2UL) - 1UL);
PWMBase->SM[3].VAL1 = (uint16_t)(((MCU_CLOCK_FREQ / M1_PWM_FREQ) / 2UL) - 1UL);

PWMBase->SM[0].VAL0 = 0UL;
PWMBase->SM[1].VAL0 = 0UL;
PWMBase->SM[2].VAL0 = 0UL;
PWMBase->SM[3].VAL0 = 0UL;

temp = (int32_t)((MCU_CLOCK_FREQ / M1_PWM_FREQ) / 4UL);
PWMBase->SM[0].VAL2 = (uint16_t)(-temp);
PWMBase->SM[1].VAL2 = (uint16_t)(-temp);
PWMBase->SM[2].VAL2 = (uint16_t)(-temp);
PWMBase->SM[3].VAL2 = (uint16_t)(-temp);

PWMBase->SM[0].VAL3 = (uint16_t)(temp);
PWMBase->SM[1].VAL3 = (uint16_t)(temp);
PWMBase->SM[2].VAL3 = (uint16_t)(temp);
PWMBase->SM[3].VAL3 = (uint16_t)(temp);

/*xx_xxx1b - PWM_OUT_TRIG0 will set when the counter value matches the VAL0 value */
/*xx_xx1xb - PWM_OUT_TRIG1 will set when the counter value matches the VAL1 value */
/*xx_x1xxb - PWM_OUT_TRIG0 will set when the counter value matches the VAL2 value */
/*xx_1xxxb - PWM_OUT_TRIG1 will set when the counter value matches the VAL3 value */
/*x1_xxxxb - PWM_OUT_TRIG0 will set when the counter value matches the VAL4 value */
//PWMBase->SM[0].VAL4 = 0UL;
/*1x_xxxxb - PWM_OUT_TRIG1 will set when the counter value matches the VAL5 value */
//PWMBase->SM[0].VAL5 = PWMBase->SM[0].INIT;
//PWMBase->SM[0].VAL0 PWM_OUT_TRIG0
//PWMBase->SM[0].VAL1 PWM_OUT_TRIG1
PWMBase->SM[0].TCTRL |= PWM_TCTRL_OUT_TRIG_EN(0b000011);//PWAOT0 PWBOT1 TRGFRQ OUT_TRIG_EN
PWMBase->SM[0].INTEN = (uint16_t)(0x1UL << 12UL);//RIE,Reload Interrupt Enable


/* set deadtime (number of Fast Peripheral Clocks)
DTCNT0,1 = T_dead * f_fpc = 1.0us * 150MHz = 150 */
#if 0
PWMBase->SM[0].DTCNT0 = (uint16_t)((M1_PWM_DEADTIME * (MCU_CLOCK_FREQ / 1000000UL)) / 1000UL);
PWMBase->SM[1].DTCNT0 = (uint16_t)((M1_PWM_DEADTIME * (MCU_CLOCK_FREQ / 1000000UL)) / 1000UL);
PWMBase->SM[2].DTCNT0 = (uint16_t)((M1_PWM_DEADTIME * (MCU_CLOCK_FREQ / 1000000UL)) / 1000UL);
PWMBase->SM[3].DTCNT0 = (uint16_t)((M1_PWM_DEADTIME * (MCU_CLOCK_FREQ / 1000000UL)) / 1000UL);
PWMBase->SM[0].DTCNT1 = (uint16_t)((M1_PWM_DEADTIME * (MCU_CLOCK_FREQ / 1000000UL)) / 1000UL);
PWMBase->SM[1].DTCNT1 = (uint16_t)((M1_PWM_DEADTIME * (MCU_CLOCK_FREQ / 1000000UL)) / 1000UL);
PWMBase->SM[2].DTCNT1 = (uint16_t)((M1_PWM_DEADTIME * (MCU_CLOCK_FREQ / 1000000UL)) / 1000UL);
PWMBase->SM[3].DTCNT1 = (uint16_t)((M1_PWM_DEADTIME * (MCU_CLOCK_FREQ / 1000000UL)) / 1000UL);
#endif
PWMBase->SM[0].DTCNT0 = 0UL;
PWMBase->SM[1].DTCNT0 = 0UL;
PWMBase->SM[2].DTCNT0 = 0UL;
PWMBase->SM[3].DTCNT0 = 0UL;
PWMBase->SM[0].DTCNT1 = 0UL;
PWMBase->SM[1].DTCNT1 = 0UL;
PWMBase->SM[2].DTCNT1 = 0UL;
PWMBase->SM[3].DTCNT1 = 0UL;

/* Control Register */
PWMBase->SM[0].CTRL = (uint16_t)(( 0x0UL << 12UL ) //LDFQ
|( 0x1UL << 11UL ) //HALF
|( 0x1UL << 10UL ) //FULL
|( 0x0UL << 7UL ) //COMPMODE
|( 0x0UL << 4UL ) //PRSC
|( 0x0UL << 3UL ) //SPLIT
|( 0x0UL << 2UL ) //LDMOD,
|( 0x0UL << 1UL ) //DBLX
|( 0x0UL << 0UL )); //DBLEN

/* Control Register */
PWMBase->SM[1].CTRL = (uint16_t)(( 0x0UL << 12UL ) //LDFQ
|( 0x1UL << 11UL ) //HALF
|( 0x1UL << 10UL ) //FULL
|( 0x0UL << 7UL ) //COMPMODE
|( 0x0UL << 4UL ) //PRSC
|( 0x0UL << 3UL ) //SPLIT
|( 0x0UL << 2UL ) //LDMOD,
|( 0x0UL << 1UL ) //DBLX
|( 0x0UL << 0UL )); //DBLEN

/* Control Register */
PWMBase->SM[2].CTRL = (uint16_t)(( 0x0UL << 12UL ) //LDFQ
|( 0x1UL << 11UL ) //HALF
|( 0x1UL << 10UL ) //FULL
|( 0x0UL << 7UL ) //COMPMODE
|( 0x0UL << 4UL ) //PRSC
|( 0x0UL << 3UL ) //SPLIT
|( 0x0UL << 2UL ) //LDMOD,
|( 0x0UL << 1UL ) //DBLX
|( 0x0UL << 0UL )); //DBLEN

/* Control Register */
PWMBase->SM[3].CTRL = (uint16_t)(( 0x0UL << 12UL ) //LDFQ
|( 0x1UL << 11UL ) //HALF
|( 0x1UL << 10UL ) //FULL
|( 0x0UL << 7UL ) //COMPMODE
|( 0x0UL << 4UL ) //PRSC
|( 0x0UL << 3UL ) //SPLIT
|( 0x0UL << 2UL ) //LDMOD,
|( 0x0UL << 1UL ) //DBLX
|( 0x0UL << 0UL )); //DBLEN


/* Fault0 trigger, Disable X,Disable B,Disable A */
#if 0
PWMBase->SM[0].DISMAP[0] = 0xF111U;
PWMBase->SM[1].DISMAP[0] = 0xF111U;
PWMBase->SM[2].DISMAP[0] = 0xF111U;
PWMBase->SM[3].DISMAP[0] = 0xF111U;
#endif
/* or */
PWMBase->SM[0].DISMAP[0] = 0;
PWMBase->SM[1].DISMAP[0] = 0;
PWMBase->SM[2].DISMAP[0] = 0;
PWMBase->SM[3].DISMAP[0] = 0;


/* PWMs are re-enabled at PWM full cycle / half cycle */
PWMBase->FSTS = (PWMBase->FSTS & (uint16_t)(~(PWM_FSTS_FFULL_MASK | PWM_FSTS_FHALF_MASK))) | PWM_FSTS_FFULL(0x1) | PWM_FSTS_FHALF(0x1);

/* PWM fault filter - 3 Fast periph. clocks sample rate, 5 agreeing samples to activate */
PWMBase->FFILT = (PWMBase->FFILT & (uint16_t)(~PWM_FFILT_FILT_PER_MASK)) | PWM_FFILT_FILT_PER(2);


/* All interrupts disabled, safe manual fault clearing, inversed logic (trigger level = high) */
#if 0
PWMBase->FCTRL &= ~(PWM_FCTRL_FLVL_MASK | PWM_FCTRL_FAUTO_MASK | PWM_FCTRL_FSAFE_MASK | PWM_FCTRL_FIE_MASK); /* clear FCTRL register prior further settings */
PWMBase->FCTRL |= PWM_FCTRL_FLVL(0x1UL);
PWMBase->FCTRL |= PWM_FCTRL_FAUTO(0x1UL);
PWMBase->FCTRL |= PWM_FCTRL_FSAFE(0x1UL);
PWMBase->FCTRL |= PWM_FCTRL_FIE(0UL); /* FAULT 0 & FAULT 1 - Interrupt disable */
#endif

/* Clear all fault flags */
PWMBase->FSTS = (PWMBase->FSTS & (uint16_t)(~PWM_FSTS_FFLAG_MASK)) | PWM_FSTS_FFLAG(0xFUL);

PWMBase->MASK = 0;//UPDATE_MASK MASKA MASKB MASKx
PWMBase->SWCOUT = 0;
PWMBase->DTSRCSEL = 0;//

PWMBase->SM[0].FRCTRL |= (uint16_t)((0UL << 4UL) | (0UL << 2UL) | (0UL << 1UL)) ;//FRAC45_EN 4,FRAC23_EN 2,FRAC1_EN1

PWMBase->SM[0].FRACVAL2 = (uint16_t)(0UL << 11UL);
PWMBase->SM[0].FRACVAL3 = (uint16_t)(0UL << 11UL);

PWMBase->SM[1].FRACVAL2 = (uint16_t)(0UL << 11UL);
PWMBase->SM[1].FRACVAL3 = (uint16_t)(0UL << 11UL);

PWMBase->SM[2].FRACVAL2 = (uint16_t)(0UL << 11UL);
PWMBase->SM[2].FRACVAL3 = (uint16_t)(0UL << 11UL);

PWMBase->SM[3].FRACVAL2 = (uint16_t)(0UL << 11UL);
PWMBase->SM[3].FRACVAL3 = (uint16_t)(0UL << 11UL);

PWMBase->SM[0].CTRL2 = (uint16_t)(( 0x0UL << 15UL ) //DBGEN
|( 0x0UL << 14UL ) //WAITEN,Sleep Enable
|( 0x1UL << 13UL ) //INDEP,0b - PWM_A and PWM_B form a complementary PWM pair.
|( 0x1UL << 12UL ) //PWM23_INIT,
|( 0x1UL << 11UL ) //PWM45_INIT
|( 0x0UL << 10UL ) //PWMX_INIT
|( 0x0UL << 8UL ) //INIT_SEL,counter load init value,00 Local sync,01 Master reload,10 Master sync,11 EXT_SYNC
|( 0x0UL << 7UL ) //FRCEN
|( 0x1UL << 6UL ) //FORCE
|( 0x4UL << 3UL ) /* FORCE_SEL
000 local force
001 master force
010 local reload
011 master reload
100 local sync
101 master sync
110 external force
111 external sync */
|( 0x0UL << 2UL ) //RELOAD_SEL,0b - The local RELOAD signal
|( 0x0UL << 0UL )); //CLK_SEL,
//00b - The IPBus clock
//01b - EXT_CLK
//10b - Submodule 0’s clock (AUX_CLK)

PWMBase->SM[1].CTRL2 = (uint16_t)(( 0x0UL << 15UL ) //DBGEN
|( 0x0UL << 14UL ) //WAITEN,Sleep Enable
|( 0x1UL << 13UL ) //INDEP,0b - PWM_A and PWM_B form a complementary PWM pair.
|( 0x1UL << 12UL ) //PWM23_INIT,
|( 0x1UL << 11UL ) //PWM45_INIT
|( 0x0UL << 10UL ) //PWMX_INIT
|( 0x2UL << 8UL ) //INIT_SEL,counter load init value,00 Local sync,01 Master reload,10 Master sync,11 EXT_SYNC
|( 0x0UL << 7UL ) //FRCEN
|( 0x1UL << 6UL ) //FORCE
|( 0x5UL << 3UL ) /* FORCE_SEL
000 local force
001 master force
010 local reload
011 master reload
100 local sync
101 master sync
110 external force
111 external sync */
|( 0x1UL << 2UL ) //RELOAD_SEL,1b - The master RELOAD signal
|( 0x0UL << 0UL )); //CLK_SEL,
//00b - The IPBus clock
//01b - EXT_CLK
//10b - Submodule 0’s clock (AUX_CLK)

PWMBase->SM[2].CTRL2 = (uint16_t)(( 0x0UL << 15UL ) //DBGEN
|( 0x0UL << 14UL ) //WAITEN,Sleep Enable
|( 0x1UL << 13UL ) //INDEP,0b - PWM_A and PWM_B form a complementary PWM pair.
|( 0x1UL << 12UL ) //PWM23_INIT,
|( 0x1UL << 11UL ) //PWM45_INIT
|( 0x0UL << 10UL ) //PWMX_INIT
|( 0x2UL << 8UL ) //INIT_SEL,counter load init value,00 Local sync,01 Master reload,10 Master sync,11 EXT_SYNC
|( 0x0UL << 7UL ) //FRCEN
|( 0x1UL << 6UL ) //FORCE
|( 0x5UL << 3UL ) /* FORCE_SEL
000 local force
001 master force
010 local reload
011 master reload
100 local sync
101 master sync
110 external force
111 external sync */
|( 0x1UL << 2UL ) //RELOAD_SEL,1b - The master RELOAD signal
|( 0x0UL << 0UL )); //CLK_SEL,
//00b - The IPBus clock
//01b - EXT_CLK
//10b - Submodule 0’s clock (AUX_CLK)

PWMBase->SM[3].CTRL2 = (uint16_t)(( 0x0UL << 15UL ) //DBGEN
|( 0x0UL << 14UL ) //WAITEN,Sleep Enable
|( 0x1UL << 13UL ) //INDEP,0b - PWM_A and PWM_B form a complementary PWM pair.
|( 0x1UL << 12UL ) //PWM23_INIT,
|( 0x1UL << 11UL ) //PWM45_INIT
|( 0x0UL << 10UL ) //PWMX_INIT
|( 0x2UL << 8UL ) //INIT_SEL,counter load init value,00 Local sync,01 Master reload,10 Master sync,11 EXT_SYNC
|( 0x0UL << 7UL ) //FRCEN
|( 0x1UL << 6UL ) //FORCE
|( 0x5UL << 3UL ) /* FORCE_SEL
000 local force
001 master force
010 local reload
011 master reload
100 local sync
101 master sync
110 external force
111 external sync */
|( 0x1UL << 2UL ) //RELOAD_SEL,1b - The master RELOAD signal
|( 0x0UL << 0UL )); //CLK_SEL,
//00b - The IPBus clock
//01b - EXT_CLK
//10b - Submodule 0’s clock (AUX_CLK)

/* Start PWMs (set load OK flags and run - we need to trigger the ADC) */
PWMBase->MCTRL = (PWMBase->MCTRL & (uint16_t)(~PWM_MCTRL_CLDOK_MASK)) | PWM_MCTRL_CLDOK(0xF);
PWMBase->MCTRL = (PWMBase->MCTRL & (uint16_t)(~PWM_MCTRL_LDOK_MASK)) | PWM_MCTRL_LDOK(0xF);
PWMBase->MCTRL = (PWMBase->MCTRL & (uint16_t)(~PWM_MCTRL_RUN_MASK)) | PWM_MCTRL_RUN(0xF);

//PWMBase->OUTEN = 0xFF0;//0xFF0,11-8,PWMA3_EN ~ PWMA0_EN, 7-4 PWMB3_EN ~ PWMB0_EN, 3-0 PWMX3_EN ~ PWMX0_EN

/* Enable & setup interrupt from PWMA */
// NVIC_SetPriority(FLEXPWM0_RELOAD0_IRQn, 1UL);
// NVIC_EnableIRQ(FLEXPWM0_RELOAD0_IRQn);
}


3
實際效果
    以上是使用 eflexPWM 生成需要的 PWM 的波形的過程,在完整的步進電機控制中這種 PWM 斬波模式可以實現比較好的電流波形,紋波比較小,如下圖所示。
    藍色為 PWM 信號,黃色為電流信號。


4
. 參考文檔
    LPC5536 參考手冊:    

    https://www.nxp.com.cn/docs/en/reference-manual/LPC553xRM.pdf

★博文內容均由個人提供,與平台無關,如有違法或侵權,請與網站管理員聯繫。

★博文作者未開放評論功能