基于STM32G030與DRV8833的步進(jìn)電機(jī)高精度細(xì)分控制方案

基于STM32G030與DRV8833的步進(jìn)電機(jī)高精度細(xì)分控制方案

1. 系統(tǒng)硬件組成

  • 主控芯片: STM32G030C8T6(Cortex-M0+, 主頻64MHz)
  • 驅(qū)動芯片: DRV8833(雙H橋,峰值電流1.5A)
  • 步進(jìn)電機(jī): 標(biāo)準(zhǔn)42型兩相四線步進(jìn)電機(jī) (200步/圈)

2. 細(xì)分控制原理

步進(jìn)電機(jī)基礎(chǔ)步距角由電機(jī)結(jié)構(gòu)決定(如1.8°)。細(xì)分控制的核心思想是精確調(diào)制兩相線圈中的電流波形:
1.理想電流模型: 兩相線圈 ( A+ A-, B+ B- ) 應(yīng)通入相位差為90°的正弦電流。
2.整步與微步: 一個(gè)完整的正弦周期(360°)對應(yīng)電機(jī)轉(zhuǎn)動4個(gè)整步(1.8° * 4)。細(xì)分(如64細(xì)分) 即是將這4個(gè)整步拆分為64 * 4 = 256個(gè)微步。
3.H橋與電流方向: DRV8833的H橋結(jié)構(gòu)允許通過PWM精確控制每個(gè)線圈兩端的電壓極性,從而控制電流大小和方向。
4.PWM模擬正弦波: 通過計(jì)算不同微步位置對應(yīng)的目標(biāo)電流值,并映射到PWM的占空比,使線圈電流逼近理想正弦波。這被稱為SPWM(Sinusoidal PWM)。

電流圖

3. 硬件連接

STM32G030C8T6        DRV8833引腳      功能說明  
-----------------    ------------    --------------------------  
TIM3_CH1 (PA6)   -->     AIN1         A相線圈正向驅(qū)動PWM  
TIM3_CH2 (PA7)   -->     AIN2         A相線圈反向驅(qū)動PWM  
TIM3_CH4 (PB1)   -->     BIN1         B相線圈正向驅(qū)動PWM  
TIM3_CH3 (PB0)   -->     BIN2         B相線圈反向驅(qū)動PWM  
3.3V             -->     SLEEP        使能DRV8833 (高電平有效)  
GND              -->     GND          共地連接  

4. 關(guān)鍵配置

// PWM 核心參數(shù)計(jì)算 (64MHz系統(tǒng)時(shí)鐘)
//PWM頻率 = 20kHz (人耳可聞范圍之上,降低電機(jī)噪音)
//定時(shí)器時(shí)鐘 = 64MHz
//預(yù)分頻器(Prescaler) = 0 (不分頻)
//自動重裝載值(ARR) = 3199
//實(shí)際PWM頻率 = 64MHz / (3199 + 1) = 20,000 Hz

void MX_TIM3_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 0;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 3199;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 1;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM3_Init 2 */

  /* USER CODE END TIM3_Init 2 */
  HAL_TIM_MspPostInit(&htim3);

}

5. 核心: 64細(xì)分正弦波電流表生成

  • 工具: 使用 spwm_calc 軟件(或其他SPWM計(jì)算工具)
  • 關(guān)鍵參數(shù):
    • SPWM幅值: 1800 (設(shè)定值,需小于ARR(3199),此值直接影響電機(jī)峰值電流和扭矩)
    • 周內(nèi)點(diǎn)數(shù): 128 (64細(xì)分 * 2,因?yàn)槊總€(gè)整步對應(yīng)半周正弦波)
    • 半周模式: 半周模式,利用對稱性節(jié)省存儲空間
  • 生成結(jié)果: 一個(gè)包含128個(gè)uint16_t類型元素的數(shù)組 Current_Subdivision_Table[128],存儲了從0°到180°的CCR值(對應(yīng)電流從0到峰值再到0)。
  • 波形預(yù)覽(軟件生成):

6. 核心算法:微步控制實(shí)現(xiàn)(Microstep_Control)

核心邏輯在定時(shí)器TIM3的更新中斷(TIM3_IRQHandler)中調(diào)用執(zhí)行。

6.1 數(shù)據(jù)結(jié)構(gòu)

typedef struct {
    uint8_t Mode;   // 0:連續(xù)旋轉(zhuǎn), 1:指定步數(shù)
    uint8_t Dir;    // 0:順時(shí)針, 1:逆時(shí)針
    int64_t Step;   // 剩余步數(shù) (Mode=1時(shí)有效)
} STEPPER_MOTOR;
STEPPER_MOTOR sm; // 全局電機(jī)狀態(tài)結(jié)構(gòu)體

6.2 速度控制(Set_StepperMotor_Speed)

  • 輸入: 期望轉(zhuǎn)速 speed (單位: RPM - 轉(zhuǎn)/分鐘)。
  • 計(jì)算原理:
    1. 計(jì)算轉(zhuǎn)一圈所需總時(shí)間 time (秒): time = 60 / speed
    2. 計(jì)算一圈所需總微步數(shù): totalMicroSteps = MICROSTEP(64) * 200 = 12800
    3. 計(jì)算純PWM驅(qū)動時(shí)間: pwmTime = totalMicroSteps * PWM_CYCLE(50us)
    4. 計(jì)算需要額外插入的延時(shí)總時(shí)間: delay = (time * 1000000) - pwmTime (微秒)
    5. 將總延時(shí)分配到每個(gè)微步間隔:
    • 平均每個(gè)微步間隔額外延時(shí): delayPerStep = delay / totalMicroSteps
    • 拆分為整數(shù)部分(g_Integer)和余數(shù)部分(g_Remainder)
    • 延時(shí)計(jì)數(shù)器: g_DelayCount1 (基礎(chǔ)延時(shí)), g_DelayCount2 (帶余數(shù)補(bǔ)償?shù)难訒r(shí))

