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

基于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. 逐步集成各模块功能
相关推荐
灯琰11 小时前
STM32L051K6U6 IAP要点记录-LL库
stm32·单片机·嵌入式硬件
MAR-Sky2 小时前
stc8h系列单片机使用中断号超过32的插件解决办法
单片机·嵌入式硬件
结城明日奈是我老婆3 小时前
基于stm32f103c8t6最小系统板俩块版通讯
stm32·单片机·嵌入式硬件
weixin_456808383 小时前
【沁恒蓝牙开发】从机判断主机是否使能CCCD
单片机·嵌入式硬件
深圳英康仕3 小时前
一款面向AGV智能搬运机器人的RK3588工控机的数据资料整理
嵌入式硬件·rk3588·工控机·agv·智能搬运机器人
fengfuyao9853 小时前
STM32F030 SD卡文件系统读取实例
stm32·单片机·嵌入式硬件
kebidaixu3 小时前
FreeRTOS 移植到 STM32F407VETX 记录(三)
stm32·单片机·嵌入式硬件
普中科技14 小时前
【普中STM32F1xx开发攻略--标准库版】-- 第 45 章 FSMC-外扩 SRAM 实验
stm32·单片机·嵌入式硬件·fsmc·普中科技·外扩sram·is62wv51216