打印
[STM32F1]

STM32F1定时器输出比较反转模式编程控制步进电机梯形加减速

[复制链接]
553|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
beijinglaobai|  楼主 | 2022-8-19 14:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
利用STM32定时器输出比较反转模式编程控制输出的脉冲实现步进电机梯形加减速控制
首先定时定时器控制引脚及运行机构参数
/*********************** 步进电机控制定时器TIM参数定义 ************************/

#define SMOTOR1_TIMx                          TIM3
#define SMOTOR1_TIM_RCC_CLK_ENABLE()          __HAL_RCC_TIM3_CLK_ENABLE()
#define SMOTOR1_TIM_RCC_CLK_DISABLE()         __HAL_RCC_TIM3_CLK_DISABLE()
#define SMOTOR1_TIM_IRQn                      TIM3_IRQn
#define SMOTOR1_TIM_IRQHandler                TIM3_IRQHandler

#define TIMx_CLOCK_FREQ                       72e6  //72000000  //72M定时器时钟源的最高频率

// 定义定时器预分频,定时器实际时钟频率为:72MHz/(STEPMOTOR_TIMx_PRESCALER+1)
#define SMOTOR1_TIM_PRESCALER               3  // 步进电机驱动器细分设置为:   32  细分

// 定义定时器周期,当定时器开始计数到SMOTOR1_TIM_PERIOD值是更新定时器并生成对应事
// 件和中断.定时器更新频率为:定时器频率/(SMOTOR1_TIM_PERIOD+1) Hz
#define SMOTOR1_TIM_PERIOD                    0xFFFF  
#define SMOTOR1_TIM_DEFAULT_PULSE             (SMOTOR1_TIM_PERIOD>>1)

#define SMOTOR1_COUNTER_FREQ                 ((float)TIMx_CLOCK_FREQ / (SMOTOR1_TIM_PRESCALER+1))
   

/*  定义控制输出引脚 */
#define SMOTOR1_PUL_GPIO_CLK_ENABLE()         __HAL_RCC_GPIOC_CLK_ENABLE();
#define SMOTOR1_PUL_GPIO_PORT                 GPIOC
#define SMOTOR1_PUL_GPIO_PIN                  GPIO_PIN_6
#define SMOTOR1_PUL_PIN_AF                    GPIO_AF3_TIM8 // 复用功能引脚
#define SMOTOR1_PUL_TIMx_CHANNELx             TIM_CHANNEL_1
#define SMOTOR1_ACTIVE_CHANNEL                HAL_TIM_ACTIVE_CHANNEL_1

#define SMOTOR1_DIR_GPIO_CLK_ENABLE()         __HAL_RCC_GPIOC_CLK_ENABLE();
#define SMOTOR1_DIR_GPIO_PORT                 GPIOC
#define SMOTOR1_DIR_GPIO_PIN                  GPIO_PIN_0
#define SMOTOR1_DIR_PIN_AF                    0 // default

#define SMOTOR1_ENA_GPIO_CLK_ENABLE()         __HAL_RCC_GPIOE_CLK_ENABLE();
#define SMOTOR1_ENA_GPIO_PORT                 GPIOE
#define SMOTOR1_ENA_GPIO_PIN                  GPIO_PIN_0
#define SMOTOR1_ENA_PIN_AF                    0 // default

/* 电机方向和使能控制 */
#define SMOTOR1_DIR_FORWARE()                 HAL_GPIO_WritePin(\
                                                SMOTOR1_DIR_GPIO_PORT,\
                                                SMOTOR1_DIR_GPIO_PIN,\
                                                GPIO_PIN_SET\
                                                );
#define SMOTOR1_DIR_REVERSAL()                HAL_GPIO_WritePin(\
                                                SMOTOR1_DIR_GPIO_PORT,\
                                                SMOTOR1_DIR_GPIO_PIN,\
                                                GPIO_PIN_RESET\
                                                );  


#define SMOTOR1_ENABLE()                      HAL_GPIO_WritePin(\
                                                SMOTOR1_ENA_GPIO_PORT,\
                                                SMOTOR1_ENA_GPIO_PIN,\
                                                GPIO_PIN_RESET);
#define SMOTOR1_DISABLE()                     HAL_GPIO_WritePin(\
                                                SMOTOR1_ENA_GPIO_PORT,\
                                                SMOTOR1_ENA_GPIO_PIN,\
                                                GPIO_PIN_SET)

//#define  DEFAULTSPEED                         600 // 初始默认速度 600*0.1 RPM

/************************* 步进电机与驱动器参数宏定义 *************************/

