电子DIY小家 发表于 2024-5-9 10:15:57

步进电机的相对位置&绝对位置控制, STC32G PWM加减速脉冲输出控制

步进电机的相对位置&绝对位置控制, STC32G PWM加减速脉冲输出控制

序言
    很多人不理解,为什么这个步进电机的启停需要加减速控制。步进电机大家应该都熟,给一个脉冲,步进电机就旋转一个步距角。但实际上,如果启动时脉冲信号变化太快,步进电机由于内部的反向电动势的阻尼作用,转子与定子之间的磁反应将跟随不上电信号的变化,将导致堵转和丢步;如果要立即停止,由于所带负载具有惯性,可能会导致过冲。因此步进电机需要做加减速控制。通常,完成步进电机的加减速时间为300ms以上。

一、实现过程
    整个PWM加减速脉冲输出的过程可以分为三个部分:

    1.加减速参数计算(常见的有三角形或梯形加减速,S型曲线加减速等)
    2.脉冲启动和输出(跟新中断内加载新的PWM频率)
    3.脉冲停止和急停(如果正常停止速度会逐渐降低并停止,如果有急停信号直接停止输出)

二、分析
    1.加减速过程分析:首先上面提到加减速常见的有很多,这里主要来分析三角形或梯形加减速,为什么会把三角形和梯形加减速放在一起呢,我们来思考如下一个例子:假如我程序设定的电机最高转速180转,步距角为1.8度不细分(转一圈需要200个脉冲),也就是说脉冲的速度需要达到180*200=36 KHZ才能到电机的最高转速,要转很多圈的情况下,我们就是先加速到这个36khz,在匀速运行,在减速,整体的过程如下:



然而某一步动作电机只需要转四分之一圈,这个时候电机还没加速到最大值就已经到位置了,所以最终的过程就会如下图红线部分


理论来说,上面一张图就可以很清晰的看出脉冲速度在每一时刻的一个过程,最完美的过程就是每一个脉冲发完都要更新一次速度,在加速的过程中,每个脉冲都会匀速的增加,但是这其中就会存在一些问题:如在高速脉冲信号中每次更新中断内都要花大量时间去计算,或者脉冲速度存在余数除不尽等等,当然要说解决其实也是能解决的,就是处理比较麻烦。这里我们就可以选择一个折中的办法,这个方法就可以轻松的适用于所有的单片机,直接在启动脉冲之前生成一个数组表格,在发脉冲过程中一直查表即可,不需要试试计算,当然我们也不需要每一个脉冲都变一下频率了,不然这个表会非常 的庞大。假设频率100-1000的加速过程,我们就可以给他分为100-200-300-400-...-900-1000的过程,当然也可以给他分的更细,比如50一档等等。
    另外这里还有个需要注意的是步进的启动往往不是从0开始的,一般都会有个初始的启动频率,在这个基础上慢慢的叠加速度。否则上来就是1hz的速度的话,别人电机都起飞了,你这电机还在学蜗牛爬{:5_284:}
    另外,还有一个相对定位和绝对定位的概念。所谓的相对定位就是相比于上一次的位置,假设上一次位置是5000,这一次要走到6000,这里就要发送+1000的脉冲。设上一次位置是5000,这一次要走到46000,这里就要发-1000的脉冲,即这个相对定位的函数的入口参数为+1000或者-1000;绝对定位的话每个坐标都是一个绝对位置,想去3000的位置入口参数就写3000,想去5000的位置入口参数就写5000;这里的位置在文中均指脉冲数。

    2.脉冲启动和输出分析:这里为了方便脉冲输出直接用的硬件PWM,配合更新中断,直接在中断内修改PWM的ARR和PSC,因为ARR只有16位,所以这里把PSC也一起带上了。
    3.脉冲停止和急停:常规的停止和脉冲加速一样频率慢慢的分几级变化然后慢慢变为0,但是在实际过程中可能出现了问题需要急停,这时候需要直接停止,哪怕会损伤机器,但是在人身安全面前,机器都不重要了。所以可以直接在中断了强行关闭脉冲!
    最后在插入一个正反转的问题,能发脉冲了就能动,但是怎么让他正转和反转呢,


    可以看到市面上常见的驱动模块基本都有一个方向脚专门去控制方向,在有脉冲的情况下,这个脚是高电平往一个方向转,是低电平往另一个方向转!

