ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32F1控制步进电机

2026/8/9 22:52:22 拓冰建站 浏览量
STM32F1控制步进电机

一、基础知识

1. 步进电机控制方式

脉冲+方向控制(最常见)

控制信号:

        DIR方向:高低电平决定正转或反转;

        STEP脉冲:每个脉冲电机前进一步(可通过端口拉高拉低来模拟脉冲,或使用pwm来生成脉冲);

方法定义说明
GPIO 模拟脉冲用软件代码控制 GPIO 引脚“高/低”来模拟脉冲信号,通常通过 delay_us() 等手动延时
PWM 脉冲(定时器)使用硬件定时器自动生成一定频率和占空比的脉冲波形,直接输出到 GPIO 引脚

2. GPIO模拟脉冲和PWM生成脉冲两者的区别

项目GPIO 模拟脉冲PWM 定时器输出
控制方式纯软件控制:代码中手动翻转引脚硬件自动输出,CPU不再关心
精度/频率稳定性受 CPU、延时函数精度影响(不稳定)非常精准(定时器硬件级别)
CPU 占用率高:CPU 要一直跑在 delay 上低:设置一次,自动输出
适合任务简单、低速、临时用实时、高速、精确的脉冲控制
速度上限通常 <5kHz,超了容易乱可达几十 kHz 或更高
支持加减速控制复杂,需要自己调节 delay 变量更容易调节频率,甚至可做变频波(配合 ARR)
控制灵活性灵活(逐步手动控制每一脉冲)受限于定时器结构,但效率高

3. 驱动器细分档

        驱动器上的“细分挡位”是用于设置步进电机细分数(microstepping)的开关,它可以控制电机每收到一个STEP脉冲时的实际转动角度,以实现更平滑、更高精度的运动控制。

        普通步进电机的步距角通常是:1.8°(即每转动一圈需要200个整步)。

细分数每微步角度(1.8°电机)每圈脉冲数
1(全步)1.8°200
2(半步)0.9°400
40.45°800
80.225°1600
160.1125°3200
320.05625°6400
例如下面的驱动器,可以调节M1、M2、M3来控制细分档位。

二、实验

1. GPIO模拟脉冲方式

实验现象:正转2圈,延时,反转1圈。

main.c

#include "stm32f10x.h"
#include "usart1.h"
#include "delay.h"
#include "string.h"
#include "systick.h"
#include "led.h"
#include "motor.h"
#include "key.h"int main(void)
{SystemInit();delay_init();StepMotor_GPIO_Init();StepMotor_Enable();        // 开启电机LED_Init();Key_Init();StepMotor_SetDirection(1); // 设置方向while (1) {
//								if (GPIO_ReadInputDataBit(KEY_GPIO, KEY1_PIN) == Bit_RESET) // 按键1按下(低电平有效)
//								{
//										StepMotor_SetDirection(1);                  // 顺时针
//										StepMotor_StepPulse(200, 500);              // 200 步,800us 间隔(约625Hz)
//										LED_ON();
//								}
//								else if (GPIO_ReadInputDataBit(KEY_GPIO, KEY2_PIN) == Bit_RESET) // 按键2按下
//								{
//										StepMotor_SetDirection(0);                  // 顺时针
//										StepMotor_StepPulse(200, 500);              // 200 步,800us 间隔(约625Hz)
//									  LED2_ON();
//								}else{
//										LED_OFF();
//									  LED2_OFF();
//							  }StepMotor_TurnOneCircle(1, 2, 500); // 正转2圈,500us间隔(约1666Hz)delay_ms(8000);StepMotor_TurnOneCircle(0, 1, 500); // 反转一圈delay_ms(8000);}
}

motor.c

