基于单片机的步进电机控制系统

基于51单片机的步进电机控制系统,包含硬件设计、软件代码和功能说明。

一、系统概述

1.1 系统功能

  • 步进电机正反转控制
  • 速度调节(加速/减速)
  • 启停控制
  • 角度控制(精确旋转指定角度)
  • 运行状态LCD显示
  • 按键控制
  • 限位保护

1.2 技术指标

参数 指标
单片机 STC89C52/AT89C52
步进电机 28BYJ-48 (5V)
驱动芯片 ULN2003
显示模块 LCD1602
控制方式 4相8拍
步距角 5.625°/64 (减速比1:64)
供电电压 DC 5V

二、硬件设计

2.1 系统原理图

复制代码
+-------------------+     +-------------------+     +-------------------+
|   电源模块        |     |   STC89C52        |     |   ULN2003         |
|   5V DC          |---->|   P1.0-P1.3       |---->|   IN1-IN4         |
|                  |     |   P2.0-P2.7       |     |   OUT1-OUT4       |
|                  |     |   P3.0-P3.3       |     |                   |
|                  |     |   P0.0-P0.7       |     |   COM (5V)        |
+-------------------+     +-------------------+     +---------+---------+
                                                       |
                                                       v
                                              +-------------------+
                                              |   步进电机        |
                                              |   28BYJ-48        |
                                              |   橙色-红-黄-蓝   |
                                              +-------------------+

+-------------------+     +-------------------+
|   LCD1602         |     |   按键模块        |
|   RS -> P2.0      |     |   K1(启动) -> P3.0|
|   RW -> P2.1      |     |   K2(停止) -> P3.1|
|   E  -> P2.2      |     |   K3(正转) -> P3.2|
|   D0-D7 -> P0     |     |   K4(反转) -> P3.3|
|   VSS -> GND      |     |   K5(加速) -> P3.4|
|   VDD -> 5V       |     |   K6(减速) -> P3.5|
|   VO  -> 电位器   |     |   K7(复位) -> RST |
+-------------------+     +-------------------+

2.2 引脚分配表

单片机引脚 连接设备 功能说明
P1.0 ULN2003 IN1 步进电机A相
P1.1 ULN2003 IN2 步进电机B相
P1.2 ULN2003 IN3 步进电机C相
P1.3 ULN2003 IN4 步进电机D相
P2.0 LCD1602 RS 寄存器选择
P2.1 LCD1602 RW 读写选择
P2.2 LCD1602 E 使能信号
P0.0-P0.7 LCD1602 D0-D7 数据总线
P3.0 按键K1 启动
P3.1 按键K2 停止
P3.2 按键K3 正转
P3.3 按键K4 反转
P3.4 按键K5 加速
P3.5 按键K6 减速
P3.6 按键K7 角度设置+
P3.7 按键K8 角度设置-

三、软件设计

3.1 主程序 (main.c)

c 复制代码
#include <reg52.h>
#include <intrins.h>
#include "lcd1602.h"
#include "stepmotor.h"
#include "keyboard.h"

// 系统状态定义
typedef enum {
    SYS_STOP = 0,
    SYS_FORWARD,
    SYS_BACKWARD,
    SYS_SPEED_UP,
    SYS_SPEED_DOWN
} SystemState;

// 全局变量
SystemState sys_state = SYS_STOP;
unsigned int motor_speed = 1000;  // 初始速度(ms)
unsigned int target_angle = 0;    // 目标角度
unsigned int current_angle = 0;   // 当前角度
bit motor_running = 0;

// 函数声明
void System_Init(void);
void Display_Status(void);
void Motor_Control(void);
void Key_Process(unsigned char key);

void main(void)
{
    System_Init();
    
    while(1)
    {
        unsigned char key = Key_Scan();
        if(key != 0xFF)
        {
            Key_Process(key);
        }
        
        Display_Status();
        Motor_Control();
    }
}

