一、背景
项目中需要用STM32F407输出4路PWM波形控制两个A4950模块,从而驱动2个直流电机。使用TIM1的在PE9、PE11、PE13、PE14上分别产生4路PWM波形,前两路(记作pwm1、pwm2,分别接A4950的IN1和IN2)控制电机1,后两路(记作pwm1、pwm2,分别接另一个A4950的IN1和IN2)控制电机2。
根据A4950的芯片手册,电机正转时,IN1给占空比不为0的pwm波形,IN2给占空比为100%的信号;电机反转时,IN1给占空比为100%的信号,IN2给占空比不为0的pwm波形。于是,配置TIM1的4路输出采用PWM1模式,有效电平为高电平。代码如下:
void PWM_TIM_Config(uint32_t Cycle)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrcture;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(PWM_TIM_CLK,ENABLE); //使能TIM1时钟
RCC_AHB1PeriphClockCmd(PWM_OUT_PORT_CLK,ENABLE); //使能GPIOE时钟
//配置PE9
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType =GPIO_OType_PP;
GPIO_InitStructure.GPIO_Pin =PWM1_OUT_PIN ;
GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed;
GPIO_Init(PWM1_OUT_PORT,&GPIO_InitStructure);
//配置PE11
GPIO_InitStructure.GPIO_Pin =PWM2_OUT_PIN ;
GPIO_Init(PWM2_OUT_PORT,&GPIO_InitStructure);
//配置PE13
GPIO_InitStructure.GPIO_Pin =PWM3_OUT_PIN ;
GPIO_Init(PWM3_OUT_PORT,&GPIO_InitStructure);
//配置PE14
GPIO_InitStructure.GPIO_Pin =PWM4_OUT_PIN ;
GPIO_Init(PWM4_OUT_PORT,&GPIO_InitStructure);
//把PE9、PE11、PE13、PE14连接到TIM1
GPIO_PinAFConfig(PWM1_OUT_PORT,PWM1_PINSOURCE,PWM1_AF);
GPIO_PinAFConfig(PWM2_OUT_PORT,PWM2_PINSOURCE,PWM2_AF);
GPIO_PinAFConfig(PWM3_OUT_PORT,PWM3_PINSOURCE,PWM3_AF);
GPIO_PinAFConfig(PWM4_OUT_PORT,PWM4_PINSOURCE,PWM4_AF);
//TIM1时基配置,上数计数
TIM_TimeBaseStructInit (&TIM_TimeBaseInitStrcture);
TIM_TimeBaseInitStrcture.TIM_Period= Cycle-1;
TIM_TimeBaseInitStrcture.TIM_Prescaler =1;//2分频,84MHz
TIM_TimeBaseInitStrcture.TIM_CounterMode =TIM_CounterMode_Up ;
TIM_TimeBaseInitStrcture.TIM_ClockDivision=0;
TIM_TimeBaseInitStrcture.TIM_RepetitionCounter =0;
TIM_TimeBaseInit(PWM_TIM,&TIM_TimeBaseInitStrcture);
//输出比较(OC1-OC4)配置:PWM1模式,有效电平为高
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1 ;
TIM_OCInitStructure.TIM_OutputState =TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse =0;
TIM_OCInitStructure.TIM_OCPolarity =TIM_OCPolarity_High;
TIM_OC1Init(PWM_TIM,&TIM_OCInitStructure );
TIM_OC2Init(PWM_TIM,&TIM_OCInitStructure );
TIM_OC3Init(PWM_TIM,&TIM_OCInitStructure );
TIM_OC4Init(PWM_TIM,&TIM_OCInitStructure );
TIM_CtrlPWMOutputs(PWM_TIM ,ENABLE);//PWM输出使能
TIM_OC1PreloadConfig (PWM_TIM,TIM_OCPreload_Enable);
TIM_OC2PreloadConfig (PWM_TIM,TIM_OCPreload_Enable);
TIM_OC3PreloadConfig (PWM_TIM,TIM_OCPreload_Enable);
TIM_OC4PreloadConfig (PWM_TIM,TIM_OCPreload_Enable);
TIM_ARRPreloadConfig (PWM_TIM,ENABLE);
TIM_Cmd(PWM_TIM,ENABLE);
}
调试时发现,两个电机有一个无法控制方向,占空比和速度的关系也和预期的不一致,反复检查代码,百思不得其解。
二、问题分析
设置pwm1占空比为11.9%,pwm2占空比为0%,pwm3占空比为23.8%,pwm4占空比为0%,用示波器观察pwm1和pwm3的波形,发现波形如图1。这与设置的“PWM1模式,高电平为有效电平”应当产生的波形极性刚好相反。而且pwm2的波形为3.3v,pwm4的波形为0v,显然pwm2的输出极性也错了。

与输出比较器输出的极性相关的寄存器时CCER,从Keil MDK中查看TIM1的CCER寄存器的状态,发现如图2所示。可以发现CC4P的值为0,CC1P到CC3P的值均为1,这表明输出比较器4是高电平为有效电平,输出比较器1到3是低电平是有效电平,这就和观察到的PWM波形输出一致了。但是程序中确实是把OC1到OC4的有效电平都设置成了高电平啊,为什么会这样呢?

在程序中设置断点(图3)观察结构体TIM_OCInitStructure的值,发现结构体各项的值如图4所示。


可以发现结构体中的TIM_OCNpolarity的值为2(即有效值为低电平),再看stm32f4xx_tim.c中TIM_OC1Init函数的代码:


由于TIM_OCNPolarity为2,所以718行的语句运行后,CCER最后在第1bit位置被置1,即CC1P=1(事实上,把TIM_OCPolarity和TIM_OCNPolarity中的任何一个设置为了TIM_OCPolarity_Low,即值2,都会导致CC1P=1)。
显然,PWM波形错误是由于结构体TIM_OCInitStructure没有被正确的初始化引起的。那么为什么它没被正确的初始化呢?因为这里是在函数内部申明的结构体变量,因此是一个局部变量,局部变量分配内存时是不会被初始化为0的,因此其成员TIM_OCNPolarity这里就刚好不为0。
为了解决这个问题,在给TIM_OCInitStructure赋值之前,先调用初始化函数TIM_OCStructInit (&TIM_OCInitStructure),使其各成员变量初始化为默认值,然后再根据需要修改结构体成员的值。修改代码后,结构体TIM_OCInitStructure的值如图7所示,寄存器CCER的值在配置好后如图8所示,产生的波形如图9所示。



三、结论
对于STM32编程中常见的用于初始化的结构体,例如TIM_TimeBaseInitTypeDef、TIM_OCInitTypeDef,一定要先调用相应的初始化函数(尤其是这些结构体作为局部变量时),例如TIM_TimeBaseStructInit、TIM_OCStructInit。