#define SMOTOR1_STEPANGLE                     (1.8f) // 步距角
#define SMOTOR1_MICORSTEP                     (16U)  // 细分数
#define SMOTOR1_PULSEREV                      (uint32_t)((SMOTOR1_MICORSTEP*360U)/SMOTOR1_STEPANGLE)// 360/(1.8/32),一转的步数
#define SMOTOR1_RATEDSPEED                    (20000U) // 额定转速0.1RPM
然后定义设备运转参数

/* 电机方向定义 */
typedef enum
{
  MOTOR_DIR_CW = 0,
  MOTOR_DIR_CCW
}MotorDir_Typedef;

/* 电机使能状态定义 */
typedef enum
{
  MOTOR_ENABLE  = 0,
  MOTOR_DISABLE
}MotorSta_Typedef ;

/* 电机加减速曲线状态定义 */
typedef enum{
  STOP  = 0,   // 加减速曲线状态:停止
  ACCEL,       // 加减速曲线状态:加速阶段
  DECEL,       // 加减速曲线状态:减速阶段
  RUN,         // 加减速曲线状态:匀速阶段  
}MotorRunSta_Typedef;

/* 电机加减速参数类型定义 */
typedef struct {
// 加减速控制相关
__IO  MotorRunSta_Typedef             run_state ; // 电机转动状态
  int32_t                             decel_start;// 启动减速位置
  int32_t                             decel_val;  // 减速阶段步数
  int32_t                             min_delay;  // 最小脉冲周期(最大速度,即匀速段速度)
__IO  int32_t                         step_delay; // 下个脉冲周期(时间间隔),启动时为加速度
__IO  int32_t                         accel_count;// 加(减)速阶段步数计数值
__IO  uint32_t                        step_count; // 步数记录
   
} SpeedRampData_Typedef;        
编写驱动引脚及定时器初始化函数

/*--------------------------- 主控制定时器 -----------------------------------*/
/**
  * 函数功能: 定时器硬件初始化配置
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 无
  */
