基于51单片机的步进电机驱动,亲测无误


前言

这一次要分享的项目是最近接单做的一个小玩意儿,基于51单片机的步进电机驱动。最近积压了两个月的小项目会在后面陆续发出,好了回归正题。本次步进电机驱动的话主要实现的功能就是实现:

步进电机的加速和减速,正转反转和开启或者停止工作。用LCD1602显示当前工作转速及正反转状态,可通过按键控制电机的开启和关闭。


一、我们该如何实现电机驱动?

目前步进电机的驱动系统已经非常完善了,总结下来你只需要准备这几个信号即可:

脉冲信号:步进电机是脉冲驱动型,拿最常见的42步进电机来说,普通状态下它的步距角为1.8°。这个步距角的含义指的是,你给电机一个脉冲信号,步进电机就转动1.8°,而很明显我们转动一圈需要360°,也就是说我们给步进电机200个脉冲信号,就能够让步进电机转一圈。因此只需要单片机内部发出脉冲信号,根据你自己的需求,比如你想使步进电机转动速度变快,相应的增加脉冲的频率即可,在相同的时间内发出的脉冲数越多,你就可以转动的越快。

方向信号:顾名思义,你给高电平或者低电平,对应的步进电机转动的方向就为正方向或者反方向。你只需要设置好IO口输出状态,51单片机不用管,32单片机推挽输出即可。

脱机信号:这玩意儿我的评价是,暂时用不到,悬空就行。

其他的就是供电电源之类的准备,其实搞懂这些,就可以实现对步进电机的驱动了

二、驱动实现

1.硬件准备

  • 主要的硬件清单

