汽车仪表步进电机的驱动

用PWM实现仪表步进电机的微步细分驱动

仪表步进电机应用较早的是瑞士SWITEC公司的XC5系列步进电机,随着国产同类产品的成熟,小型仪表步进电机应用更加广泛。目前,国内外常用的几种仪表步进电机电气参数、驱动原理基本相同:MCU发脉冲给驱动芯片,电机会根据脉冲数转动相应的步数。也有将步进电机驱动模块集成在MCU中的,如飞思卡尔的HY64系列。这种应用中,软件编程较为简单,但需要专用的硬件模块。在某些应用场景中,我们可能会因为PCB布线空间、成本等因素,需要尽量减少电机驱动的硬件、简化驱动软件。这里就以伟力VID2905仪表步进电机为例,分析在分步和微步细分两种模式下的驱动原理及用单片机PWM直接驱动电机的实现方法。

VID2905仪表步进电机原理简述
如下图,电机有两组定子绕组线圈,1,2脚为一组,3,4脚为一组。和大多数电机基本原理一样,给定子绕组加上特定的交变信号而产生旋转磁场,转子在磁场中受力转动。绕组驱动电流电最大20mA。内置180:1齿轮减速器,即转子转180度时,输出轴转动1度。


分步驱动原理及实现方法

上图是产品规格书中,分步驱动的图解说明。从图中可以看出,每个分步转子转动60度;一个全步是3个分步,转子转动180度;一个周期是6个分步,转子转动360度;经180:1减速,输出轴转动角度分别是1/3度、1度、2度。(至于为什么每分步是60度,不必去深究,这是由电机的结构决定的)根据上图的驱动脉冲时序,在每个分步给两组线圈加上相应极性的驱动电压,就可以实现电机的转动。改变脉冲序列的递增或递减顺序,就可以控制电机的转动方向。两组线圈共占用4个IO口。下表是4个IO的电平变化时序。H表示高电平,L表示低电平。

以下是分步驱动函数(相关的宏定义、初始化部分省略了),已在STM32F407上调试过,读者可以移植到自己的开发平台上直接使用。入口参数是转动方向和分步数,分步数乘以1/3就是电机轴的转动角度。实际应用中,只需要根据采集到的模拟量值,按量程关系换算为电机轴的角度,就可以现模拟量值的变化指示。分步模式驱动完全可以满足如简单的车载仪表等一些要求不高的应用。


/*---------------------------------------------------------------------
电机分步式运行
入口参数:
   M_Dir:转动方向,1--正转  0--反转
   M_Step:转动的分步数
----------------------------------------------------------------------*/

void Motor_RunStep(u8 M_Dir,u16 M_Step)
{
  static u8 M_Phase=1;
    u16 i;
    for(i=0;i<M_Step;i++)
    {
        if(M_Dir==0) //反转
            {
                M_Phase--;            
                if(M_Phase==0)
                M_Phase=6;
            }
        else
            {
                M_Phase++;            
                if(M_Phase==7)
                M_Phase=1;
            }  
                switch (M_Phase)    
                    {
                        case 1:
                            M_1=1;M_2=0;M_3=0;M_4=1;
                            break;
                        case 2:
                            M_1=0;M_2=0;M_3=0;M_4=1;
                            break;
                        case 3:
                            M_1=0;M_2=1;M_3=0;M_4=0;
                            break;
                        case 4:
                            M_1=0;M_2=1;M_3=1;M_4=0;
                            break;
                        case 5:
                            M_1=0;M_2=0;M_3=1;M_4=0;
                            break;
                        case 6:
                            M_1=1;M_2=0;M_3=0;M_4=0;
                            break;
                        default:
                            break;        
                    }
            delay_ms(1);
        }
}    
分步驱动的优点是驱动代码简单,缺点是转动抖动噪声大。要减小电机的抖动降低转动噪声,首先需要分析抖动及噪声的产生原理(如下图,时间有限,只能顺手画个草图):电机转子是由线圈产生的磁场变化带动旋转的。前面已经描述,每个分步转子转动60度,即每个分步下,由两组线圈所产生产磁场方向跳变角度是60度。(而且我们会注意到,在这种跳变会导致磁场的方向、大小都变化)假设我们需要转子在两组线圈磁场的合力下转动到目标位置A,给两组线圈按上表中的时序加上相应电压,磁场方向跳变到位置A,则转子随着磁场跳变开始转动,当转子转到A时,会因为机械惯性超过A而继续转动到B,此时转子转动方向和磁场力的方向相反了,那么在磁场力的作用下,它又反向朝着A转动;到A时,又会因机械惯性而继续转动到C,此时转动方向又与磁场力方向相反,则又会反向转动,…,直到在摩擦力在作用下最终停止在位置A。这种反复来回“荡秋千”,便形成了电机的抖动,并产生较大噪声。