#include  "motor.h"void StepMotor_GPIO_Init(void) {RCC_APB2PeriphClockCmd(STEPMOTOR_RCC, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = STEP_PIN | DIR_PIN | EN_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(STEPMOTOR_GPIO, &GPIO_InitStructure);GPIO_ResetBits(STEPMOTOR_GPIO, STEP_PIN);GPIO_ResetBits(STEPMOTOR_GPIO, DIR_PIN);GPIO_SetBits(STEPMOTOR_GPIO, EN_PIN); // 默认使能(高电平)
}void StepMotor_Enable(void) {GPIO_SetBits(STEPMOTOR_GPIO, EN_PIN);  // 高电平 = 使能
}void StepMotor_Disable(void) {GPIO_ResetBits(STEPMOTOR_GPIO, EN_PIN); // 低电平 = 失能(断电)
}void StepMotor_SetDirection(uint8_t dir) {if (dir)GPIO_SetBits(STEPMOTOR_GPIO, DIR_PIN);elseGPIO_ResetBits(STEPMOTOR_GPIO, DIR_PIN);
}void StepMotor_StepPulse(uint32_t steps, uint32_t delay_us_val) {for (uint32_t i = 0; i < steps; i++) {GPIO_SetBits(STEPMOTOR_GPIO, STEP_PIN);   // STEP 上升沿delay_us(delay_us_val);GPIO_ResetBits(STEPMOTOR_GPIO, STEP_PIN); // 下降沿delay_us(delay_us_val);}
}void StepMotor_TurnOneCircle(uint8_t dir, uint16_t circles, uint32_t speed_us)
{StepMotor_SetDirection(dir);               // 设置方向(1=正转,0=反转)for (uint32_t i = 0; i < circles*1600; i++) {       // 1600步 = 一圈(8细分)GPIO_SetBits(GPIOA, STEP_PIN);         // STEP高delay_us(speed_us);GPIO_ResetBits(GPIOA, STEP_PIN);       // STEP低delay_us(speed_us);}
}// 简单延时函数(可使用SysTick或Timer)
void delay_us_function(uint32_t us)
{for (uint32_t i = 0; i < us * 8; i++)__NOP();
}

motor.h

#ifndef __MOTOR_H
#define __MOTOR_H#include "stm32f10x.h"// 步进电机引脚定义(你已说明:PA1 -> STEP,PA2 -> DIR,PA3 -> EN)
// GPIO 配置
#define STEPMOTOR_GPIO        GPIOA
#define STEPMOTOR_RCC         RCC_APB2Periph_GPIOA#define STEP_PIN              GPIO_Pin_1
#define DIR_PIN               GPIO_Pin_2
#define EN_PIN                GPIO_Pin_3void StepMotor_GPIO_Init(void);
void StepMotor_SetDirection(uint8_t dir);
void StepMotor_Enable(void);
void StepMotor_Disable(void);
void StepMotor_StepPulse(uint32_t steps, uint32_t delay_us_val);
void StepMotor_TurnCircle(uint8_t dir, uint16_t circles, uint32_t speed_us);
void delay_us(uint32_t us);#endif // __STEPMOTOR_H

key.c

#include "key.h"static uint8_t key1_last = 1;
static uint8_t key2_last = 1;void Key_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);GPIO_InitTypeDef gpio;// KEY1/KEY2 输入,带上拉gpio.GPIO_Pin = KEY1_PIN | KEY2_PIN;gpio.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(KEY_GPIO, &gpio);}uint8_t Key_Scan(void)
{uint8_t key1_now = GPIO_ReadInputDataBit(KEY_GPIO, KEY1_PIN);   //读取当前电平,0为按下。uint8_t key2_now = GPIO_ReadInputDataBit(KEY_GPIO, KEY2_PIN);uint8_t result = KEY_NONE;  //0,没有按键// 按键1:检测下降沿if (key1_last == 1 && key1_now == 0) {delay_ms(20);  // 消抖if (GPIO_ReadInputDataBit(KEY_GPIO, KEY1_PIN) == Bit_RESET)result = KEY1_PRESSED;    //result =1,表示按键1按下。}// 按键2:检测下降沿if (key2_last == 1 && key2_now == 0) {delay_ms(20);  // 消抖if (GPIO_ReadInputDataBit(KEY_GPIO, KEY2_PIN) == Bit_RESET)result = KEY2_PRESSED;   //result =2,表示按键1按下。}key1_last = key1_now;key2_last = key2_now;return result;
}

key.h

#ifndef __KEY_H
#define __KEY_H#include "stm32f10x.h"#define KEY1_PIN GPIO_Pin_1 // 正转按键   //PC1
#define KEY2_PIN GPIO_Pin_3 // 反转按键   //PC3
#define KEY_GPIO GPIOC// 返回值定义(按下事件)
#define KEY_NONE        0
#define KEY1_PRESSED    1
#define KEY2_PRESSED    2
void Key_Init(void);uint8_t Key_Scan(void);
#endif

2. pwm输出脉冲方式

实验现象:按下按键1电机正转2圈,按下按键2电机正转1圈。

tim2_pwm.c

#include "tim2_pwm.h"volatile uint32_t step_count;
volatile uint32_t target_steps;void StepMotor_PWM_Init(uint32_t freq_hz)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef gpio;gpio.GPIO_Pin = GPIO_Pin_1;gpio.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出gpio.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio);TIM_TimeBaseInitTypeDef tim;uint16_t prescaler = 72 - 1; // 假设主频72MHz → 1MHz定时器uint16_t period = 1000000 / freq_hz - 1; // 计算周期tim.TIM_Prescaler = prescaler;tim.TIM_CounterMode = TIM_CounterMode_Up;tim.TIM_Period = period;tim.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInit(TIM2, &tim);TIM_OCInitTypeDef oc;oc.TIM_OCMode = TIM_OCMode_PWM1;oc.TIM_OutputState = TIM_OutputState_Enable;oc.TIM_Pulse = period / 2; // 50% 占空比oc.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OC2Init(TIM2, &oc); // PA1 = CH2TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 开启更新中断NVIC_InitTypeDef nvic;nvic.NVIC_IRQChannel = TIM2_IRQn;nvic.NVIC_IRQChannelPreemptionPriority = 1;nvic.NVIC_IRQChannelSubPriority = 1;nvic.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&nvic);
}void StepMotor_RunCircles(uint32_t circles)
{step_count = 0;target_steps = circles*1600;TIM_Cmd(TIM2, ENABLE); // 启动PWM
}void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);step_count++;if (step_count >= target_steps){TIM_Cmd(TIM2, DISABLE); // 关闭PWMstep_count = 0;}}
}

main.c

#include "stm32f10x.h"
#include "usart1.h"
#include "delay.h"
#include "string.h"
#include "systick.h"
#include "led.h"
#include "motor.h"
#include "key.h"
#include "tim2_pwm.h"int main(void)
{SystemInit();delay_init();StepMotor_GPIO_Init();LED_Init();Key_Init();StepMotor_PWM_Init(1000);  // 设置1kHz频率StepMotor_Enable();while (1) {uint8_t key = Key_Scan();if (key == KEY1_PRESSED) {StepMotor_SetDirection(1);StepMotor_RunCircles(2);}else if (key == KEY2_PRESSED) {StepMotor_SetDirection(0);StepMotor_RunCircles(1);}}
}

注意:pwm输出这里有些函数是和GPIO公用的,比如引脚的初始化,这里我没有再去划分。