void System_Init(void)
{
    // 初始化LCD
    LCD_Init();
    LCD_Clear();
    
    // 初始化步进电机
    StepMotor_Init();
    
    // 显示欢迎界面
    LCD_ShowString(0, 0, "Step Motor Ctrl");
    LCD_ShowString(0, 1, "Initializing...");
    Delay_Ms(1000);
    
    // 显示主界面
    LCD_Clear();
    LCD_ShowString(0, 0, "State:STOP");
    LCD_ShowString(0, 1, "Speed:1000ms");
}

void Display_Status(void)
{
    char display_buf[16];
    
    // 显示状态
    LCD_ShowString(0, 0, "State:");
    switch(sys_state)
    {
        case SYS_STOP:
            LCD_ShowString(6, 0, "STOP ");
            break;
        case SYS_FORWARD:
            LCD_ShowString(6, 0, "FORWD");
            break;
        case SYS_BACKWARD:
            LCD_ShowString(6, 0, "BACK ");
            break;
        default:
            break;
    }
    
    // 显示速度
    sprintf(display_buf, "Speed:%dms", motor_speed);
    LCD_ShowString(0, 1, display_buf);
    
    // 显示角度
    sprintf(display_buf, "Angle:%03d/%03d", current_angle, target_angle);
    LCD_ShowString(8, 0, display_buf);
}

void Motor_Control(void)
{
    static unsigned int speed_counter = 0;
    
    if(motor_running)
    {
        if(++speed_counter >= motor_speed/10)
        {
            speed_counter = 0;
            switch(sys_state)
            {
                case SYS_FORWARD:
                    StepMotor_Forward();
                    current_angle = (current_angle + 1) % 360;
                    break;
                case SYS_BACKWARD:
                    StepMotor_Backward();
                    current_angle = (current_angle - 1 + 360) % 360;
                    break;
                default:
                    break;
            }
        }
    }
}

void Key_Process(unsigned char key)
{
    switch(key)
    {
        case KEY_START:    // 启动
            motor_running = 1;
            sys_state = SYS_FORWARD;
            break;
            
        case KEY_STOP:     // 停止
            motor_running = 0;
            sys_state = SYS_STOP;
            break;
            
        case KEY_FORWARD:  // 正转
            sys_state = SYS_FORWARD;
            motor_running = 1;
            break;
            
        case KEY_BACKWARD: // 反转
            sys_state = SYS_BACKWARD;
            motor_running = 1;
            break;
            
        case KEY_SPEED_UP: // 加速
            if(motor_speed > 200)
                motor_speed -= 100;
            break;
            
        case KEY_SPEED_DOWN: // 减速
            if(motor_speed < 2000)
                motor_speed += 100;
            break;
            
        case KEY_ANGLE_ADD: // 角度增加
            target_angle = (target_angle + 45) % 360;
            break;
            
        case KEY_ANGLE_SUB: // 角度减少
            target_angle = (target_angle - 45 + 360) % 360;
            break;
            
        default:
            break;
    }
}

3.2 步进电机驱动 (stepmotor.h)

c 复制代码
#ifndef __STEPMOTOR_H__
#define __STEPMOTOR_H__

#include <reg52.h>

// 步进电机控制引脚定义
sbit MOTOR_A = P1^0;  // A相
sbit MOTOR_B = P1^1;  // B相
sbit MOTOR_C = P1^2;  // C相
sbit MOTOR_D = P1^3;  // D相

// 步进电机状态定义
typedef enum {
    MOTOR_STOP = 0,
    MOTOR_FORWARD,
    MOTOR_BACKWARD
} MotorState;

// 步进电机相序表(4相8拍)
extern unsigned char code PhaseTable[8][4];

// 函数声明
void StepMotor_Init(void);
void StepMotor_Forward(void);
void StepMotor_Backward(void);
void StepMotor_Stop(void);
void StepMotor_Rotate_Angle(unsigned int angle, bit direction);