6.3 相位切換與電流表索引計(jì)算

核心變量: g_StepLogic (范圍 0-255, 對應(yīng)256個(gè)微步)

  • 劃分4個(gè)相位區(qū) (每區(qū)64微步):

    phase = g_StepLogic >> 6; // 等價(jià)于 g_StepLogic / 64 (值: 0,1,2,3)
    
  • 根據(jù)相位區(qū)和方向(Dir)計(jì)算4個(gè)PWM通道的電流表索引:

    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;
    }
    
    • 映射到電流表: 計(jì)算出的索引值(g_AIN1_Index等)直接用于查表 Current_Subdivision_Table。
    • PWM占空比設(shè)置: CCRx = PWM_CNT(3200) - 查表結(jié)果 (因配置為高電平有效,占空比 = (PWM_CNT - CCRx) / PWM_CNT)

6.4 步進(jìn)與狀態(tài)更新

1.速度控制延時(shí): 根據(jù)之前計(jì)算的g_DelayCount1/2和g_TimerCount1/2決定是否進(jìn)行下一步。
2.查表設(shè)置PWM: 當(dāng)需要步進(jìn)時(shí),使用計(jì)算好的索引設(shè)置4個(gè)CCR寄存器。
3.更新g_StepLogic: 根據(jù)方向(Dir)遞增或遞減。達(dá)到邊界(0或255)時(shí)循環(huán)。
4.步數(shù)管理 (Mode=1): 遞減sm.Step,減至0時(shí)停止電機(jī)。
5.連續(xù)旋轉(zhuǎn) (Mode=0): g_StepLogic循環(huán)更新,電機(jī)持續(xù)旋轉(zhuǎn)。

7. 完整驅(qū)動源碼

  • drv_stepper.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 MICROSTEP       64          //64細(xì)分,一圈需要64*200步
    //PWM時(shí)鐘64MHz不分頻,計(jì)數(shù)值3200
    #define PWM_CNT         3200        //PWM計(jì)數(shù)值
    #define PWM_CYCLE       50          //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_ */
    
    
  • drv_stepper.c實(shí)現(xiàn)文件

    #include "drv_stepper.h"
    
    //64細(xì)分電流表,半周數(shù)據(jù)
    const uint16_t Current_Subdivision_Table[128] =
    {
            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;       //整數(shù)部分
    static int g_Remainder = 0;     //余數(shù)部分
    
    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  設(shè)置步進(jìn)電機(jī)速度
    * @param  speed:    轉(zhuǎn)速(單位:r/min)
    * @retval 無
    */
    void Set_StepperMotor_Speed(float speed)
    {
        float delay = 0,delay_cnt = 0;
        float time = 60 / speed;    //每轉(zhuǎn)所需時(shí)間,單位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  啟動步進(jìn)電機(jī)
    * @param  mode: 0-連續(xù)旋轉(zhuǎn)  1-轉(zhuǎn)動指定步數(shù)
    * @param    dir:    0-順時(shí)針   1-逆時(shí)針
    * @param  step: 指定步數(shù)(mode=1時(shí)有效)
    * @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  停止步進(jìn)電機(jī)
    * @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定時(shí)器UPDATE中斷中調(diào)用
    * @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[g_AIN1_Index]);
                AIN2_SetCompare(TIM3, PWM_CNT - Current_Subdivision_Table[g_AIN2_Index]);
                BIN1_SetCompare(TIM3, PWM_CNT - Current_Subdivision_Table[g_BIN1_Index]);
                BIN2_SetCompare(TIM3, PWM_CNT - Current_Subdivision_Table[g_BIN2_Index]);
                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;
                }
            }
        }
    }
    
  • 定時(shí)器中斷中調(diào)用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 */
    }
    

總結(jié)與要點(diǎn)

1.成功實(shí)現(xiàn)高精度控制: 本方案基于STM32G030的PWM定時(shí)器與DRV8833 H橋,實(shí)現(xiàn)了兩相步進(jìn)電機(jī)的64細(xì)分平滑驅(qū)動。
2.SPWM是關(guān)鍵: 預(yù)先生成的128點(diǎn)正弦電流表是驅(qū)動平滑性的核心,配合H橋的互補(bǔ)PWM輸出精確控制線圈電流。
3.靈活的速度控制: 速度算法通過動態(tài)插入延時(shí)實(shí)現(xiàn)精確轉(zhuǎn)速控制,且能處理非整數(shù)倍的延時(shí)需求。
4.低噪聲與低振動: 20kHz PWM頻率有效避開了人耳敏感范圍,配合正弦波驅(qū)動顯著降低了電機(jī)噪聲和振動。
5.資源占用優(yōu)化: 利用半周模式存儲正弦表,節(jié)省了寶貴的Flash空間。
6.可擴(kuò)展性: 代碼結(jié)構(gòu)清晰,通過修改MICROSTEP定義和重新生成電流表即可支持不同細(xì)分?jǐn)?shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容