使用 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

★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★博文作者未开放评论功能