#endif

3.3 步进电机驱动 (stepmotor.c)

c 复制代码
#include "stepmotor.h"
#include "delay.h"

// 4相8拍步进序列
unsigned char code PhaseTable[8][4] = {
    {1, 0, 0, 0},  // A
    {1, 1, 0, 0},  // AB
    {0, 1, 0, 0},  // B
    {0, 1, 1, 0},  // BC
    {0, 0, 1, 0},  // C
    {0, 0, 1, 1},  // CD
    {0, 0, 0, 1},  // D
    {1, 0, 0, 1}   // DA
};

static unsigned char phase_index = 0;  // 当前相位索引

void StepMotor_Init(void)
{
    MOTOR_A = 0;
    MOTOR_B = 0;
    MOTOR_C = 0;
    MOTOR_D = 0;
}

void StepMotor_Forward(void)
{
    phase_index = (phase_index + 1) % 8;
    
    MOTOR_A = PhaseTable[phase_index][0];
    MOTOR_B = PhaseTable[phase_index][1];
    MOTOR_C = PhaseTable[phase_index][2];
    MOTOR_D = PhaseTable[phase_index][3];
    
    Delay_Ms(2);  // 短暂延时
}

void StepMotor_Backward(void)
{
    phase_index = (phase_index - 1 + 8) % 8;
    
    MOTOR_A = PhaseTable[phase_index][0];
    MOTOR_B = PhaseTable[phase_index][1];
    MOTOR_C = PhaseTable[phase_index][2];
    MOTOR_D = PhaseTable[phase_index][3];
    
    Delay_Ms(2);  // 短暂延时
}

void StepMotor_Stop(void)
{
    MOTOR_A = 0;
    MOTOR_B = 0;
    MOTOR_C = 0;
    MOTOR_D = 0;
}

// 旋转指定角度
void StepMotor_Rotate_Angle(unsigned int angle, bit direction)
{
    unsigned int steps;
    unsigned int i;
    
    // 计算需要的步数
    // 28BYJ-48步进角5.625°,减速比1:64
    // 每步实际角度 = 5.625/64 ≈ 0.0879°
    // 步数 = 角度 / 0.0879 ≈ 角度 * 11.377
    steps = (unsigned int)((float)angle * 11.377);
    
    for(i = 0; i < steps; i++)
    {
        if(direction == 0)  // 正转
            StepMotor_Forward();
        else                // 反转
            StepMotor_Backward();
            
        Delay_Ms(5);  // 控制转速
    }
}

3.4 LCD1602驱动 (lcd1602.h)

c 复制代码
#ifndef __LCD1602_H__
#define __LCD1602_H__

#include <reg52.h>

// LCD引脚定义
sbit LCD_RS = P2^0;
sbit LCD_RW = P2^1;
sbit LCD_E  = P2^2;
#define LCD_DATA P0

// 函数声明
void LCD_Init(void);
void LCD_Clear(void);
void LCD_ShowString(unsigned char x, unsigned char y, char *str);
void LCD_ShowChar(unsigned char x, unsigned char y, char ch);
void LCD_Write_Command(unsigned char cmd);
void LCD_Write_Data(unsigned char dat);

#endif

3.5 LCD1602驱动 (lcd1602.c)

c 复制代码
#include "lcd1602.h"
#include "delay.h"

void LCD_Write_Command(unsigned char cmd)
{
    LCD_RS = 0;  // 命令
    LCD_RW = 0;  // 写
    LCD_DATA = cmd;
    LCD_E = 1;
    Delay_Ms(1);
    LCD_E = 0;
    Delay_Ms(1);
}

void LCD_Write_Data(unsigned char dat)
{
    LCD_RS = 1;  // 数据
    LCD_RW = 0;  // 写
    LCD_DATA = dat;
    LCD_E = 1;
    Delay_Ms(1);
    LCD_E = 0;
    Delay_Ms(1);
}