微步细分驱动原理及实现方法
为了减小电机转动时产生的抖动和噪声,就得想办法使定子线圈的磁场跳变幅度变小,也就是把一个分步变成若干个更小的步,即微步来完成,这种方式就是微步细分驱动。


上图是电机厂家提供的微步细分时序图,每个分步分成了4个微步。我们不难看出,它非常近似于正弦波。可以设想,如果“拖动”转子转动的磁场,其方向、大小的变化是按照上图中的圆的方程变化的,而且恰好每周等分为24个位置,那么转子也就会按着圆的方程去转动。如果用X轴表示第一组线圈所产生的磁场,Y轴表示第二组线圈所产生的磁场,那么当X,Y轴分别按照正、余弦规律变化时,二者合成的磁场则符合圆的方程,即x2+y2=r^2,x=rcos(α),y=rsin(α)。这时,转子在磁场力F,也按圆的方程变化,就可实现转子的平稳转动。
因此,现在问题就转化为,单片机只能输出高低电平(逻辑0和1),如何在两组线圈中产生按正、余弦规律变化的磁场。要生产按正、余弦变化的磁场,就需要在两组线圈中通过按正、余弦变化的电流。这样就使问题进一步简化了,尽管单片机只有01两种逻辑,但是通常都会有带有PWM功能的定时器,而PWM占空比的改变是非常容易的。在两组线圈上加PWM信号,当我们不停地修改两组线圈PWM波的占空比,分别按正弦、余弦规律变化,由于线圈的电感作用,就会在线圈中产生正、弦变化的电流,这样就产生了正、余弦变化的磁场。如果需要非常精确的正弦、余弦电流,需要加检流电阻、ADC模块,引入反馈,形成对电流的精确调节。在普通仪表指示的实际应用中,完全可以按开环方式运行。
通过以上描述,我们可以总结出微步细分驱动的几个要点。1:使定时器的两个PWM通道,初始占空比设为0,同时配置两个通用IO口作为线圈的负端;2:转子转动一圈为1个周期,分为24个微步,那么就需要在正弦(余弦)信号的一个周期内选取24个点,作为占空比,可以提前算好,放在常变量中,直接查表使用。3:每走一个微步,修改一次占空比,在占空比的过0点,通过改变线圈负端的极性和PWM的输出比较极性改变线圈中的电流方向。下面的代码是在STM32F407上实现的微步驱动函数。代码已调试过,可以直接使用。

    
/*------------------------------------------------------------------------------
线圈1的CCR值,余弦
-------------------------------------------------------------------------------*/
static const u8 CosTable[] =
{
255, 246, 221, 180, 127, 65, 0, 65, 127, 180, 221, 246,
255, 246, 221, 180, 127, 65, 0, 65, 127, 180, 221, 246
};
/*------------------------------------------------------------------------------
线圈2的CCR值,正弦
-------------------------------------------------------------------------------*/
static const u8 SinTable[] =
{
127, 180, 221, 246, 255, 246, 221, 180, 127, 65, 0, 65,
127, 180, 221, 246, 255, 246, 221, 180, 127, 65, 0, 65
};