static void  SMotor1_GPIO_MspInit(void)
{
  GPIO_InitTypeDef GPIO_InitStruct={0};
  
  /* 定时器引脚时钟使能 */
  SMOTOR1_PUL_GPIO_CLK_ENABLE();
  SMOTOR1_DIR_GPIO_CLK_ENABLE();
  SMOTOR1_ENA_GPIO_CLK_ENABLE();
  
  /* 脉冲输出引脚配置 */  
  GPIO_InitStruct.Pin       = SMOTOR1_PUL_GPIO_PIN ;
  GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;      // 复用模式
  GPIO_InitStruct.Pull      = GPIO_PULLUP;
  GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(SMOTOR1_PUL_GPIO_PORT, &GPIO_InitStruct);
  
  /* 方向控制引脚配置 */  
  GPIO_InitStruct.Pin       = SMOTOR1_DIR_GPIO_PIN;
  GPIO_InitStruct.Mode      = GPIO_MODE_OUTPUT_PP;
//  GPIO_InitStruct.Alternate = 0;        
  GPIO_InitStruct.Pull      = GPIO_PULLUP;
  HAL_GPIO_Init(SMOTOR1_DIR_GPIO_PORT, &GPIO_InitStruct);
  
  /* 电机使能引脚配置 */  
  GPIO_InitStruct.Pin       = SMOTOR1_ENA_GPIO_PIN;
  HAL_GPIO_Init(SMOTOR1_ENA_GPIO_PORT, &GPIO_InitStruct);
  
  /* NVIC中断优先级配置 */
  HAL_NVIC_SetPriority(SMOTOR1_TIM_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(SMOTOR1_TIM_IRQn);
  
}

/**
  * 函数功能: 步进电机控制定时器初始化
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 初始化PWM输出功能
  */
void SMOTOR1_TIMx_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig={0};
  TIM_BreakDeadTimeConfigTypeDef sconfigBDTR={0};
  TIM_OC_InitTypeDef sConfigOC={0};
  TIM_MasterConfigTypeDef sMasterConfig={0};
  /* 定时器硬件配置 */
  SMotor1_GPIO_MspInit();
  
  __HAL_AFIO_REMAP_TIM3_ENABLE();//TIM3端口完全重映射
       
  /* 定时器外设配置 */
  SMOTOR1_TIM_RCC_CLK_ENABLE();
  htimx_SMotor1.Instance               = SMOTOR1_TIMx;
//  htimx_SMotor1.Init.Prescaler         = SMOTOR1_TIM_PRESCALER;
  htimx_SMotor1.Init.CounterMode       = TIM_COUNTERMODE_UP;
  htimx_SMotor1.Init.Period            = SMOTOR1_TIM_PERIOD;
  htimx_SMotor1.Init.ClockDivision     = TIM_CLOCKDIVISION_DIV1;
//  htimx_SMotor1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  HAL_TIM_PWM_Init(&htimx_SMotor1);

  /* 时钟源配置 */
  sClockSourceConfig.ClockSource    = TIM_CLOCKSOURCE_INTERNAL; // 内部时钟
  sClockSourceConfig.ClockPrescaler = TIM_CLOCKPRESCALER_DIV1;  // 无时钟预分频
  sClockSourceConfig.ClockPolarity  = TIM_CLOCKPOLARITY_INVERTED;// 无效配置
  sClockSourceConfig.ClockFilter    = 0x0;  
  HAL_TIM_ConfigClockSource(&htimx_SMotor1, &sClockSourceConfig);
  
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode     = TIM_MASTERSLAVEMODE_ENABLE;
//  sMasterConfig.MasterOutputTrigger2= TIM_TRGO2_RESET;
  HAL_TIMEx_MasterConfigSynchronization(&htimx_SMotor1,&sMasterConfig);
  
  /* 死区时间和状态配置 */
  /** 这里主要配置高级定时器的互补通道输出控制,对于刹车功能的配置是无效的
    * 如果使用非高级定时器,可以注释掉这段代码
    */
  sconfigBDTR.OffStateRunMode  = TIM_OSSR_DISABLE;  // 关闭时的运行模式输出极性
  sconfigBDTR.OffStateIDLEMode = TIM_OSSI_DISABLE;  // 关闭时的空闲模式输出极性
  sconfigBDTR.BreakState       = TIM_BREAK_DISABLE; // 不使用刹车功能
  sconfigBDTR.BreakPolarity    = TIM_BREAKPOLARITY_LOW;
  sconfigBDTR.DeadTime         = 0; // Min_Data = 0x00 and Max_Data = 0xFF
  sconfigBDTR.LockLevel        = TIM_LOCKLEVEL_OFF;
//  sconfigBDTR.Break2Filter     = 0; // Min_Data = 0x0 and Max_Data = 0xF
//  sconfigBDTR.Break2Polarity   = TIM_BREAK2POLARITY_LOW;
//  sconfigBDTR.Break2State      = TIM_BREAK2_DISABLE;
  sconfigBDTR.AutomaticOutput  = TIM_AUTOMATICOUTPUT_DISABLE;
  HAL_TIMEx_ConfigBreakDeadTime(&htimx_SMotor1, &sconfigBDTR);
  
  /* PWM配置,CH1 PWM Mode*/
  sConfigOC.OCMode       = TIM_OCMODE_TOGGLE;
  sConfigOC.Pulse        = SMOTOR1_TIM_DEFAULT_PULSE;
  sConfigOC.OCPolarity   = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCNPolarity  = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCIdleState  = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  sConfigOC.OCFastMode   = TIM_OCFAST_DISABLE;
  HAL_TIM_OC_ConfigChannel(&htimx_SMotor1, &sConfigOC, SMOTOR1_PUL_TIMx_CHANNELx);
  
  /* 配置电机默认状态,默认禁止电机转动,并且方向是顺时针转动 */
  SMotor1_Set_Dir  ( MOTOR_DIR_CW ); // 电机方向,默认顺时针转动.
  SMotor1_Set_State( MOTOR_DISABLE );// 电机状态,默认禁止
}

/**
  * 函数功能: 设置电机方向
  * 输入参数: ptrSMotor 步进电机控制句柄指针
  *           Dir 电机方向 参数可选:
  *               [url=home.php?mod=space&uid=2817080]@ARG[/url]  MOTOR_DIR_CW
  *               @arg  MOTOR_DIR_CCW
  * 返 回 值:
  * 说    明:设置步进电机的方向.控制驱动器的DIR端口
  */
void SMotor1_Set_Dir(MotorDir_Typedef Dir)
{
  /* 设置对应编号的步进电机方向 */
  Motor_Dir = Dir;
  if(Dir == MOTOR_DIR_CW)
  {
    SMOTOR1_DIR_FORWARE();
  }
  else
  {
    SMOTOR1_DIR_REVERSAL();
  }
}