void LCD_Init(void)
{
    Delay_Ms(50);
    LCD_Write_Command(0x38);  // 8位数据接口,2行显示,5×7点阵
    Delay_Ms(5);
    LCD_Write_Command(0x0C);  // 显示开,光标关,闪烁关
    Delay_Ms(5);
    LCD_Write_Command(0x06);  // 写入后地址自动加1
    Delay_Ms(5);
    LCD_Write_Command(0x01);  // 清屏
    Delay_Ms(5);
}

void LCD_Clear(void)
{
    LCD_Write_Command(0x01);  // 清屏
    Delay_Ms(5);
}

void LCD_ShowChar(unsigned char x, unsigned char y, char ch)
{
    if(y == 0)
        LCD_Write_Command(0x80 + x);  // 第一行
    else
        LCD_Write_Command(0xC0 + x);  // 第二行
    
    LCD_Write_Data(ch);
}

void LCD_ShowString(unsigned char x, unsigned char y, char *str)
{
    unsigned char i = 0;
    
    if(y == 0)
        LCD_Write_Command(0x80 + x);  // 第一行
    else
        LCD_Write_Command(0xC0 + x);  // 第二行
    
    while(str[i] != '\0')
    {
        LCD_Write_Data(str[i]);
        i++;
    }
}

3.6 键盘扫描 (keyboard.h)

c 复制代码
#ifndef __KEYBOARD_H__
#define __KEYBOARD_H__

#include <reg52.h>

// 按键定义
#define KEY_START     0x01
#define KEY_STOP      0x02
#define KEY_FORWARD   0x03
#define KEY_BACKWARD  0x04
#define KEY_SPEED_UP  0x05
#define KEY_SPEED_DOWN 0x06
#define KEY_ANGLE_ADD 0x07
#define KEY_ANGLE_SUB 0x08
#define KEY_NONE      0xFF

// 函数声明
unsigned char Key_Scan(void);
void Key_Init(void);

#endif

3.7 键盘扫描 (keyboard.c)

c 复制代码
#include "keyboard.h"
#include "delay.h"

void Key_Init(void)
{
    // 按键端口初始化
    // P3.0-P3.7作为输入
}

unsigned char Key_Scan(void)
{
    unsigned char key_value = KEY_NONE;
    
    // 扫描P3口按键
    if(P3_0 == 0)  // 启动键
    {
        Delay_Ms(10);  // 消抖
        if(P3_0 == 0)
            key_value = KEY_START;
        while(P3_0 == 0);  // 等待松开
    }
    
    if(P3_1 == 0)  // 停止键
    {
        Delay_Ms(10);
        if(P3_1 == 0)
            key_value = KEY_STOP;
        while(P3_1 == 0);
    }
    
    if(P3_2 == 0)  // 正转键
    {
        Delay_Ms(10);
        if(P3_2 == 0)
            key_value = KEY_FORWARD;
        while(P3_2 == 0);
    }
    
    if(P3_3 == 0)  // 反转键
    {
        Delay_Ms(10);
        if(P3_3 == 0)
            key_value = KEY_BACKWARD;
        while(P3_3 == 0);
    }
    
    if(P3_4 == 0)  // 加速键
    {
        Delay_Ms(10);
        if(P3_4 == 0)
            key_value = KEY_SPEED_UP;
        while(P3_4 == 0);
    }
    
    if(P3_5 == 0)  // 减速键
    {
        Delay_Ms(10);
        if(P3_5 == 0)
            key_value = KEY_SPEED_DOWN;
        while(P3_5 == 0);
    }
    
    if(P3_6 == 0)  // 角度+
    {
        Delay_Ms(10);
        if(P3_6 == 0)
            key_value = KEY_ANGLE_ADD;
        while(P3_6 == 0);
    }
    
    if(P3_7 == 0)  // 角度-
    {
        Delay_Ms(10);
        if(P3_7 == 0)
            key_value = KEY_ANGLE_SUB;
        while(P3_7 == 0);
    }
    
    return key_value;
}