/*---------------------------------------------------------------------
电机驱动端口初始化 (微步模式)
----------------------------------------------------------------------*/
void MOTOR_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStru;
    
    TIM_TimeBaseInitTypeDef  TIM3_TimeBaseStru;
    TIM_OCInitTypeDef        TIM3_OCStru;     
    
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);  //使能时钟
    RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM3,ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);  //使能时钟
    
    GPIO_InitStru.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;        //PE0,PE1
    GPIO_InitStru.GPIO_Mode=GPIO_Mode_OUT;                //输出模式
    GPIO_InitStru.GPIO_OType=GPIO_OType_PP;               //推挽输出
    GPIO_InitStru.GPIO_PuPd=GPIO_PuPd_UP;                 //上拉
    GPIO_InitStru.GPIO_Speed=GPIO_Speed_50MHz;            //速度50MHz
    
    GPIO_Init(GPIOE,&GPIO_InitStru);
    GPIO_ResetBits(GPIOE,GPIO_Pin_0|GPIO_Pin_1);      //初始值
    
  GPIO_InitStru.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7; 
    GPIO_InitStru.GPIO_Mode=GPIO_Mode_AF;
    
    GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3);
    GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM3);
    GPIO_Init(GPIOC,&GPIO_InitStru);
    
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    GPIO_PinAFConfig(GPIOA,GPIO_Pin_14,GPIO_AF_SWJ);
    GPIO_PinAFConfig(GPIOA,GPIO_Pin_13,GPIO_AF_SWJ);

    
    TIM3_TimeBaseStru.TIM_ClockDivision=TIM_CKD_DIV1;
    TIM3_TimeBaseStru.TIM_CounterMode=TIM_CounterMode_Up;
    TIM3_TimeBaseStru.TIM_Period=255;
    TIM3_TimeBaseStru.TIM_Prescaler=167;
    TIM_TimeBaseInit(TIM3,&TIM3_TimeBaseStru);

    TIM3_OCStru.TIM_OCIdleState=TIM_OCIdleState_Reset;
    TIM3_OCStru.TIM_OutputState=TIM_OutputState_Enable;
    TIM3_OCStru.TIM_OCMode=TIM_OCMode_PWM1;
    TIM3_OCStru.TIM_Pulse=0;
    TIM_OC1Init(TIM3, &TIM3_OCStru);
    TIM_OC2Init(TIM3, &TIM3_OCStru);
     
    TIM_Cmd(TIM3, ENABLE);    
}

/*---------------------------------------------------------------------
电机微步式运行
入口参数:
   M_Dir:转动方向,1--正转  0--反转
   M_Step:转动的微步数,每1微步电机轴转动1/12度
----------------------------------------------------------------------*/
void MOTOR_RunMicroStep(u8 M_Dir,u16 M_Step)
{
    static u8 M_Phase;
    u16 i;
    for(i=0;i<M_Step;i++)
    {
         if(M_Dir==1)      //顺时针转
                {
                        if(M_Phase==23)//如果相位到最大值23了,则回0,开始下一周期
                            M_Phase=0;
                        else          //否则相位加1
                            M_Phase++;
                        switch(M_Phase)//根据相位值来决定电流方向的改变
                        {
                            case 6:
                                M1_2=1;
                                TIM_OC1PolarityConfig(TIM3,TIM_OCPolarity_Low);    
                                break;
                            case 10:
                                M1_3=1; 
                                TIM_OC2PolarityConfig(TIM3,TIM_OCPolarity_Low);    
                                break;
                            case 18:
                                M1_2=0; 
                                TIM_OC1PolarityConfig(TIM3,TIM_OCPolarity_High);
                                break;
                            case 22:
                                M1_3=0; 
                                TIM_OC2PolarityConfig(TIM3,TIM_OCPolarity_High);                    
                                break;
                            default:
                                break;
                        }
                }
                else if(M_Dir==0)            //如果M_Dir=0,反转。
                {
                    if(M_Phase==0)//如果相位到0,则回最大值23,开始下一周期
                            M_Phase=23;
                        else          
                            M_Phase--;//否则相位减1
                        switch(M_Phase)//根据相位值来决定电流方向的改变
                        {
                            case 17:
                                M1_2=1;  
                                TIM_OC1PolarityConfig(TIM3,TIM_OCPolarity_Low);                
                                break;
                            case 21:
                                M1_3=1; 
                                TIM_OC2PolarityConfig(TIM3,TIM_OCPolarity_Low);                    
                                break;
                            case 5:
                                M1_2=0; 
                                TIM_OC1PolarityConfig(TIM3,TIM_OCPolarity_High);    
                                break;
                            case 9:
                                M1_3=0;
                                TIM_OC2PolarityConfig(TIM3,TIM_OCPolarity_High);    
                                break;
                            default:
                                break;
                        }
                }
                TIM_SetCompare1(TIM3, CosTable[M_Phase]);
                TIM_SetCompare2(TIM3, SinTable[M_Phase]);
                delay_ms(1);
    } 
}
其它注意事项
1、任何驱动方式中,都要注意每两步之间的延时不能太短,否则会造成电机“失步”。这个时间需要根据电机规格书中的数据去推算。
2、PWM波的频率要合适,频率过高或过低都会使电流更不接近于正、余弦。
3、提前计算的正余弦常量表,最大值要和PWM中的ARR值(重装载值)一样,这样才能保证在一个周期中,最大占空比能达到1。否则可能因为电流不够造成电机不动。
4、如果需要在运行过程中进行加减速,则需要增加一个定时器,根据步距的长短,用定时器去及时修改步间延时。步间延时决定了电机的转动速度。

  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值