/**
  * 函数功能: 设置电机状态
  * 输入参数: ptrSMotor 步进电机控制结构体指针
  *           Ena 电机状态 参数可选:
  *               @arg  MOTOR_ENABLE
  *               @arg  MOTOR_DISABLE
  * 返 回 值:
  * 说    明: 设置步进电机的状态,控制驱动器的ENA端口
  */
void SMotor1_Set_State( MotorSta_Typedef Ena)
{
  /* 设置对应编号的步进电机方向 */
  Motor_State = Ena;
  if(Ena == MOTOR_ENABLE)
  {
    SMOTOR1_ENABLE();
  }
  else
  {
    SMOTOR1_DISABLE();
  }
}
编辑定时器中断处理函数

/**
  * 函数功能: 定时器中断回调函数
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 实现加减速过程
  */
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
  uint32_t tmp = 0;
  // 保存新(下)一个延时周期
  uint32_t new_step_delay = 0;
  // 加速过程中最后一次延时(脉冲周期).
  __IO static uint16_t last_accel_delay = 0;
  // 总移动步数计数器
  __IO static uint32_t step_count = 0;
  // 记录new_step_delay中的余数,提高下一步计算的精度
  __IO static int32_t rest = 0;
  //定时器使用翻转模式,需要进入两次中断才输出一个完整脉冲
  __IO static uint8_t i = 0;
   
  if(htim->Channel == SMOTOR1_ACTIVE_CHANNEL)
  {
    // 设置比较值   
    tmp =__HAL_TIM_GET_COMPARE(&htimx_SMotor1,SMOTOR1_PUL_TIMx_CHANNELx);
    tmp += srd.step_delay;
    __HAL_TIM_SET_COMPARE(&htimx_SMotor1,SMOTOR1_PUL_TIMx_CHANNELx,(uint16_t)tmp );

    i++;       // 定时器中断次数计数值
    if( ( i & 0x01) == 0) // 偶数,输出一个完整脉冲   
    {
      switch(srd.run_state) // 加减速曲线阶段
      {
        case STOP:
          step_count = 0;  // 清零步数计数器
          rest = 0;        // 清零余值
          last_accel_delay = 0;
          // 关闭通道
          Stop_MotorMoving();
          break;

        case ACCEL:
          step_count++;      // 步数加1
          srd.accel_count++; // 加速计数值加1
        
          new_step_delay = srd.step_delay - (((2 * srd.step_delay) + rest)/(4 * srd.accel_count + 1));//计算新(下)一步脉冲周期(时间间隔)
          rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
        
          if(step_count >= srd.decel_start)// 检查是够应该开始减速
          {
            srd.accel_count = srd.decel_val; // 加速计数值为减速阶段计数值的初始值
            srd.run_state = DECEL;           // 下个脉冲进入减速阶段
          }
          else if(new_step_delay <= srd.min_delay) // 检查是否到达期望的最大速度
          {
            last_accel_delay = new_step_delay; // 保存加速过程中最后一次延时(脉冲周期)
            new_step_delay = srd.min_delay;    // 使用min_delay(对应最大速度speed)
            rest = 0;                          // 清零余值
            srd.run_state = RUN;               // 设置为匀速运行状态
          }
          break;

        case RUN:
          step_count ++;  // 步数加1
          new_step_delay = srd.min_delay;     // 使用min_delay(对应最大速度speed)
          if(step_count >= srd.decel_start)   // 需要开始减速
          {
            srd.accel_count = srd.decel_val;  // 减速步数做为加速计数值
            new_step_delay = last_accel_delay;// 加阶段最后的延时做为减速阶段的起始延时(脉冲周期)
            srd.run_state = DECEL;            // 状态改变为减速
          }
          break;

        case DECEL:
          step_count++;  // 步数加1
          srd.accel_count++;
          new_step_delay = srd.step_delay - (((2 * srd.step_delay) + rest)/(4 * srd.accel_count + 1)); //计算新(下)一步脉冲周期(时间间隔)
          rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
         
          //检查是否为最后一步
          if(srd.accel_count >= -1)
          {
            srd.run_state = STOP;
          }
          break;
      }     
      if(new_step_delay>0xFFFF)
        new_step_delay = 0xFFFF;
      srd.step_delay = new_step_delay; // 为下个(新的)延时(脉冲周期)赋值
    }
  }
}




然后编写梯形加减速控制算法函数

/****************************** 梯形加减速控制 ********************************/
/**
  * 函数功能: 停止电机转动
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 禁止输出PWM,失能电机
  */
static void  Stop_MotorMoving()
{
  HAL_TIM_OC_Stop_IT(&htimx_SMotor1, SMOTOR1_PUL_TIMx_CHANNELx);
  LED1_OFF();
}

