51单片机实现步进电机(39BYG316/28BYJ48)的启停,圈数设定,正反转,速率调整,LCD1602显示等调节
一、方案设计
方案1:采用89C51+ULN2003+28BYJ48,28BYJ48是一种常见的四相五线步进减速电机,其减速比为1/64,步进角1.8°,即如果采用四相四拍运行,若使步进电机转一周则需要64*(360/1.8)个脉冲;如果采用四相八拍运行,若使步进电机转一周则需要64*(360/0.9)个脉冲;
方案2:采用89C51+ULN2003+39BYG316,39BYG316是一种常见的四相五线步进电机,步进角1.8°,即如果采用四相四拍运行,若使步进电机转一周则需要6360/1.8个脉冲;如果采用四相八拍运行,若使步进电机转一周则需要360/0.9个脉冲;
二、硬件设计
硬件设计分为MCU模块、LCD和按键模块、电源模块、驱动模块共四大模块。
MCU模块采用较为常见的STC89C52RC,一般的淘宝店都能买到,设计时要特别注意P0口需要配置上拉电阻,晶振选取11.0592M的。
LCD和按键模块,四个按键的功能分别是K1启停,K2设定圈数,K3方向转换,K4速率调整,LCD选取LCD1602。
电源模块的设计比较简单,测试用的输入为7.2V电源,因此仅采用一稳压芯片AMS1117-5.0即可得到所需要的单片机供电电压,并添加了一个自锁开关方便及时断电,电源指示灯用于指示电源状态。
驱动模块采用最最常用的达林顿管驱动器ULN2003,并在输出端口设置四路LED显示当前驱动状态。
另外,步进电机的接线较为麻烦易错,28BYJ48在淘宝上有现成的解决办法,结合ULN2003驱动很容易接线;39BYG316电机也是在淘宝上买的,4.5元一个,接线仿照28BYJ48也很容易解决。
二、软件设计
首先,为了步进电机更好的运行,采用八拍的运行方式,给出四相八拍正反转表,如下:
//为了简便的宏定义
#define uchar unsigned char
#define uint unsigned int
//四相八拍反转表
uchar code FFW[8]={0x08,0x0c,0x04,0x06,0x02,0x03,0x01,0x09};
//四相八拍正转表
uchar code REV[8]={0x09,0x01,0x03,0x02,0x06,0x04,0x0c,0x08};
然后,按照硬件设计编写软件连接
sbit K1 = P3^2; // 启停
sbit K2 = P3^3; // 设定圈数
sbit K3 = P3^4; // 方向调整
sbit K4 = P3^5; // 速率调整
sbit LCD_RS = P2^6; // LCD连接
sbit LCD_RW = P2^5; // LCD连接
sbit LCD_EN = P2^7; // LCD连接
设置必须用的延时函数,为后面按键消抖,LCD1602通信等所使用
//空指令延时函数
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();};
/********************************************************/
/*延时 t ms函数
/********************************************************/
void delay(uint t)
{
uchar k;
while(t--)
{
for(k=0; k<125; k++)
{}
}
}
再然后设置LCD1602的通信
/********************************************************/
/* 检查LCD状态
/* lcd_busy=1 忙,等待
/* lcd_busy=0 闲,可写指令与数据
/********************************************************/
bit lcd_busy()
{
bit result;
LCD_RS = 0;
LCD_RW = 1;
LCD_EN = 1;
delayNOP();
result = (bit)(P0&0x80);
LCD_EN = 0;
return(result);
}
/********************************************************/
/* 写指令到LCD
/* RS=L , RW=L, E=高脉冲, D0-D7= 指令码
/*
/********************************************************/
void lcd_wcmd(uchar cmd)
{
while(lcd_busy());
LCD_RS = 0;
LCD_RW = 0;
LCD_EN = 0;
P0 = cmd;
delay(1);
LCD_EN = 1;
delay(5);
LCD_EN = 0;
}
/********************************************************/
/* 写显示数据到LCD
/* RS=H , RW=L, E=高脉冲, D0-D7= 数据
/*
/********************************************************/
void lcd_wdat(uchar dat)
{
while(lcd_busy());
LCD_RS = 1;
LCD_RW = 0;
LCD_EN = 0;
P0 = dat;
delay(1);
LCD_EN = 1;
delay(5);
LCD_EN = 0;
}
/********************************************************/
/* LCD 初始化
/*
/********************************************************/
void lcd_init()
{
lcd_wcmd(0x38);
lcd_wcmd(0x0c);
lcd_wcmd(0x06);
lcd_wcmd(0x01);
lcd_wcmd(0x80);
}
/********************************************************/
/* 设定显示位置
/*
/********************************************************/
void lcd_pos(uchar pos)
{
lcd_wcmd(pos | 0x80); // 数据指针 = 80 + 地址变量
}
然后设置LCD1602的初始化
//LCD1602 显示的状态数据
uchar code cdis1[ ] = {" STEPPING MOTOR "};
uchar code cdis2[ ] = {"CONTROL PROCESS"};
uchar code cdis3[ ] = {" STOP "};
uchar code cdis4[ ] = {"NUM: RATE:"};
uchar code cdis5[ ] = {" RUNNING "};
/********************************************************/
/* LCD1602 初始化
/*
/********************************************************/
void LCD_init_DIS()
{
delay(10);
lcd_init(); // 初始化 LCD
lcd_pos(0); // 设置为第一行第一位
m = 0;
while(cdis1[m] != '\0')
{
lcd_wdat(cdis1[m]);
m++;
}
lcd_pos(0x40); // 设置为第二行第一位
m = 0;
while(cdis2[m] != '\0')
{
lcd_wdat(cdis2[m]);
m++;
}
delay(3000); // 开机等待,给与提示!
lcd_pos(0); // 设置为第一行第一位
m = 0;
while(cdis3[m] != '\0')
{
lcd_wdat(cdis3[m]);
m++;
}
lcd_pos(0x40); // 设置为第二行第一位
m = 0;
while(cdis4[m] != '\0')
{
lcd_wdat(cdis4[m]);
m++;
}
for(m=0;m<2;m++)
{
lcd_pos(0x0c+m); // 显示方向符号
lcd_wdat(0x3e);
}
}
对于LCD中变化量的显示写了数据替换程序和数据显示程序
/********************************************************/
/*
/* 数据替换程序
/*
/********************************************************/
void data_conv()
{
data_temP1=data_temp/10; // 高位
if(data_temP1==0)
{data_temP1=0x20;} // 高位时0不显示
else
{data_temP1=data_temP1+0x30;}
data_temp2=data_temp%10; // 低位
data_temp2=data_temp2+0x30;
}
/********************************************************/
/*
/* 数据显示程序
/*
/********************************************************/
void data_dis()
{
data_temp = snum; // Ï显示圈数
data_conv();
lcd_pos(0x44);
lcd_wdat(data_temP1);
lcd_pos(0x45);
lcd_wdat(data_temp2);
data_temp = rate; // 显示速率
data_conv();
lcd_pos(0x4d);
lcd_wdat(data_temP1);
lcd_pos(0x4e);
lcd_wdat(data_temp2);
}
/********************************************************
/*
/* 显示运行方向符号
/*
/********************************************************/
void motor_DR()
{
if(direction==1) // 正转
{
for(m=0;m<2;m++)
{
lcd_pos(0x0c+m); // 显示正转
lcd_wdat(0x3e);
}
}
else // 反转
{
for(m=0;m<2;m++)
{
lcd_pos(0x0c+m); // 显示反转
lcd_wdat(0x3c);
}
}
}
/********************************************************
/*
/* 显示运行状态
/*
/********************************************************/
void motor_RUN()
{
if(on_off==1)
{
TR0=1;
lcd_pos(0); // 设置为第一行第一位
m = 0;
while(cdis5[m] != '\0')
{
lcd_wdat(cdis5[m]); //RUNNING
m++;
}
motor_DR();
}
else
{
TR0=0;
P1 =0x0f;
lcd_pos(0); // 设置为第一行第一位
m = 0;
while(cdis3[m] != '\0')
{
lcd_wdat(cdis3[m]); //STOP
m++;
}
motor_DR();
snum=snum1;
number1=0; // 清零圈数
}
}
使用定时器中断进行电机驱动,最快速度下每1ms发一次脉冲,即400ms即可转一圈
/********************************************************/
/*
/* 定时器0中断
/*
/********************************************************/
void motor_onoff() interrupt 1
{
TL0 = 0x18;
TH0 = 0xfc;
q++;
if(q < rate)
{ return; }
else
{
q=0;
number++; // 脉冲计数
if(number==400) //400 四相八拍,步距角1.8°,每360°/(1.8°/2)=400
//脉冲转一圈
{
snum--;
number=0;
number1++;
} // 电机转动圈数
if(direction==1) // 方向
{
if(v<8)
{P1 = FFW[v];v++;} // 正转
if(v==8)
{ v=0; }
}
else
{ if(v<8)
{P1 = REV[v];v++;} // 反转
if(v==8)
{ v=0; }
}
}
}
在主函数中进行按键扫描,控制电机
/********************************************************
*
* 主函数
*
*********************************************************/
main()
{
LCD_init_DIS();
TMOD = 0x01; // T0 定时 方式1
TL0 = 0x18;
TH0 = 0xfc; // (65536-64536)*1us=1ms
EA = 1;
ET0 = 1;
P1 = 0x0f; //电机驱动置1
while(1)
{
/********************************************************/
// 启停
if(K1==0)
{
delay(100);// 消抖
while(K1==0); // 待按键释放
on_off=~on_off;
motor_RUN();
} // K1 end
/********************************************************/
// 设定圈数
if(K2==0)
{
delay(100);// 消抖
if(K2==0)
{
if(snum_dr==1)
{
snum++;
snum1=snum;
if(snum==0x64)// 最大100圈
{ snum_dr=~snum_dr;}
}
else
{
snum--;
snum1=snum;
if(snum==0x01)
{ snum_dr=~snum_dr; }
}
}
} //K2 end
/********************************************************/
// 方向转换
if(K3==0)
{
delay(100);// 按键消抖
if(K3==0)
{
direction=~direction;
motor_DR();
}
}//K3 end
/********************************************************/
// 速率调整
if(K4==0)
{
delay(100);// 消抖
if(K4==0)
{
if(rate_dr==1)
{
rate++;
if(rate==0x0a)// 最大10档
{ rate_dr=~rate_dr;}
}
else
{
rate--;
if(rate==0x01)
{ rate_dr=~rate_dr; }
}
}
} //K4 end
/********************************************************/
if(number1==snum1) // 与设定圈数相等即宕机
{
number1=0;
on_off=0;
TR0=0;
snum=snum1;
P1=0x0f;
motor_RUN();
}
data_dis();
} // while(1) end
} //main end
至此,全部控制结束。可用51单片机实现步进电机(39BYG316/28BYJ48)的启停,圈数设定,正反转,速率调整,LCD1602显示等调节。
51单片机实现步进电机(39BYG316/28BYJ48)的启停,圈数设定,正反转,速率调整,LCD1602显示等调节硬件实现(原理图,PCB)+软件实现(keil4代码) 链接如下:
https://download.csdn.net/download/weixin_43058070/10998514