基于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)
#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)
#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)
#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)
#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)
#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)
#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)
#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)
#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)
#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调速
// 使用定时器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 增加串口通信
// 串口初始化
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 增加限位开关保护
// 限位开关连接
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 操作步骤
- 接通5V电源
- 等待LCD显示初始化界面
- 按"启动"键开始运行
- 使用"正转"/"反转"键改变方向
- 使用"加速"/"减速"键调节速度
- 使用"角度+/-"设置目标角度
- 按"停止"键停止运行
6.2 注意事项
- 步进电机工作时电流较大,注意散热
- 不要长时间堵转,以免烧毁电机
- 限位开关必须正确连接,防止超程
- 电源电压稳定在5V±0.5V
七、调试技巧
7.1 常见问题解决
| 问题 |
解决方法 |
| 电机不转 |
检查ULN2003接线,测量各相电压 |
| 电机抖动 |
检查相序是否正确,调整延时时间 |
| LCD不显示 |
检查对比度调节,确认初始化时序 |
| 按键失灵 |
检查上拉电阻,消抖处理 |
| 转速不稳 |
优化定时器中断,使用PWM控制 |
7.2 测试方法
- 单独测试LCD显示
- 单独测试按键输入
- 单独测试步进电机单步运行
- 逐步集成各模块功能