mattlincoln 发表于 2025-9-30 09:22

步进电机细分控制算法

2、细分算法原理如下图上半部分所示,步进电机的两相线圈中的电流为相差90°的正弦波,一个正弦周期,步进电机转动了四整步。细分算法的基本思想是将一个整步分成若干个微步,控制每个微步的PWM占空比间接控制线圈中的电流大小及方向,从而使线圈中通过的电流近似正弦波。




细分生成软件
[*]细分控制算法:
[*]根据细分原理部分电流图下半部分实现对DRV8833的引脚控制,直接贴源码,配合电流图阅读更清晰。
.h文件如下:
#ifndef INC_DRV_STEPPER_H_
#define INC_DRV_STEPPER_H_
#include "main.h"
#include "stdint.h"
#define AIN1_SetCompare(TIMx,CompareValue) TIMx->CCR1 = CompareValue
#define AIN2_SetCompare(TIMx,CompareValue) TIMx->CCR2 = CompareValue
#define BIN1_SetCompare(TIMx,CompareValue) TIMx->CCR4 = CompareValue
#define BIN2_SetCompare(TIMx,CompareValue) TIMx->CCR3 = CompareValue
#define MICROSTEP64   //64细分,一圈需要64*200步
//PWM时钟64MHz不分频,计数值3200
#define PWM_CNT   3200//PWM计数值
#define PWM_CYCLE50   //PWM周期,单位us
typedef struct{
    uint8_t Mode;
    uint8_t Dir;
    int64_t Step;
}
STEPPER_MOTOR;
extern STEPPER_MOTOR sm;
extern uint8_t g_Full_En;
void Set_StepperMotor_Speed(float speed);
void Start_StepperMotor(uint8_t mode, uint8_t dir, uint32_t step);
void Stop_StepperMotor(void);
void Microstep_Control(void);
#endif /* INC_DRV_STEPPER_H_ */
.c文件如下:
/*
* stepper.c
*
*Created on: Feb 17, 2025
*      Author: 74351
*/
#include "drv_stepper.h"
//64细分电流表,半周数据
const uint16_t Current_Subdivision_Table =
{
    0,44,88,132,176,220,264,308,351,394,437,480,523,565,606,648,
    689,729,770,809,849,887,925,963,1000,1036,1072,1107,1142,1176,1209,1241,
    1273,1304,1334,1363,1391,1419,1446,1472,1497,1521,1544,1566,1587,1608,1627,1646,
    1663,1679,1695,1709,1722,1735,1746,1756,1765,1773,1781,1786,1791,1795,1798,1799,
    1800,1799,1798,1795,1791,1786,1781,1773,1765,1756,1746,1735,1722,1709,1695,1679,
    1663,1646,1627,1608,1587,1566,1544,1521,1497,1472,1446,1419,1391,1363,1334,1304,
    1273,1241,1209,1176,1142,1107,1072,1036,1000,963,925,887,849,809,770,729,
    689,648,606,565,523,480,437,394,351,308,264,220,176,132,88,44
};
STEPPER_MOTOR sm = {
    .Mode = 0,
    .Dir = 0,
    .Step = 0
};
static int16_t g_StepLogic = 0;
static uint8_t g_AIN1_Index = 0;
static uint8_t g_AIN2_Index = 0;
static uint8_t g_BIN1_Index = 0;
static uint8_t g_BIN2_Index = 0;
uint8_t g_Full_En = 0;
static uint16_t g_Full_Delay = 30;
static int16_t g_Full_TimerCount = 30;
static int g_Integer = 0;   //整数部分
static int g_Remainder = 0;//余数部分
static int16_t g_DelayCount1 = 1;
static int16_t g_DelayCount2 = 1;
static int16_t g_TimerCount1 = 0;
static int16_t g_TimerCount2 = 0;
static int16_t g_Step_Count = 0;
/**
* @brief设置步进电机速度
* @paramspeed: 转速(单位:r/min)
* @retval 无
*/
void Set_StepperMotor_Speed(float speed)
{
    float delay = 0,delay_cnt = 0;
    float time = 60 / speed; //每转所需时间,单位s
    delay = (time * 1000000) - (MICROSTEP * 200 * PWM_CYCLE);
    delay_cnt = delay / PWM_CYCLE;
    g_Integer = (int)delay_cnt / (MICROSTEP * 200);
    g_Remainder = (int)delay_cnt % (MICROSTEP * 200);
    if(g_Integer >= 0)
    {
      g_DelayCount1 = g_Integer + 1;
      g_TimerCount1 = g_DelayCount1;
    }
    if(g_Remainder > 0)
    {
      g_DelayCount2 = g_DelayCount1 + 1;
      g_TimerCount2 = g_DelayCount2;
    }
}
/**
* @brief启动步进电机
* @parammode: 0-连续旋转 1-转动指定步数
* @paramdir: 0-顺时针 1-逆时针
* @paramstep: 指定步数(mode=1时有效)
* @retval 无
*/
void Start_StepperMotor(uint8_t mode, uint8_t dir, uint32_t step)
{
    if(mode == 0)
    {
      sm.Mode = 0;
    }
    else
    {
      sm.Mode = 1;
      sm.Step = step;
    }
    if(dir == 0)
    {
      sm.Dir = 0;
      if(g_Full_En == 0)
      {
            g_StepLogic = MICROSTEP << 2;
      }
      else
      {
            g_StepLogic = 3;
      }
    }
    else
    {
      sm.Dir = 1;
      g_StepLogic = 0;
    }
    if(g_Full_En == 1)
    {
      g_Full_TimerCount = g_Full_Delay;
    }
    TIM3->DIER |= (0x1UL << 0);
    TIM3->CR1 |= (0x1UL << 0);
    AIN1_SetCompare(TIM3, 0);
    AIN2_SetCompare(TIM3, 0);
    BIN1_SetCompare(TIM3, 0);
    BIN2_SetCompare(TIM3, 0);
    TIM3->CCER |= (0x1UL << 0);
    TIM3->CCER |= (0x1UL << 4);
    TIM3->CCER |= (0x1UL << 8);
    TIM3->CCER |= (0x1UL << 12);
}
/**
* @brief停止步进电机
* @retval 无
*/
void Stop_StepperMotor(void)
{
    TIM3->DIER &= ~(0x1UL << 0);
    TIM3->CR1 &= ~(0x1UL << 0);
    AIN1_SetCompare(TIM3, 0);
    AIN2_SetCompare(TIM3, 0);
    BIN1_SetCompare(TIM3, 0);
    BIN2_SetCompare(TIM3, 0);
    TIM3->CCER &= ~(0x1UL << 0);
    TIM3->CCER &= ~(0x1UL << 4);
    TIM3->CCER &= ~(0x1UL << 8);
    TIM3->CCER &= ~(0x1UL << 12);
}
/**
* @brief四拍微步控制,需要在PWM定时器UPDATE中断中调用
* @retval 无
*/
void Microstep_Control(void)
{
    uint8_t phase = 0;
    if(g_Full_En == 0)
    {
      if(g_Step_Count >= (MICROSTEP * 200))
      {
            g_Step_Count = 0;
      }
      if(g_Step_Count < g_Remainder)
      {
            g_TimerCount2--;
      }
      else
      {
            g_TimerCount1--;
      }
      if(g_TimerCount1 <= 0 || g_TimerCount2 <= 0)
      {
            g_TimerCount1 = g_DelayCount1;
            g_TimerCount2 = g_DelayCount2;
            phase = g_StepLogic >> 6;
            switch(phase)
            {
                case 0:
                g_AIN1_Index = g_StepLogic;
                g_AIN2_Index = 0;
                g_BIN1_Index = 0;
                g_BIN2_Index = g_StepLogic + MICROSTEP;
                break;
                case 1:
                g_AIN1_Index = g_StepLogic;
                g_AIN2_Index = 0;
                g_BIN1_Index = g_StepLogic - MICROSTEP;
                g_BIN2_Index = 0;
                break;
                case 2:
                g_AIN1_Index = 0;
                g_AIN2_Index = g_StepLogic - (MICROSTEP << 1);
                g_BIN1_Index = g_StepLogic - MICROSTEP;
                g_BIN2_Index = 0;
                break;
                case 3:
                g_AIN1_Index = 0;
                g_AIN2_Index = g_StepLogic - (MICROSTEP << 1);
                g_BIN1_Index = 0;
                g_BIN2_Index = g_StepLogic - ((MICROSTEP << 1) + MICROSTEP);
                break;
            }
            g_Step_Count++;
            AIN1_SetCompare(TIM3, PWM_CNT - Current_Subdivision_Table);
            AIN2_SetCompare(TIM3, PWM_CNT - Current_Subdivision_Table);
            BIN1_SetCompare(TIM3, PWM_CNT - Current_Subdivision_Table);
            BIN2_SetCompare(TIM3, PWM_CNT - Current_Subdivision_Table);
            if(sm.Dir == 0)
            {
                g_StepLogic--;
                if(g_StepLogic < 0)
                {
                  g_StepLogic = MICROSTEP << 2;
                }
            }
            else
            {
                g_StepLogic++;
                if(g_StepLogic > ((MICROSTEP << 2) - 1))
                {
                  g_StepLogic = 0;
                }
            }
            if(sm.Mode == 1)
            {
                sm.Step--;
                if(sm.Step < 0)
                {
                  sm.Step = 0;
                  Stop_StepperMotor();
                }
            }
      }
    }
    else
    {
      g_Full_TimerCount--;
      if(g_Full_TimerCount < 0)
      {
            g_Full_TimerCount = g_Full_Delay;
            if(sm.Dir == 0)
            {
                g_StepLogic--;
                if(g_StepLogic < 0) g_StepLogic = 3;
            }
            else
            {
                g_StepLogic++;
                if(g_StepLogic > 3) g_StepLogic = 0;
            }
            switch (g_StepLogic)
            {
                case 0:
                AIN1_SetCompare(TIM3,PWM_CNT);
                AIN2_SetCompare(TIM3,0);
                BIN1_SetCompare(TIM3,PWM_CNT);
                BIN2_SetCompare(TIM3,0);
                break;
                case 1:
                AIN1_SetCompare(TIM3,0);
                AIN2_SetCompare(TIM3,PWM_CNT);
                BIN1_SetCompare(TIM3,PWM_CNT);
                BIN2_SetCompare(TIM3,0);
                break;
                case 2:
                AIN1_SetCompare(TIM3,0);
                AIN2_SetCompare(TIM3,PWM_CNT);
                BIN1_SetCompare(TIM3,0);
                BIN2_SetCompare(TIM3,PWM_CNT);
                break;
                case 3:
                AIN1_SetCompare(TIM3,PWM_CNT);
                AIN2_SetCompare(TIM3,0);
                BIN1_SetCompare(TIM3,0);
                BIN2_SetCompare(TIM3,PWM_CNT);
                break;
            }
      }
    }
}
最后在定时器中断中调用Microstep_Control()即可:
void TIM3_IRQHandler(void)
{
/* USER CODE BEGIN TIM3_IRQn 0 */
    if((TIM3->SR & (0x1UL << 0)) == (0x1UL << 0))   //UPDATE中断
    {
      TIM3->SR = ~(0x1UL << 0);
      Microstep_Control();
    }
/* USER CODE END TIM3_IRQn 0 */
/* USER CODE BEGIN TIM3_IRQn 1 */
/* USER CODE END TIM3_IRQn 1 */
}


szt1993 发表于 2025-9-30 09:55

步进电机细分控制算法
页: [1]
查看完整版本: 步进电机细分控制算法