3.8 延时函数 (delay.h)

c 复制代码
#ifndef __DELAY_H__
#define __DELAY_H__

#include <reg52.h>

// 函数声明
void Delay_Ms(unsigned int ms);
void Delay_Us(unsigned int us);

#endif

3.9 延时函数 (delay.c)

c 复制代码
#include "delay.h"

// 毫秒延时
void Delay_Ms(unsigned int ms)
{
    unsigned int i, j;
    for(i = ms; i > 0; i--)
        for(j = 110; j > 0; j--);
}

// 微秒延时(粗略)
void Delay_Us(unsigned int us)
{
    while(us--)
    {
        _nop_();
        _nop_();
        _nop_();
        _nop_();
    }
}

四、Proteus仿真电路

4.1 仿真电路图

复制代码
+5V
 |
 +-+ ULN2003
 |  IN1 <- P1.0
 |  IN2 <- P1.1
 |  IN3 <- P1.2
 |  IN4 <- P1.3
 |  OUT1 -> 步进电机A
 |  OUT2 -> 步进电机B
 |  OUT3 -> 步进电机C
 |  OUT4 -> 步进电机D
 |  COM -> +5V
 +-+
 
 LCD1602
  VSS -> GND
  VDD -> +5V
  VO  -> 电位器中间
  RS  -> P2.0
  RW  -> P2.1
  E   -> P2.2
  D0-D7 -> P0.0-P0.7
  A   -> +5V (背光)
  K   -> GND
 
 按键
  K1 -> P3.0 -> GND (启动)
  K2 -> P3.1 -> GND (停止)
  K3 -> P3.2 -> GND (正转)
  K4 -> P3.3 -> GND (反转)
  K5 -> P3.4 -> GND (加速)
  K6 -> P3.5 -> GND (减速)
  K7 -> P3.6 -> GND (角度+)
  K8 -> P3.7 -> GND (角度-)

五、系统功能扩展

5.1 增加PWM调速

c 复制代码
// 使用定时器0产生PWM
void Timer0_Init(void)
{
    TMOD &= 0xF0;  // 清除T0控制位
    TMOD |= 0x01;  // 设置T0为模式1(16位定时器)
    TH0 = 0xFC;    // 1ms定时
    TL0 = 0x66;
    ET0 = 1;       // 允许T0中断
    EA = 1;        // 允许总中断
    TR0 = 1;       // 启动T0
}

// 定时器0中断服务程序
void Timer0_ISR(void) interrupt 1
{
    static unsigned int pwm_counter = 0;
    
    TH0 = 0xFC;    // 重装初值
    TL0 = 0x66;
    
    if(++pwm_counter >= motor_speed)
    {
        pwm_counter = 0;
        // 在这里控制步进电机转动
        if(motor_running)
        {
            // 步进电机控制代码
        }
    }
}

5.2 增加串口通信

c 复制代码
// 串口初始化
void UART_Init(void)
{
    TMOD &= 0x0F;  // 清除T1控制位
    TMOD |= 0x20;  // 设置T1为模式2(8位自动重载)
    TH1 = 0xFD;    // 波特率9600
    TL1 = 0xFD;
    TR1 = 1;       // 启动T1
    SM0 = 0;       // 设置串口为模式1
    SM1 = 1;
    REN = 1;       // 允许接收
    EA = 1;        // 允许总中断
    ES = 1;        // 允许串口中断
}

