关于模块的介绍大家可以看看这位佬的博客:步进电机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

image-20260306211246808

image-20260306211147694

image-20260306211115511

然后就可以开始使用了

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