三、代码实现
    1.相对定位的脉冲表计算如下
void DRVI(long offset,u32 frequency)
{
      u16 h=0;
      u16 i=0;
      u32 j=0;
      
      u32 mul = 1;
      u32 fre_s=0;
      
      if((offset!=0)&&(RunFlag==OFF))//相对偏移值为0则不接受命令,脉冲输出已执行,不接受命令
      {      
                Target=Current+offset;   //目标值等于当前值加上相对偏移值
               
//-----------------------------------------------正反转的方向计算-----------------------------------------------                        
                if(frequency<StartFreq)    //如果设定目标频率小于启动频率
                {
                        frequency=StartFreq;
                }
                else if(frequency>MasterFrequency)//否则如果设定目标频率高于最高限制频率
                {
                        frequency=MasterFrequency;
                }

//-----------------------------------------------各个阶段的频率点和pwm装载值计算-----------------------------------------------      
                LadderNum=UDTimer/10;                              //加减速级数
                j=(frequency-StartFreq)/LadderNum;      //等差
                for(i=0;i<LadderNum;i++)
                {
                        mul = 1;
                        LadderFreq=i*j+StartFreq;//加减速各阶梯频率
                        
                        while( (MAIN_Fosc/LadderFreq/mul)>65535 )
                        {
                              mul++;
                        }
                        fre_s = (MAIN_Fosc/LadderFreq/mul);
                        LadderPSC = mul-1;
                        LadderARR = fre_s-1;
                        
//                        LadderPSC=(6000000/LadderFreq)-1;//加减速各阶梯频率对应定时器预分频值
                }
                LadderFreq=frequency;//目标频率,最高频率
//                LadderPSC=6000000/frequency-1;//目标频率(最高频率)对应定时器预分频值
                mul = 1;
                while( (MAIN_Fosc/frequency/mul)>65535 )
                {
                        mul++;
                }
                fre_s = (MAIN_Fosc/frequency/mul);
                LadderPSC = mul-1;
                LadderARR = fre_s-1;
               
                              
                if(offset>0)//相对偏移值为正数
                {
                        DIR1 = 0;//相对偏移值为正数,方向为正,方向信号高电平
                        PlusMinus=ON;//正负方向标志置ON
                        
                        LadderTarget=Current+StartFreq/100;//加速第一段目标脉冲值
                        for(i=1;i<LadderNum;i++)
                        {
                              LadderTarget=LadderTarget+LadderFreq/100;//加速各段目标脉冲值
                        }      
                        
                        while(offset<=((LadderTarget-Current)<<1))//如果偏移量小于二倍加速增量
                        {
                              LadderNum--;//加速等级数减一频率设定过高、实际输出脉冲数过少的情况下不必加速至设定频率,避免过冲
                        }
                        
                        for(i=0,h=LadderNum<<1;i<LadderNum;i++,h--)
                        {
                              LadderPSC=LadderPSC;//减速各段定时器重装值
                              LadderARR=LadderARR;//减速各段定时器重装值
                        }
                        
                        LadderTarget=Target;//减速最后一段目标脉冲值
                        for(i=(LadderNum<<1)-1,h=0;i>LadderNum;i--,h++)
                        {
                              LadderTarget=LadderTarget-LadderFreq/100;//减速各段目标脉冲值
                        }
                }
                else//否则相对偏移值为负数                                             
                {
                        DIR1 = 1;//相对偏移值为负数,方向为负,方向信号低电平
                        PlusMinus=OFF;//正负方向标志OFF
                        
                        LadderTarget=Current-StartFreq/100;//加速第一段目标脉冲值
                        for(i=1;i<LadderNum;i++)
                        {
                              LadderTarget=LadderTarget-LadderFreq/100;//加速各段目标脉冲值
                        }
                        
                        while(offset>=((LadderTarget-Current)<<1))//如果偏移量小于二倍加速增量
                        {
                              LadderNum--;//加速等级数减一频率设定过高、实际输出脉冲数过少的情况下不必加速至设定频率,避免过冲
                        }
                        
                        for(i=0,h=LadderNum<<1;i<LadderNum;i++,h--)
                        {
                              LadderPSC=LadderPSC;
                              LadderARR=LadderARR;
                        }
                        
                        LadderTarget=Target;//减速最后一段目标脉冲值
                        for(i=(LadderNum<<1)-1,h=0;i>LadderNum;i--,h++)
                        {
                              LadderTarget=LadderTarget+LadderFreq/100;//减速各段目标脉冲值
                        }
                }
                LadderTarget=Target + Current - LadderTarget;
                Pluse_start();//脉冲输出正式启动
      }
}    绝对位置的脉冲表计算如下:
void DRVA(long target,u32 frequency)
{
      u16 h;
      u16 i;
      u32 j;

      u32 mul = 1;
      u32 fre_s=0;
      
      long offset=target-Current;
      
      if((offset!=0)&&(RunFlag==OFF))    //目标位置等于当前位置,则不接受命令
      {                     
                Target=target;               //目标位置设定(等于参数)
               
//-----------------------------------------------正反转的方向计算-----------------------------------------------               
                if(frequency<StartFreq)      //如果设定目标频率小于启动频率
                {
                        frequency=StartFreq;
                }
                else if(frequency>MasterFrequency)//否则如果设定目标频率高于最高限制频率
                {
                        frequency=MasterFrequency;
                }
               
//-----------------------------------------------各个阶段的频率点和pwm装载值计算-----------------------------------------------                        
                LadderNum=UDTimer/10;//加减速级数
                j=(frequency-StartFreq)/LadderNum;//等差
                for(i=0;i<LadderNum;i++)
                {
                        mul = 1;
                        LadderFreq=i*j+StartFreq;//加减速各阶梯频率
                        
                        while( (MAIN_Fosc/LadderFreq/mul)>65535 )
                        {
                              mul++;
                        }
                        fre_s = (MAIN_Fosc/LadderFreq/mul);
                        LadderPSC = mul-1;
                        LadderARR = fre_s-1;
                }
                LadderFreq=frequency;//目标频率,最高频率
//                LadderPSC=6000000/frequency-1;//目标频率(最高频率)对应定时器预分频值
                mul = 1;
                while( (MAIN_Fosc/frequency/mul)>65535 )
                {
                        mul++;
                }
                fre_s = (MAIN_Fosc/frequency/mul);
                LadderPSC = mul-1;
                LadderARR = fre_s-1;
               
                if(offset>0)//目标位置值大于当前位置值
                {
                        DIR1 = 0;//相对偏移值为正数,方向为正,方向信号高电平
                        PlusMinus=ON;//正负方向标志置ON
                        
                        LadderTarget=Current+StartFreq/100;
                        for(i=1;i<LadderNum;i++)
                        {
                              LadderTarget=LadderTarget+LadderFreq/100;
                        }      
                        
                        while(offset<=((LadderTarget-Current)<<1))//如果偏移量小于二倍加速增量
                        {
                              LadderNum--;//加速等级数减一频率设定过高、实际输出脉冲数过少的情况下不必加速至设定频率,避免过冲
                        }
                        
                        for(i=0,h=LadderNum<<1;i<LadderNum;i++,h--)
                        {
                              LadderPSC=LadderPSC;
                              LadderARR=LadderARR;//减速各段定时器重装值
                        }
                        
                        LadderTarget=Target;//减速最后一段目标脉冲值
                        for(i=(LadderNum<<1)-1,h=0;i>LadderNum;i--,h++)
                        {
                              LadderTarget=LadderTarget-LadderFreq/100;//减速各段目标脉冲值
                        }
               LadderTarget=Target + Current - LadderTarget;//匀速段目标位置/进入减速时位置      
                Pluse_start();//脉冲输出正式启动            
                }
                else if(offset<0)//否则目标位置值小于当前位置值,                                                         
                {
                        DIR1 = 1;//相对偏移值为负数,方向为负,方向信号低电平
                        PlusMinus=OFF;//正负方向标志OFF
                        
                        LadderTarget=Current-StartFreq/100;
                        for(i=1;i<LadderNum;i++)
                        {
                              LadderTarget=LadderTarget-LadderFreq/100;
                        }
                        
                        while(offset>=((LadderTarget-Current)<<1))//如果偏移量小于二倍加速增量
                        {
                              LadderNum--;//加速等级数减一频率设定过高、实际输出脉冲数过少的情况下不必加速至设定频率,避免过冲
                        }
                        
                        for(i=0,h=LadderNum<<1;i<LadderNum;i++,h--)
                              {
                              LadderPSC=LadderPSC;
                                        LadderARR=LadderARR;
                        }
                        
                        LadderTarget=Target;//减速最后一段目标脉冲值
                        for(i=(LadderNum<<1)-1,h=0;          i>LadderNum;   i--,h++)
                        {
                              LadderTarget=LadderTarget+LadderFreq/100;//减速各段目标脉冲值
                        }
                LadderTarget=Target + Current - LadderTarget;//匀速段目标位置/进入减速时位置      
                Pluse_start();//脉冲输出正式启动            
                }
      else
            RunFlag=OFF;

      }
}这两段代码本质都差不多,唯一的区别就是关于偏移offset的计算,一个是直接得到的,一个是要根据上一次的位置换算的。所以在程序里必须要有当前位置脉冲数Current和目标位置脉冲数Target的变量。

    2.PWM初始化和输出的代码:
