基于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ù)。

