步进电机28BYJ-48的驱动代码
本文介绍了28BYJ-48步进电机的驱动实现,主要包括以下内容:1) 定义了电机每转4096步的基本参数和8拍驱动方式的步序查找表;2) 实现了电机状态管理,包括步序索引、剩余步数和方向控制;3) 提供了核心驱动函数,包括引脚控制、单步驱动和速度设置;4) 封装了初始化接口和运动控制接口,支持非阻塞方式控制电机旋转。代码基于STM32 HAL库开发,采用8拍驱动方式,可通过修改定时器参数调节转速,
·
关于模块的介绍大家可以看看这位佬的博客:步进电机28BYJ-48的驱动(arduino,STM32平台),最全的驱动详细原理,驱动电路分析,驱动代码解释_步进电机28by代码-CSDN博客 ,也可以自行上网搜索
下面直接分享可用代码
我使用的是HAL库开发的,如果你需要标准库可以把我的代码扔给ai,让他给你改成标准库
28byj48_app.c
#include "28byj48_app.h"
#include <stdint.h>
/* ============================================================
* 常量定义
* ============================================================ */
/** @brief 28BYJ-48步进电机每转一圈所需的步数(8拍模式)为4096步 */
#define STEPS_PER_CIRCLE 4096
/** @brief 每步对应的角度(度) = 360° / 4096步 ≈ 0.0879°/步 */
#define DEG_PER_STEP (360.0f / STEPS_PER_CIRCLE)
/* ============================================================
* 8拍步序查找表
* ============================================================ */
/**
* @brief 8拍步序列表
*
* 28BYJ-48使用8拍驱动方式,每拍对应一组线圈的通电状态。
* 顺序:{ IN1, IN2, IN3, IN4 }
* 1 表示对应线圈通电,0 表示断电
*
* 正转时,按 index 0→1→2→...→7→0 循环
* 反转时,按 index 7→6→5→...→0→7 循环
*/
static const uint8_t step_sequence[8][4] = {
{1, 0, 0, 0}, /* 拍0:A */
{1, 1, 0, 0}, /* 拍1:AB */
{0, 1, 0, 0}, /* 拍2:B */
{0, 1, 1, 0}, /* 拍3:BC */
{0, 0, 1, 0}, /* 拍4:C */
{0, 0, 1, 1}, /* 拍5:CD */
{0, 0, 0, 1}, /* 拍6:D */
{1, 0, 0, 1} /* 拍7:DA */
};
/* ============================================================
* 电机状态管理(私有变量)
* ============================================================ */
/** @brief 电机运行状态枚举 */
typedef enum {
MOTOR_STATE_IDLE = 0, // 空闲
MOTOR_STATE_RUNNING = 1 // 运行中
} MotorState_t;
/** @brief 定时器句柄指针(由Init传入) */
static TIM_HandleTypeDef *g_htim_motor = NULL;
/** @brief 当前步序索引(0~7) */
static uint8_t g_step_index = 0;
/** @brief 剩余步数计数器 */
static volatile uint32_t g_remaining_steps = 0;
/** @brief 旋转方向 */
static StepMotor_DirectionTypeDef g_direction = STEP_MOTOR_DIR_FORWARD;
/** @brief 电机运行状态 */
static volatile MotorState_t g_motor_state = MOTOR_STATE_IDLE;
/* ============================================================
* 内部私有函数(static,不对外暴露)
* ============================================================ */
/**
* @brief 向4个控制引脚写入电平
* @param in1 IN1引脚电平(1=高,0=低)
* @param in2 IN2引脚电平
* @param in3 IN3引脚电平
* @param in4 IN4引脚电平
* @note 使用 HAL_GPIO_WritePin 函数,引脚标识由CubeMX生成
*/
static inline void StepMotor_WritePins(uint8_t in1, uint8_t in2,
uint8_t in3, uint8_t in4)
{
HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, in1 ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, in2 ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN3_GPIO_Port, IN3_Pin, in3 ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN4_GPIO_Port, IN4_Pin, in4 ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
/**
* @brief 驱动器执行步序列表的某一拍
* @param step_index 当前拍号(0~7),超出范围会自动取模
* @note 根据 step_sequence 数组设置对应的4个电平
*/
static void StepMotor_DriveOneStep(uint8_t step_index)
{
const uint8_t *seq = step_sequence[step_index % 8];
StepMotor_WritePins(seq[0], seq[1], seq[2], seq[3]);
}
/**
* @brief 配置定时器的自动重装载值(控制速度)
* @param speed 速度等级枚举
* @note 假设定时器预分频后频率为 1MHz(1us计数周期)
* SPEED_FAST (2ms) → ARR = 2000-1 = 1999
* SPEED_NORMAL (5ms) → ARR = 5000-1 = 4999
* SPEED_SLOW (8ms) → ARR = 8000-1 = 7999
* SPEED_CRAWL (15ms) → ARR = 15000-1 = 14999
*/
static void StepMotor_SetTimerPeriod(StepMotor_SpeedTypeDef speed)
{
uint32_t arr_value;
switch (speed)
{
case SPEED_FAST:
arr_value = 2000 - 1; // 2ms,最快可靠速度
break;
case SPEED_NORMAL:
arr_value = 5000 - 1; // 5ms
break;
case SPEED_SLOW:
arr_value = 8000 - 1; // 8ms
break;
case SPEED_CRAWL:
arr_value = 15000 - 1; // 15ms,最大扭矩
break;
default:
arr_value = 5000 - 1; // 默认5ms
break;
}
/* 动态修改定时器自动重装载值 */
__HAL_TIM_SET_AUTORELOAD(g_htim_motor, arr_value);
}
/* ============================================================
* 公共接口实现
* ============================================================ */
/**
* @brief 步进电机初始化
* @param htim 定时器句柄指针(如 &htim2)
* @note 保存定时器句柄,但不启动定时器(等待用户调用运动函数)
*/
void StepMotor_Init(TIM_HandleTypeDef *htim)
{
g_htim_motor = htim;
g_step_index = 0;
g_remaining_steps = 0;
g_motor_state = MOTOR_STATE_IDLE;
/* 关闭所有线圈(初始状态) */
StepMotor_WritePins(0, 0, 0, 0);
}
/**
* @brief 控制电机旋转指定步数(非阻塞)
* @param speed 速度等级
* @param direction 旋转方向
* @param step_count 需要运行的步数
* @retval true=成功启动, false=电机忙
*/
uint8_t StepMotor_SetSpeed(StepMotor_SpeedTypeDef speed,
StepMotor_DirectionTypeDef direction,
uint32_t step_count)
{
/* 检查电机是否空闲 */
if (g_motor_state == MOTOR_STATE_RUNNING)
{
return 0; // 上次任务未完成,拒绝新任务
}
/* 检查步数是否有效 */
if (step_count == 0)
{
return 0;
}
/* 配置运动参数 */
g_remaining_steps = step_count;
g_direction = direction;
g_motor_state = MOTOR_STATE_RUNNING;
/* 配置定时器周期(速度) */
StepMotor_SetTimerPeriod(speed);
/* 清除更新中断标志,防止误触发 */
__HAL_TIM_CLEAR_FLAG(g_htim_motor, TIM_FLAG_UPDATE);
/* 启动定时器中断 */
HAL_TIM_Base_Start_IT(g_htim_motor);
return 1;
}
/**
* @brief 控制电机旋转指定角度(非阻塞)
* @param angle_deg 目标角度,单位:度,支持小数(如 90.0f, 180.5f)
* @param speed 速度等级
* @param direction 旋转方向
* @retval true=成功启动, false=电机忙
* @note 计算公式:steps = angle / DEG_PER_STEP = angle * 4096 / 360
* +0.5f 用于四舍五入,提高角度精度
*/
uint8_t StepMotor_RotateAngle(float angle_deg,
StepMotor_SpeedTypeDef speed,
StepMotor_DirectionTypeDef direction)
{
/* 将角度换算为步数(四舍五入) */
uint32_t steps = (uint32_t)((angle_deg / DEG_PER_STEP) + 0.5f);
return StepMotor_SetSpeed(speed, direction, steps);
}
/**
* @brief 控制电机旋转指定圈数(非阻塞)
* @param circles 旋转圈数,正整数
* @param speed 速度等级
* @param direction 旋转方向
* @retval true=成功启动, false=电机忙
* @note 计算公式:steps = circles * STEPS_PER_CIRCLE = circles * 4096
*/
uint8_t StepMotor_RotateCircle(uint32_t circles,
StepMotor_SpeedTypeDef speed,
StepMotor_DirectionTypeDef direction)
{
return StepMotor_SetSpeed(speed, direction, circles * STEPS_PER_CIRCLE);
}
/**
* @brief 查询电机是否正在运行
* @retval true=运行中, false=空闲
*/
uint8_t StepMotor_IsBusy(void)
{
return (g_motor_state == MOTOR_STATE_RUNNING);
}
/**
* @brief 获取剩余步数
* @retval 剩余未完成的步数
*/
uint32_t StepMotor_GetRemainingSteps(void)
{
return g_remaining_steps;
}
/**
* @brief 立即停止电机并关闭所有线圈(紧急停止)
* @note 调用后电机会失去保持力矩
*/
void StepMotor_Stop(void)
{
/* 停止定时器中断 */
HAL_TIM_Base_Stop_IT(g_htim_motor);
/* 关闭所有线圈 */
StepMotor_WritePins(0, 0, 0, 0);
/* 重置状态 */
g_motor_state = MOTOR_STATE_IDLE;
g_remaining_steps = 0;
}
/* ============================================================
* 定时器中断回调函数
* ============================================================ */
/**
* @brief 定时器周期中断回调函数
* @param htim 触发中断的定时器句柄
* @note 此函数会在 HAL_TIM_IRQHandler 内部自动调用
* 每次中断执行一步,然后递减计数器,完成后停止定时器
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* 检查是否为电机定时器 */
if (htim->Instance != g_htim_motor->Instance)
{
return;
}
/* 检查是否有剩余步数 */
if (g_remaining_steps == 0)
{
/* 任务完成,停止定时器并关闭线圈 */
HAL_TIM_Base_Stop_IT(g_htim_motor);
StepMotor_WritePins(0, 0, 0, 0); // 关闭所有线圈
g_motor_state = MOTOR_STATE_IDLE;
return;
}
/* 执行当前拍的驱动信号 */
StepMotor_DriveOneStep(g_step_index);
/* 根据方向更新步序索引 */
if (g_direction == STEP_MOTOR_DIR_FORWARD)
{
g_step_index = (g_step_index + 1) % 8; // 正转:递增循环 0→1→...→7→0
}
else
{
g_step_index = (g_step_index + 7) % 8; // 反转:递减循环(+7相当于-1,避免负数)
}
/* 递减剩余步数 */
g_remaining_steps--;
}
/**
* @brief 定时器中断回调函数(兼容旧接口,可选)
* @param htim 定时器句柄
* @note 用户可以选择在 stm32f1xx_it.c 中直接调用此函数
* 但推荐使用 HAL_TIM_PeriodElapsedCallback(HAL库会自动调用)
*/
void StepMotor_TIM_Callback(TIM_HandleTypeDef *htim)
{
HAL_TIM_PeriodElapsedCallback(htim);
}
uint8_t door_status; //门的状态 0---关门 1--开门
void v_open_door(void) //开门
{
StepMotor_RotateAngle(120.0f, SPEED_FAST, STEP_MOTOR_DIR_FORWARD);
door_status = 1;
}
void v_close_door(void) //关门
{
StepMotor_RotateAngle(120.0f, SPEED_FAST, STEP_MOTOR_DIR_BACKWARD);
door_status = 0;
}
28byj48_app.h
#ifndef __28BYJ48_APP_H__
#define __28BYJ48_APP_H__
#include "bsp_system.h"
#include "main.h"
/* ============================================================
* 使用说明(非阻塞版本)
* ============================================================
* 1. 在CubeMX中配置:
* - 4个GPIO引脚:IN1, IN2, IN3, IN4(推挽输出)
* - TIM2定时器:内部时钟,预分频71,自动重装载999,使能中断
*
* 2. 在main.c中调用初始化:
* StepMotor_Init(&htim2);
*
* 3. 启动电机(非阻塞):
* StepMotor_RotateAngle(90.0f, SPEED_FAST, STEP_MOTOR_DIR_FORWARD);
*
* 4. 查询状态:
* if (StepMotor_IsBusy()) {
* // 电机正在运行
* } else {
* // 电机已停止,可以发送新指令
* }
*
* 5. 紧急停止:
* StepMotor_Stop();
*/
/*
硬件连接:
28BYJ48 MCU
IN1 -> PB6
IN2 -> PB7
IN3 -> PB10
IN4 -> PB11
5V -> 5V
GND -> GND
*/
/* ============================================================
* 枚举类型定义
* ============================================================ */
/**
* @brief 步进电机旋转方向枚举
*/
typedef enum {
STEP_MOTOR_DIR_FORWARD = 0, // 正转
STEP_MOTOR_DIR_BACKWARD = 1 // 反转
} StepMotor_DirectionTypeDef;
/**
* @brief 步进电机速度等级枚举
*
* 枚举值为每步之间的延时(单位:毫秒),同时作为定时器周期配置的依据。
*
* 注意:28BYJ-48 线圈电感较大,步进间隔低于 2ms 时线圈来不及建立足够的磁场,
* 转子会失步(原地震动而不旋转)。因此最快可靠速度为 2ms/步。
*
* SPEED_FAST : 2ms/步,最快可靠速度(转一圈约 8 秒)
* SPEED_NORMAL : 5ms/步,正常速度(转一圈约 20 秒)
* SPEED_SLOW : 8ms/步,慢速(转一圈约 33 秒)
* SPEED_CRAWL : 15ms/步,极慢,适合需要大扭矩的场合(转一圈约 61 秒)
*/
typedef enum {
SPEED_FAST = 2, // 对应定时器周期 2ms,最快可靠速度
SPEED_NORMAL = 5, // 对应定时器周期 5ms
SPEED_SLOW = 8, // 对应定时器周期 8ms
SPEED_CRAWL = 15 // 对应定时器周期 15ms,最大扭矩
} StepMotor_SpeedTypeDef;
/* ============================================================
* 公共接口函数声明
* ============================================================ */
/**
* @brief 步进电机初始化
* @param htim 定时器句柄指针(推荐使用 TIM2)
* @note 必须在使用其他函数前调用,传入已配置好的定时器
*/
void StepMotor_Init(TIM_HandleTypeDef *htim);
/**
* @brief 控制电机旋转指定步数(非阻塞)
* @param speed 速度等级,取 StepMotor_SpeedTypeDef 枚举值
* @param direction 旋转方向,取 StepMotor_DirectionTypeDef 枚举值
* @param step_count 需要运行的步数
* @retval true=成功启动, false=电机忙(上次任务未完成)
*/
uint8_t StepMotor_SetSpeed(StepMotor_SpeedTypeDef speed,
StepMotor_DirectionTypeDef direction,
uint32_t step_count);
/**
* @brief 控制电机旋转指定角度(非阻塞)
* @param angle_deg 目标旋转角度,单位:度,支持小数
* @param speed 速度等级,取 StepMotor_SpeedTypeDef 枚举值
* @param direction 旋转方向,取 StepMotor_DirectionTypeDef 枚举值
* @retval true=成功启动, false=电机忙(上次任务未完成)
*/
uint8_t StepMotor_RotateAngle(float angle_deg,
StepMotor_SpeedTypeDef speed,
StepMotor_DirectionTypeDef direction);
/**
* @brief 控制电机旋转指定圈数(非阻塞)
* @param circles 目标旋转圈数,正整数
* @param speed 速度等级,取 StepMotor_SpeedTypeDef 枚举值
* @param direction 旋转方向,取 StepMotor_DirectionTypeDef 枚举值
* @retval true=成功启动, false=电机忙(上次任务未完成)
*/
uint8_t StepMotor_RotateCircle(uint32_t circles,
StepMotor_SpeedTypeDef speed,
StepMotor_DirectionTypeDef direction);
/**
* @brief 查询电机是否正在运行
* @retval true=运行中, false=空闲
*/
uint8_t StepMotor_IsBusy(void);
/**
* @brief 获取剩余步数
* @retval 剩余未完成的步数
*/
uint32_t StepMotor_GetRemainingSteps(void);
/**
* @brief 立即停止电机并关闭所有线圈(紧急停止)
* @note 调用后电机会失去保持力矩,可能会有惯性滑动
*/
void StepMotor_Stop(void);
/**
* @brief 定时器中断回调函数(内部使用,用户无需调用)
* @note 在 stm32f1xx_it.c 的 TIM2_IRQHandler 中会自动调用 HAL_TIM_IRQHandler
* 然后触发此回调
*/
void StepMotor_TIM_Callback(TIM_HandleTypeDef *htim);
void v_open_door(void); //开门
void v_close_door(void); //关门
#endif /* __28BYJ48_APP_H__ */
快速上手
在cubemax中把要用的四个引脚的标签名改了,改成IN1-4



然后就可以开始使用了
更多推荐



所有评论(0)