void PWMB_Init(void)
{
    PWMB_PSCRH = (u8)(1 >> 8);                        //配置预分频系数
    PWMB_PSCRL = (u8)(1 );               
                  
    PWMB_CCER1 = 0x00;                                    //写 CCMRx 前必须先清零 CCxE 关闭通道
    PWMB_CCER2 = 0x00;                           
    PWMB_CCMR1 = 0x68;                                    //通道模式配置 pwm模式1
      PWMB_CCER1 = 0x01;                                    //配置通道1输出使能和极性
               
    PWMB_ARRH = (u8)((2000-1) >> 8);                      //设置周期时间
    PWMB_ARRL = (u8)(2000-1);               
               
    PWMB_ENO = 0x00;               
    PWMB_ENO = ENO5P;                                    //使能输出
               
    PWMB_PS = 0x00;                                       //高级 PWM 通道输出脚选择位
    PWMB_PS |= PWM5_0;                                    //选择 PWM1_0 通道
      
    PWMB_IER |= (1<<2);                                     //使能更新中断
                  
    PWMB_BKR = 0x80;                                        //使能主输出

    PWMB_CCR5H = 0;                      //设置周期时间
    PWMB_CCR5L = 0;      
      
      PWMB_SR1 = 0;
      PWMB_CR1 = 0x80;
    PWMB_CR1 = 0x00;
    PWMB_ENO = 0x00;
    PWM1 = 1;
    DIR1 = 1;
      
    Variable_Init();
}
void PWMB_ISR() interrupt 27
{
      long temp;      

      if(PWMB_SR1 & 0x04)
      {
                PWMB_SR1 &= (~0x04);
               
//                if( PWM1==0 )
                {
                        if(PlusMinus==ON)
                        {
                              temp=Current;
                              temp++;       //如果方向为正,当前值加一
                              Current=temp;
                //可以增加正转限位开关保护
                        }
                        else   
                        {
                              temp=Current;
                              temp--;       //否则方向为负,当前值减一
                              Current=temp;
                //可以增加反转限位开关保护               
                        }

                        if(Current==LadderTarget)
                        {
                              if(LadderOrderNum< (LadderNum<<1))
                              {
                                        LadderOrderNum++;
                //                        TIM3_PSC=LadderPSC;
                                        PWMB_PSCRH = (u8)((LadderPSC) >> 8);                        //配置预分频系数
                                        PWMB_PSCRL = (u8)((LadderPSC) );   
                                        PWMB_ARRH = (u8)(LadderARR >> 8);                        //配置预分频系数
                                        PWMB_ARRL = (u8)(LadderARR );   
                                        PWMB_CCR5H = (u8)((LadderARR/2) >> 8);                        //配置预分频系数
                                        PWMB_CCR5L = (u8)((LadderARR/2) );                           
                              }
                              else
                              {
                                        PWMB_CR1 = 0x00;         
                                        PWMB_ENO = 0x00;
                  PWM1 = 1;
                  DIR1 = 1;                  
                                        RunFlag=OFF;   
                              }
                        }      
                }               
      }
      if(RunFlag==OFF)
    {
                PWMB_CR1 = 0x00;
      PWMB_ENO = 0x00;
      PWM1 = 1;
      DIR1 = 1;
      RunFlag=OFF;
    }      
    Y01 = !Y01;
   PWMB_SR1 = 0;
}需要注意的是在中断里有个RunFlag变量,如果说需要急停直接在需要触发保护的地方将这个标志位置为OFF即可。
另外就是PWM脉冲启动
void Pluse_start()
{
      StartSave=Current;
      LadderOrderNum=0;//加减速级数序号为0      
      
      PWMB_PSCRH = (u8)((LadderPSC) >> 8);                        //配置预分频系数
      PWMB_PSCRL = (u8)((LadderPSC) );   
      PWMB_ARRH = (u8)(LadderARR >> 8);                        //配置预分频系数
      PWMB_ARRL = (u8)(LadderARR );   
      PWMB_CCR5H = (u8)((LadderARR/2) >> 8);                        //配置预分频系数
      PWMB_CCR5L = (u8)((LadderARR/2) );            
      
      delay_ms(2);

      PWMB_ENO = ENO5P;                                  //启动计数器
      
      PWMB_CR1 = 0x80;
      PWMB_CR1 |= 0x01;
      PWMB_SR1 = 0;
    RunFlag=ON;                           //脉冲输出定位指令执行标志置ON
}
基于上述的函数,我们就可以在主函数里实现任意的相对位置和绝对位置控制了
      if( mode==0 )
      {
            STOP();
      }
      else if( mode==1 )
      {
            DRVI(50,40000);
            PAUSE();
            mode =0;
      }
      else if( mode==2 )
      {
            DRVI(-50,40000);
            PAUSE();
            mode =0;         
      }      
      else if( mode==3 )
      {
            DRVA(0,40000);
            PAUSE();
            mode =0;            
      }
      else if( mode==4 )
      {
            DRVA(200,40000);
            PAUSE();
            mode =0;            
      }
      
      if( flag_1ms==1 )
      {
            if( X00==IO_ON )
            {
                key_val++;
                if( key_val==10 )
                  mode =1;
            }
            else
                key_val = 0;
            if( X01==IO_ON )
            {
                key_val++;
                if( key_val==10 )
                  mode =2;
            }
            else
                key_val = 0;
            if( X02==IO_ON )
            {
                key_val++;
                if( key_val==10 )
                  mode =3;
            }
            else
                key_val = 0;
            if( X03==IO_ON )
            {
                key_val++;
                if( key_val==10 )
                  mode =4;
            }
            else
                key_val = 0;            

            flag_1ms=0;
      }
在while函数里编写如下功能,可以去试试都是些什么功能!有问题欢迎下面留言或者群里发言。



jwd 发表于 2024-5-9 10:21:23

干货,{:4_250:}{:4_250:},谢谢冲哥分享!

21cnxin 发表于 2024-5-9 13:55:24

{:4_250:},谢谢分享

国学芯用 发表于 2024-5-9 16:47:59

{:5_332:}

jinhangshebei 发表于 2024-5-10 06:34:46

很好

ofela 发表于 2024-5-10 09:56:26

再加上串口控制就更完美了

西红柿牛柳 发表于 2024-5-10 09:59:23

感谢冲哥分享!{:4_250:}

ageway 发表于 2024-5-10 10:13:19

{:4_250:}{:4_250:}{:4_250:}

lijun4545 发表于 2024-5-10 12:23:03

只知其然,而不知其所以然

电子DIY小家 发表于 2024-5-10 14:35:00

lijun4545 发表于 2024-5-10 12:23
只知其然,而不知其所以然
所以老话说的好:师傅领进门,修行靠个人
页: [1] 2 3 4
查看完整版本: 步进电机的相对位置&绝对位置控制, STC32G PWM加减速脉冲输出控制