/**
  * 函数功能: 步进电机线性速度控制
  * 输入参数: StepMotor 包含了梯形加减速速度控制所需要的参数包括有:
  *           @arg  Step  总的步数,单位是步数,也就是脉冲数,1转的脉冲数就是SMOTOR1_PULSEREV
  *           @arg  Accel 加速度,单位是r/s^2
  *           @arg  Decel 减速度,单位是r/s^2
  *           @arg  Speed 最高速度,单位是r/s
  * 返 回 值: 无
  * 说    明: 计算初始速度值,和各种限制条件,启动脉冲输出
  */
void STEPMOTOR_LSCMoveRel (float step, float accel,float decel, float speed)
{
  /* 由于时间变化的一倍,所以速度和加速度也相应的乘上2和4 */
  accel = accel * 4; // 由于使用比较翻转模式,
  decel = decel * 4; // 计算结果是半个周期的比较值
  speed = speed * 2; // 所以时间也要做相应的调整.(t = t*2)
  
  /* 达到设定的速度时需要的步数 */
        float max_s_lim;
  /* 必须要开始减速的步数(如果加速没有达到最大速度)*/
        float accel_lim;
  /* 预分频系数 */
  
  uint32_t tmp = 0;
  
  LED1_ON();
  
  /* 计算起始速度和最大速度对应的定时器周期值 */
  // 通过计算第一个(c0) 的步进延时来设定加速度,其中accel单位为 Rev/sec^2
  // step_delay = 1/tt * sqrt(2*alpha/accel)
  // step_delay = ( tfreq*0.676 ) * sqrt( (2*alpha) / accel )
  srd.step_delay = round((SMOTOR1_COUNTER_FREQ * sqrtf(ALPHA_x2 / accel))*0.676f);//C0,初始速度的定时器值
  
  // 设置最大速度极限.
  srd.min_delay = round((ALPHA * SMOTOR1_COUNTER_FREQ ) / speed);
  
  /* 计算加减速需要的参数 */
  // 计算多少步之后达到最大速度的限制
  // max_s_lim = speed^2 / (2*alpha*accel)
  max_s_lim = speed * speed / (ALPHA_x2 * accel) ;
   
  // 计算多少步之后必须开始减速
  // n1 = (n1+n2)decel / (accel + decel)
  accel_lim =  step*decel / (accel + decel) ;  
  
  /* 计算加速限制条件和开始减速的位置 */
  // 使用限制条件可以计算出减速阶段步数
  if(accel_lim <= max_s_lim)
  {
    srd.decel_val = (int32_t)(accel_lim - step);
  }
  else
  {
    srd.decel_val = round (- fabs ( max_s_lim * accel / decel) );
  }
  // 计算开始减速时的步数
  srd.decel_start = (int32_t)(step + srd.decel_val);
  srd.accel_count = 0;
  srd.run_state = ACCEL;
  
  SMotor1_Set_Dir(MOTOR_DIR_CW);//  默认方向是顺时针
  SMotor1_Set_State(MOTOR_ENABLE);//  使能电机转动
  
    // 设置比较值   
  tmp =__HAL_TIM_GET_COUNTER(&htimx_SMotor1);
  tmp += srd.step_delay;
  __HAL_TIM_SET_COMPARE(&htimx_SMotor1,SMOTOR1_PUL_TIMx_CHANNELx,(uint16_t)tmp);  
  HAL_TIM_OC_Start_IT(&htimx_SMotor1, SMOTOR1_PUL_TIMx_CHANNELx);
}


设置运行参数

#define MOTORSPEED        10.0f   //  单位是r/s 
#define ACCEL             5.0f    //  单位是r/(s^2)
#define DECEL             5.0f    //  单位是r/(s^2)
#define STEP              (10*SMOTOR1_PULSEREV) // (100转 的脉冲数,一转是6400)


在主程序中调用初始化程序,运行控制代码就可以了

  /* 初始化配置步进电机1控制定时器 */
  SMOTOR1_TIMx_Init();


      STEPMOTOR_LSCMoveRel(STEP, ACCEL, DECEL, MOTORSPEED);   


使用特权

评论回复
沙发
littlelida| | 2022-8-21 13:59 | 只看该作者
实测效果如何

使用特权

评论回复
板凳
beijinglaobai|  楼主 | 2022-8-21 14:46 | 只看该作者

使用特权

评论回复
地板
beijinglaobai|  楼主 | 2022-8-21 14:48 | 只看该作者

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

58

主题

113

帖子

1

粉丝