// 串口中断服务程序
void UART_ISR(void) interrupt 4
{
    unsigned char received_data;
    
    if(RI)  // 接收中断
    {
        RI = 0;  // 清除接收中断标志
        received_data = SBUF;
        
        // 根据接收到的数据控制步进电机
        switch(received_data)
        {
            case 'F':  // 正转
                sys_state = SYS_FORWARD;
                motor_running = 1;
                break;
            case 'B':  // 反转
                sys_state = SYS_BACKWARD;
                motor_running = 1;
                break;
            case 'S':  // 停止
                motor_running = 0;
                sys_state = SYS_STOP;
                break;
            case '+':  // 加速
                if(motor_speed > 200)
                    motor_speed -= 100;
                break;
            case '-':  // 减速
                if(motor_speed < 2000)
                    motor_speed += 100;
                break;
        }
    }
}

5.3 增加限位开关保护

c 复制代码
// 限位开关连接
sbit LIMIT_LEFT = P1^4;   // 左限位
sbit LIMIT_RIGHT = P1^5;  // 右限位

// 在电机控制函数中增加限位检测
void Motor_Control_With_Limit(void)
{
    if(LIMIT_LEFT == 0 && sys_state == SYS_BACKWARD)
    {
        motor_running = 0;  // 到达左限位,停止
        sys_state = SYS_STOP;
        LCD_ShowString(0, 0, "Limit:L");
    }
    
    if(LIMIT_RIGHT == 0 && sys_state == SYS_FORWARD)
    {
        motor_running = 0;  // 到达右限位,停止
        sys_state = SYS_STOP;
        LCD_ShowString(0, 0, "Limit:R");
    }
}

参考代码 基于单片机的步进电机控制性系统 www.youwenfan.com/contentcsv/117976.html

六、使用说明

6.1 操作步骤

  1. 接通5V电源
  2. 等待LCD显示初始化界面
  3. 按"启动"键开始运行
  4. 使用"正转"/"反转"键改变方向
  5. 使用"加速"/"减速"键调节速度
  6. 使用"角度+/-"设置目标角度
  7. 按"停止"键停止运行

6.2 注意事项

  1. 步进电机工作时电流较大,注意散热
  2. 不要长时间堵转,以免烧毁电机
  3. 限位开关必须正确连接,防止超程
  4. 电源电压稳定在5V±0.5V

七、调试技巧

7.1 常见问题解决

问题 解决方法
电机不转 检查ULN2003接线,测量各相电压
电机抖动 检查相序是否正确,调整延时时间
LCD不显示 检查对比度调节,确认初始化时序
按键失灵 检查上拉电阻,消抖处理
转速不稳 优化定时器中断,使用PWM控制

7.2 测试方法

  1. 单独测试LCD显示
  2. 单独测试按键输入
  3. 单独测试步进电机单步运行
  4. 逐步集成各模块功能
相关推荐
sramdram13 小时前
MCU微控制器OA打印机方案
单片机·嵌入式硬件·mcu·mcu微控制器
天天爱吃肉821814 小时前
【电源拆解:跟着问答逐一认识开关适配器PCB元器件】
大数据·人工智能·python·功能测试·嵌入式硬件·汽车
科芯创展17 小时前
3.5A,60VIN,异步降压芯片,XZ1815
单片机·嵌入式硬件
茯苓gao17 小时前
嵌入式开发笔记:Qt信号槽机制深度解析——从原理到实战的全方位指南
开发语言·笔记·嵌入式硬件·qt·学习
浅止菌18 小时前
海思回归第二弹:瑞芯微的隐忧与对手的反击
嵌入式硬件
Kuakewei88818 小时前
PW2609A在2.6A负载下压差0.14V,温升较3A工况低11°C
单片机·嵌入式硬件
Zyed19 小时前
[FreeRTOS]Day7项目介绍
stm32·单片机·嵌入式硬件
xuxie9920 小时前
start7 电机闭环控制
单片机·嵌入式硬件
嵌入式-老费20 小时前
esp32开发与应用(播放wav音频文件)
嵌入式硬件
LCG元21 小时前
嵌入式开发踩坑排查实战:从 STM32 外设初始化到 Linux SPI 驱动的 15 类硬件疑难故障深度复盘
linux·stm32·单片机