本帖最后由 Killoser 于 2022-12-16 17:35 编辑

理论

图片.png

图片.png

为了实现步进电机的平缓启停以及避免转高转速时不失步停转。在步进电机启动、停止过程中,需要采用加减速的算法对启动过程进行控制。S曲线是加减速控制最理想的方案。但是S曲线的公式以及控制过程都比较复杂。基于单片机实现这一算法需要有深厚的数学以及单片机软件设计能力。
本视频从浅入深介绍S曲线加减数的理论、编程与实践。
将S曲线离散化,在整个加减速过程中,以一定的时间间隔更新频率,总的更新次数为2*N,i表示为第i次的更新,则第i次更新的频率f(i)表示为:
图片.png

其中fb为开始的频率,fr是最终运行的频率,α是曲线的伸缩系数,一般可以取3-5之间的常数。
比如,启动加速,开始频率为400Hz,运行频率为5KHz。
停止减速,开始频率为5KHz,停止频率为400Hz,
加减速的时间均为1s,按10ms的时间间隔更新频率,总共更新100次,α取5。
则可以绘制以下的加减速曲线:
图片.png



编程
通过以下步骤实现实现步进电机的S曲线的加减速控制:

  • 配置1ms的定时器以及1ms的中断程序
  • 在中断程序中对加减速的频率更新次数i进行计数
  • 配置产生步进电机驱动信号的PWM模块,设置PWM的定时中断以及中断程序
  • 在PWM的定时中断程序中,计算当前更新次数对应的频率,并按出来的频率更新PWM的频率以及占空比
  • 在PWM的定时中断程序中,计算步进电机运行的步数,如果达到设置的步数减去停止的S曲线减速运行所设置的步数,则开始减速运行。
  • 同时检测外部输入,如果有需要停止运行的输入条件,则开始减速运行。
整个加减速控制过程的难点在于:

  • 步进电机的计步以及频率更新需要在每一个PWM中断中进行。
  • 步进电机的运行频率最高到40KHz,这种频率下,PWM的定时中断周期达到了25us,PWM定时器中断程序运行总时间尽量小,根据经验至少小于中断周期的30%,即7.5us。一旦超过这个数值,导致所有中断程序(包括PWM定时中断程序)漏运行,基本主程序无法运行,导致整个控制器假死现象。
  • 根据S曲线的公司是一个复杂的非线性的指数浮点数运算,需要耗费大量的时间,直接调用C语言的库函数计算这一数值可能耗费几十上百毫秒。
为了解决S曲线的运算时间问题,基于STM32F103,我采用了查表法,具体步骤如下:
1. 将α值定义5,


的取值范围为-5~5之间。
2. 在整个加减速过程中,表达式
图片.png

在取值范围-5~5内均匀取1024个数值,得到数值表。
3. 定义一个unsigned short型有1024个元素的const类型的数组,用于存储数值表。
4. Const数组存储在内部的flash,数值表共占用2048字节。
STM32F103RTC6共256K,程序组和设置参数占用48K,bootloader程序占用了8K,远程升级空间占用了100K,目前应用程序只用到40K左右。
剩余60K左右的空间,腾出2K的空间来存储数值表,有空间,就是这么任性。
5. 在中断程序中,根据总的更新次数以及当前的更新计数值,计算
图片.png

值,再映射到0-1023的数值有的索引值,通过索引获取数值。

6. 需要注意的是stm32f103不支持浮点数的运算,所以对于浮点数的运算,需要换算成乘以一个数再除以另一个数,比如*α,需要转变为*65535/13107。

下面一段代码是根据更新的计数值获取频率的函数:
U16 fnMC_GetFreq(U16 n, U16 halftn, U16 alpha, U16 minfreq, U16 maxfreq){//alpha=alpha * 4096
  • signed int udataA;
  • signed short uiDataA;
  • U16 uiRes;
  • U32 uwData;
  • udataA = (signed int)alpha * (signed int)n;
  • udataA = (signed int)udataA / halftn;
  • if(udataA > 32767){
  • udataA = 32767;
  • }
  • uiDataA = (signed short)alpha - (signed short)udataA;
  • uiDataA = (signed short)4 * 4096 - uiDataA;
  • if(uiDataA < 0){
  • uiDataA = 0;
  • }
  • uiRes = (U16)uiDataA;
  • uiRes = uiRes / 32;//*1023/8/4096
  • if(uiRes > 1023){
  • uiRes = 1023;
  • }
  • udataA = (signed int)(maxfreq - minfreq) * g_mc_uchExp[uiRes];
  • udataA = udataA / 65535;
  • uiDataA = (signed int)udataA;
  • uiDataA += minfreq;
  • if(uiDataA < 200){
  • uiDataA = 200;
  • }
  • return(uiDataA);
  • }
  • 复制代码
    下面一段代码是产生步进电机控制信号的PWM周期中断程序:
    int data;
  •          U16 freq;
  •          STRMotorRegs *motor;
  •          motor = &g_motor_regs[0];
  • MOTOR_A_CLEARINT();
  •          motor->steps ++;
  •          data = (int)MOTOR_A_STEPS_GET();
  •          if(motor->direction == 0)
  •          {
  •                  data = data + 1;
  •                 
  •          }
  •          else
  •          {
  •                  data = data - 1;
  •          }
  •          MOTOR_A_STEPS_SET(data);
  •          freq = motor->runfrequency;
  •          if(motor->runstate == MOTOR_RUN_STATE_INC){
  •                 if(motor->runtimer >= motor->starttime){
  •                         motor->runstate = MOTOR_RUN_STATE_IDLE;
  •                 }else{
  •                         freq = fnMC_GetFreq(motor->runtimer, motor->halfstarttime, motor->alpha, motor->startfreq, motor->runfrequency);//U16 n,U16 halftn,U16 alpha,U16 maxfreq,U16 minfreq)
  •                 }
  •                 motor->runsnapfreq = freq;
  •          }else if(motor->runstate == MOTOR_RUN_STATE_IDLE){
  •                 if(motor->totalstep <= (motor->steps + motor->stopremainstep)){
  •                         motor->runstate = MOTOR_RUN_STATE_DEC;
  •                         motor->runtimer = 0;
  •                        
  •                 }
  •                 motor->runsnapfreq = freq;
  •          }else{
  •                  if(motor->runtimer >= motor->stoptime){
  •                         freq = motor->stopfreq;
  •                 }else{
  •                         freq = fnMC_GetFreq(motor->runtimer, motor->halfstoptime, motor->alpha, motor->runsnapfreq, motor->stopfreq);//U16 n,U16 halftn,U16 alpha,U16 maxfreq,U16 minfreq)
  •                 }
  •          }
  •          
  •          if(motor->steps >= motor->totalstep)
  •          {
  •                   motor->starting = FALSE;
  •          }
  •          
  •          if(FALSE == motor->starting)
  •          {
  •                  MOTOR_A_DISABLE();
  •          }else{
  •                  motor->curfrequency = freq;
  •                  fnMT_Cal_MotorA_TimeConf();
  •          }
  • 复制代码
    来源:物联网全栈开发