1、AT89C51最小系统套件,兼容即可
2、LCD1602液晶显示屏
3、LM2596S-5.0稳压芯片
4、步进电机驱动器+42步进电机(我买的是一体式的)
5、供电电源(我用的是XH_2接口的12V电池接入

  • 接线说明

P1口的0-4号引脚均接入按键中,实现不同的功能
P3^7为正反转的控制位
P3^4为脉冲的输出位

  • 电路图
    在这里插入图片描述
  • PCB
    在这里插入图片描述

2.软件编写

  • 关键点

在代码中总共用到了两个定时器:

其中定时器一主要是为了刷新界面,每隔2s对界面进行刷新,注意此处是在步进电机不细分的情况下,对应的pulse_speed参量是每秒钟的脉冲总数
在这里插入图片描述
定时器二主要是对脉冲输出进行控制,work_degree为步进电机的档位,根据不同的档位选择不同的定时时间,也就是脉冲的频率,档位越高,定时器的时间就越短,相对应的频率也就越高,在此处还定义了一个flag变量,定义此变量的意义在于每执行一次定时器,输出电平就会反转。相当于输出脉冲的频率是此定时器的1/2,快乐加倍。
在这里插入图片描述

至于其他地方代码都是比较简洁明了的,也有相对应的注释。

  • 代码

main.c

#include<reg51.h>
#include"lcd.h"
#define uchar unsigned char
#define uint  unsigned int
typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;
void DelayMs(unsigned int );
/*******************************************************************************
* 函数名         : 变量定义
* 函数功能		   : 新的变量
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/
unsigned char num[4];             //显示每分钟多少转
unsigned char dangwei[1];         //档位的显示
unsigned int  pulse_speed=100;    //每分钟多少转
unsigned int  pulse_speed1=0;     //每分钟多少转
unsigned char code wenzi1[16]={"now_V:     r/min"};
unsigned char code wenzi2[16]={"status:         "};
unsigned char code zheng1[5]={"zheng"};
unsigned char code fan1[5]={"fan  "};
//占空比输出
uint flag=1;                      //计时增加值
u16 duty=1;                       //占空比
u16 work_degree=1;                //步进电机工作等级
u16 work_status=0;                //工作状态,为0的时候不工作,为1的时候工作
//电机控制
sbit status=P3^7;					        //正反转控制位
sbit out=P3^4;			              //脉冲输出位
//按键控制
sbit add=P1^0;				            //加速
sbit reduce=P1^1;		              //减速
sbit zheng=P1^2;			            //正转
sbit fan=P1^3;		                //反转
sbit start=P1^4;					        //开始或者停止工作

/*******************************************************************************
* 函 数 名         : delay
* 函数功能		   : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
	while(i--);	
}
/*******************************************************************************
* 函 数 名         : keypros
* 函数功能		   : 按键处理函数,判断按键K1是否按下
*******************************************************************************/
void keypros()
{
	if(add==0)		  //检测按键K1是否按下
	{	
		delay(1000);   //消除抖动 一般大约10ms
		if(add==0)	 //再次判断按键是否按下
		{
			work_degree+=1;
			if(work_degree==8)work_degree=7;
		}
		while(!add);	 //检测按键是否松开
	}	
	if(reduce==0)		  //检测按键K1是否按下
	{	
		delay(1000);   //消除抖动 一般大约10ms
		if(reduce==0)	 //再次判断按键是否按下
		{
			work_degree-=1;
			if(work_degree==0)work_degree=1;
		}
		while(!reduce);	 //检测按键是否松开
	}
	if(zheng==0)		  //检测按键K1是否按下
	{	
		delay(1000);   //消除抖动 一般大约10ms
		if(zheng==0)	 //再次判断按键是否按下
		{
			status=1;
		}
		while(!zheng);	 //检测按键是否松开
	}
	if(fan==0)		  //检测按键K1是否按下
	{	
		delay(1000);   //消除抖动 一般大约10ms
		if(fan==0)	 //再次判断按键是否按下
		{
			status=0;
		}
		while(!fan);	 //检测按键是否松开
	}
	if(start==0)		  //检测按键K1是否按下
	{	
		delay(1000);   //消除抖动 一般大约10ms
		if(start==0)	 //再次判断按键是否按下
		{
			work_status+=1;
			if(work_status>=2)work_status=0;
		}
		while(!start);	 //检测按键是否松开
	}	
}
/*******************************************************************************
* 函 数 名         : Timer0Init
* 函数功能		   : 定时器0初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer0Init()
{
	TMOD|=0X11;//选择为定时器0模式,工作方式1,仅用TR0打开启动。

	TH0=0XD8;	//给定时器赋初值,定时1ms
	TL0=0XF1;	
	ET0=1;//打开定时器0中断允许
	EA=1;//打开总中断
	TR0=1;//打开定时器			
}
/*******************************************************************************
* 函 数 名         : Timer1Init
* 函数功能		   : 定时器1初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer1Init()
{
	TMOD|=0X11;//选择为定时器1模式,工作方式1,仅用TR1打开启动。

	TH1=0X3C;	//给定时器赋初值,定时50ms
	TL1=0XB1;	
	ET1=1;//打开定时器1中断允许
	EA=1;//打开总中断
	TR1=1;//打开定时器			
}
/*******************************************************************************
* 函数名         : main
* 函数功能		   : 主函数
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/

void main()
{
	unsigned char i;
	Timer0Init();                     //定时器0初始化
	Timer1Init();                     //定时器1初始化
	LcdInit();
	LcdWriteCom(0x80);
	for(i=0;i<16;i++)
	{
		LcdWriteData(wenzi1[i]);	
	}
	LcdWriteCom(0x80+0x40);
	for(i=0;i<16;i++)
	{
		LcdWriteData(wenzi2[i]);	
	}
	while(1)
	{
		//按键处理
		keypros();
		//显示转速
//		pulse_speed=;
		num[0]=pulse_speed/1000;
	    num[1]=pulse_speed%1000/100;
        num[2]=pulse_speed%100/10;
        num[3]=pulse_speed%10;
		dangwei[0]=work_degree;
		if(num[0]>9)
		{
			LcdWriteCom(0x80+0x06);			//设置显示位置
			LcdWriteData(0x37+num[0]);	//将数值转换为该显示的ASCII码
		}
		else
		{
			LcdWriteCom(0x80+0x06);
			LcdWriteData(num[0]+0x30);	//将数值转换为该显示的ASCII码
		}
		
		if(num[1]>9)
		{
			LcdWriteCom(0x80+0x07);
			LcdWriteData(num[1]+0x37);		//将数值转换为该显示的ASCII码
		}
		else
		{
			LcdWriteCom(0x80+0x07);
			LcdWriteData(num[1]+0x30);		//将数值转换为该显示的ASCII码
		}	
		
		if(num[2]>9)
		{
			LcdWriteCom(0x80+0x08);
			LcdWriteData(num[2]+0x37);		//将数值转换为该显示的ASCII码
		}
		else
		{
			LcdWriteCom(0x80+0x08);
			LcdWriteData(num[2]+0x30);		//将数值转换为该显示的ASCII码
		}	

		if(num[3]>9)
		{
			LcdWriteCom(0x80+0x09);
			LcdWriteData(num[3]+0x37);		//将数值转换为该显示的ASCII码
		}
		else
		{
			LcdWriteCom(0x80+0x09);
			LcdWriteData(num[3]+0x30);		//将数值转换为该显示的ASCII码
		}					
		
		//显示正反转
		if(status==1)
		{
		  LcdWriteCom(0x80+0x47);
	    for(i=0;i<5;i++)
	    {
		    LcdWriteData(zheng1[i]);	
	    }
		}
		else
		{
			LcdWriteCom(0x80+0x47);
	    for(i=0;i<5;i++)
	    {
		    LcdWriteData(fan1[i]);	
	    }
		}
		
		//显示当前档位
		if(dangwei[0]>9)
		{
			LcdWriteCom(0x80+0x4F);
			LcdWriteData(dangwei[0]+0x37);		//将数值转换为该显示的ASCII码
		}
		else
		{
			LcdWriteCom(0x80+0x4F);
			LcdWriteData(dangwei[0]+0x30);		//将数值转换为该显示的ASCII码
		}	
	}
}									 
/*******************************************************************************
* 函 数 名         : void Timer0() interrupt 1
* 函数功能		   : 定时器0中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer0() interrupt 1
{
	static u16 i;
	TH0=0Xdc;	//给定时器赋初值,定时10ms
	TL0=0X00;
	i++;
	if(i==200)
	{
		if(work_degree==1)pulse_speed=50;
		else if(work_degree==2)pulse_speed=100;
		else if(work_degree==3)pulse_speed=200;
        else if(work_degree==4)pulse_speed=500;
		else if(work_degree==5)pulse_speed=1000;
		else if(work_degree==6)pulse_speed=2000;
		else if(work_degree==7)pulse_speed=4000;
		i=0;
	}	
}
/*******************************************************************************
* 函 数 名         : void Timer1() interrupt 3
* 函数功能		   : 定时器0中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Timer1() interrupt 3
{
	if(work_degree==1)TH1=0xD8,TL1=0xF1;//定时10ms
    else if(work_degree==2)TH1=0xEC,TL1=0x79;//定时5ms
    else if(work_degree==3)TH1=0xF6,TL1=0x3D;//定时2.5ms
	else if(work_degree==4)TH1=0xFC,TL1=0x19;//定时1ms
	else if(work_degree==5)TH1=0xFE,TL1=0x0D;//定时500us
	else if(work_degree==6)TH1=0xFF,TL1=0x07;//定时250us
	else if(work_degree==7)TH1=0xFF,TL1=0x84;//定时125us
	flag++;
	if(flag>1)
	{
     flag=0;
	}	
	if(work_status==1)
	{
		if(flag<duty)
		{
			out=1;
		}
		else
		{
			out=0;
		}
	}
	else
	{
		out=0;
	}
}

Lcd.c

#include"lcd.h"
/*******************************************************************************
* 函 数 名         : Lcd1602_Delay1ms
* 函数功能		   : 延时函数,延时1ms
* 输    入         : c
* 输    出         : 无
* 说    名         : 该函数是在12MHZ晶振下,12分频单片机的延时。
*******************************************************************************/

void Lcd1602_Delay1ms(uint c)   //误差 0us
{
    uchar a,b;
	for (; c>0; c--)
	{
		 for (b=199;b>0;b--)
		 {
		  	for(a=1;a>0;a--);
		 }      
	}
    	
}

/*******************************************************************************
* 函 数 名         : LcdWriteCom
* 函数功能		   : 向LCD写入一个字节的命令
* 输    入         : com
* 输    出         : 无
*******************************************************************************/
#ifndef 	LCD1602_4PINS	 //当没有定义这个LCD1602_4PINS时
void LcdWriteCom(uchar com)	  //写入命令
{
	LCD1602_E = 0;     //使能
	LCD1602_RS = 0;	   //选择发送命令
	LCD1602_RW = 0;	   //选择写入
	
	LCD1602_DATAPINS = com;     //放入命令
	Lcd1602_Delay1ms(1);		//等待数据稳定

	LCD1602_E = 1;	          //写入时序
	Lcd1602_Delay1ms(5);	  //保持时间
	LCD1602_E = 0;
}
#else 
void LcdWriteCom(uchar com)	  //写入命令
{
	LCD1602_E = 0;	 //使能清零
	LCD1602_RS = 0;	 //选择写入命令
	LCD1602_RW = 0;	 //选择写入

	LCD1602_DATAPINS = com;	//由于4位的接线是接到P0口的高四位,所以传送高四位不用改
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	 //写入时序
	Lcd1602_Delay1ms(5);
	LCD1602_E = 0;

//	Lcd1602_Delay1ms(1);
	LCD1602_DATAPINS = com << 4; //发送低四位
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	 //写入时序
	Lcd1602_Delay1ms(5);
	LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名         : LcdWriteData
* 函数功能		   : 向LCD写入一个字节的数据
* 输    入         : dat
* 输    出         : 无
*******************************************************************************/		   
#ifndef 	LCD1602_4PINS		   
void LcdWriteData(uchar dat)			//写入数据
{
	LCD1602_E = 0;	//使能清零
	LCD1602_RS = 1;	//选择输入数据
	LCD1602_RW = 0;	//选择写入

	LCD1602_DATAPINS = dat; //写入数据
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;   //写入时序
	Lcd1602_Delay1ms(5);   //保持时间
	LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat)			//写入数据
{
	LCD1602_E = 0;	  //使能清零
	LCD1602_RS = 1;	  //选择写入数据
	LCD1602_RW = 0;	  //选择写入

	LCD1602_DATAPINS = dat;	//由于4位的接线是接到P0口的高四位,所以传送高四位不用改
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	  //写入时序
	Lcd1602_Delay1ms(5);
	LCD1602_E = 0;

	LCD1602_DATAPINS = dat << 4; //写入低四位
	Lcd1602_Delay1ms(1);

	LCD1602_E = 1;	  //写入时序
	Lcd1602_Delay1ms(5);
	LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 数 名       : LcdInit()
* 函数功能		 : 初始化LCD屏
* 输    入       : 无
* 输    出       : 无
*******************************************************************************/		   
#ifndef		LCD1602_4PINS
void LcdInit()						  //LCD初始化子程序
{
 	LcdWriteCom(0x38);  //开显示
	LcdWriteCom(0x0c);  //开显示不显示光标
	LcdWriteCom(0x06);  //写一个指针加1
	LcdWriteCom(0x01);  //清屏
	LcdWriteCom(0x80);  //设置数据指针起点
}
#else
void LcdInit()						  //LCD初始化子程序
{
	LcdWriteCom(0x32);	 //将8位总线转为4位总线
	LcdWriteCom(0x28);	 //在四位线下的初始化
	LcdWriteCom(0x0c);  //开显示不显示光标
	LcdWriteCom(0x06);  //写一个指针加1
	LcdWriteCom(0x01);  //清屏
	LcdWriteCom(0x80);  //设置数据指针起点
}
#endif

lcd.h

#ifndef __LCD_H_
#define __LCD_H_
/**********************************
当使用的是4位数据传输的时候定义,
使用8位取消这个定义
**********************************/
//#define LCD1602_4PINS

/**********************************
包含头文件
**********************************/
#include<reg51.h>

//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint 
#define uint unsigned int
#endif

/**********************************
PIN口定义
**********************************/
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^5;
sbit LCD1602_RW=P2^6;
sbit LCD1602_RS=P2^7;

/**********************************
函数声明
**********************************/
/*在51单片机12MHZ时钟下的延时函数*/
void Lcd1602_Delay1ms(uint c);   //误差 0us
/*LCD1602写入8位命令子函数*/
void LcdWriteCom(uchar com);
/*LCD1602写入8位数据子函数*/	
void LcdWriteData(uchar dat)	;
/*LCD1602初始化子程序*/		
void LcdInit();						  

#endif

需要注意的是,虽然lcd的代码是基于普中科技的实验例程修正过来的,但是对于RS\RW\EN三个接口的引脚有所更改,建议复制粘贴我的lcd代码

3.实物

在这里插入图片描述
在这里插入图片描述
实物视频请移步我的个人主页视频去看


总结

冲冲冲,步进电机驱动很简单,大家可以在此基础上结合其他模块玩一些好玩的东西,稍后代码和资料会发布到我的资源中

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值