基于51单片机的电机(L298芯片,PWM调速,可加速、减速、正转、反转)

        电机是我们日常生活中比较常用的一个工具,我们经常需要使用电机来转动和拖动,完成一些任务。本次设计便用基于51单片机完成的电机转动与调速,使得电机可以正常启停、加速、减速以及正反转。

        文章末尾提供资源免费下载,包括proteus仿真文件与源码。

        51单片机的最大电压为5V,最大输出电流为10mA。可想而知,我们不可能依靠单片机引脚去驱动电机,因此我们需要借助电机驱动芯片来完成这项艰难的任务。L289芯片在protues中长这个样子:

                       db5dc574ab404607befc22baf07a73a9.png

        乍一看,这东西看着挺复杂,这么多引脚。其实,仔细研究过就会知道,这东西很简单。其他博主有很详细的介绍L298的引脚功能,这里就不讲,简单说下本次用到的。IN1和IN2是用来控制启停和正反转的,IN1为1,IN2为0就正转,反之则为反转,全为0或全1则制动,同时,这也是PWM输入入口,控制电机转速。ENA引脚为1,表示可以PWM调速。VCC为工作电压,5V,VS为驱动电压12V,OUT1和OUT2表示输出,连接电机正负极,1接正,2接负。此处为网上找到的资源,我测试过可以用,如有纰漏,欢迎指正。

下面介绍本次设计的主角,带编码器的直流电机。protues中,它长这个样子:

                                         9e361c8db857420fbd1bdf2e0e3ad41a.png

左右两边的“手”便是它的正负极,我们将他两与L298的OUT1与OUT2相连。上面的三根头发,左右两边根据设定值,每圈发出固定数量的脉冲,我们根据脉冲数即可知道转了几圈。本次设计中,每圈输出24个脉冲。那么在哪里修改呢?双击电机,可以看到下图:

                              38b892c60e0d462c9a6c5c2ac7487038.png

相信你看到了24了吧,该怎么改,你自己决定。那电机最中间的头发呢?据说是每圈一个脉冲,感兴趣的同学可以试试。电机最下面的绿色屏幕就是转速了,单位是r/min。

至于脉冲数的统计,这里提供两个思路,一个是计数器,一个是外部中断。我们可以将其中一个定时器做计数器,计算脉冲,另一个计时,计算转速。另外一种是通过外部中断统计脉冲数,定时器计时,获得转速。

至于PWM的生成方法,我们可以这么想。IO口有两种状态,1和0。我用定时器计100us,70us我r让一个IO口为1,30us为0,我不就得到了一串方波,周期100us。程序实现就是设置一个变量comper,作为比较量,另一个变量count_pwm为累加量,10us加1,大于comper为1,小于为0,ok,得到了可调PWM。

下面是具体实现,每段代码,每个变量都有详细注释,同时欢迎留言询问,源码0error,0warning。

(1)定时器部分(timer.c)定时器T0计时,T1产生PWM

#include <reg52.h>
/************************************
函数名:timer_Init
功能:定时器初始化,T0用作50ms定时,
	  T1用10us定时
形参:无
返回值:无
*************************************/
void timer_Init()
{
	TMOD = 0x11;
	TL0 = 0xB0;				//设置定时初始值50ms
	TH0 = 0x3C;				//设置定时初始值
	TL1 = 0xF6;				//设置定时初始值10us
	TH1 = 0xFF;				//设置定时初始值
	TF0 = 0;				//清除溢出标志
	TF1 = 1;
	ET0 = 1;				//定时中断允许
	ET1 = 1;
	EA = 1;					//总中断
	IT0 =1;					//外部中断
	EX0 =1;					//下降沿出发
	TR0 = 1;				//定时器开
	TR1 = 0;
}

 timer.h

#ifndef __TIMER_H
#define __TIMER_H
void timer_Init();
#endif

 (2)按键部分,控制电机启停、正反转、加速、减速

key.c

#include <reg52.h>
#include <intrins.h>            //nop所在头文件
#include "key.h"				//按键头文件
/***********************************
函数名:delay_key
功能:5ms延时,用于按键消抖,此处是
为了protues仿真速度,实际应为20ms
形参:无
返回值:无
***********************************/
void delay_key()
{
	unsigned char i, j;

	i = 6;
	j = 211;
	do
	{
		while (--j);
	} while (--i);
}
/***********************************
函数名:key_pron
功能:按键处理函数(k1:开始,k2:停止
k3:加速,k4:减速 ,k5:正转,k6:反转
形参:无
返回值:无
*************************************/
void key_pron()
{
	if(K1 == 0)            //开始
	{
		delay_key();		//消抖
		if(K1 == 0)
		{
			TR1=1;			//启动定时器
			ENA = 1;
			motor_flag = 0;
			IN2 = 0;
			comper = 50;//初始转速
			while(K1 == 0)  //松手检测
			{
				SMG_display();
			}
		}
	}
	
	if(K2 == 0)				//停止
	{
		delay_key();		//消抖
		if(K2 == 0)
		{
			TR1 = 0;		//停止定时器
			IN1 = 0;
			IN2 = 0;
			comper = 50;  //初始转速
			while(K2 == 0)	//松手检测
			{
				SMG_display();
			}
		}
	}
	
	if(K3 == 0)				//加速
	{
		delay_key();
		if(K3 == 0)
		{
			comper=comper-10;	//增加占空比
			if(comper<0)		//上限
				comper = 0;
			while(K3 == 0)
			{
				SMG_display();
			}
		}
	}
	
	if(K4 == 0)				//减速
	{
		delay_key();
		if(K4 == 0)
		{
			comper=comper+10;//减小占空比
			if(comper >100)//下限
				comper = 100;
			while(K4 == 0)
			{
				SMG_display();
			}
		}
	}
	
	if(K5 == 0)				//正转
	{
		delay_key();
		if(K5 == 0)
		{
			motor_flag = 0;   //正转标志
			ENA = 1;
			IN2 = 0;
			while(K5 == 0)
			{
				SMG_display();
			}
		}
	}
	
	if(K6 == 0)				//反转
	{
		delay_key();
		if(K6 == 0)
		{
			motor_flag = 1; //反转标志
			ENA = 1;
			IN1 = 0;
			while(K6 == 0)
			{
				SMG_display();
			}
		}
	}
}

key.h

#ifndef __KEY_H
#define __KEY_H
//按键
sbit K1 = P1^0;
sbit K2 = P1^1;
sbit K3 = P1^2;
sbit K4 = P1^3;
sbit K5 = P1^4;
sbit K6 = P1^5;

sbit ENA = P2^5;
sbit IN1 = P2^3;
sbit IN2 = P2^4;
extern bit motor_flag;
extern void SMG_display();
void delay_key();
void key_pron();
extern char comper;					//PWM占空比调节
#endif

(3)数码管显示部分,采取共阴四位数码管,保留一位小数

smg.c

#include <reg52.h>
#include "seg.h"
unsigned char code SMG_NODOT[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//无小数点
unsigned char code SMG_DOT[10]= {0xbf,0x86,0xdb,0xcf,0x6e6,0xed,0xfd,0x87,0xff,0xef};//有小数点
/*************************************
函数名:SMG_Init
功能:数码管初始化函数
形参:pos:显示位 value:显示值
返回值:无
**************************************/
void SMG_Init(unsigned char pos,unsigned char value)
{
	unsigned char dat = 0x80;
	P3 = P3&(~(dat >> pos));
	P0=value;
	delay(2);
	P0 = 0x00;
	P3 = P3|0xf0;
}
/***********************************
函数名:SMG_display
功能:数码管显示函数
形参:无
返回值:无
************************************/
void SMG_display()
{
	SMG_Init(0,SMG_NODOT[speed/1000]);
	SMG_Init(1,SMG_NODOT[(speed%1000)/100]);
	SMG_Init(2,SMG_DOT[(speed%100)/10]);
	SMG_Init(3,SMG_NODOT[speed%10]);
}

smg.h

#ifndef __SEG_H
#define __SEG_H
extern void delay(unsigned char t);
extern unsigned int speed;
void SMG_Init(unsigned char pos,unsigned char value);
void SMG_display();
#endif

(4)主函数main.c

/**********************************************************
Author   :sakura
Time     :2023/4/22
Funcation:51单片机使用L298电机芯片驱动带编码器的电机,
		  通过编码器测量转速,同时使用LED数码管显示,
		  按键可使电机正反转并改变速度
***********************************************************/
//头文件
#include <reg52.h>				//51单片机头文件
#include <intrins.h>            //nop所在头文件
#include "main.h"				//主函数头文件
#include "timer.h"				//定时器头文件
#include "key.h"				//按键头文件
#include "seg.h"				//数码管头文件
/*************************************
函数名:main
功能:主函数入口
形参:无
返回值:无
**************************************/
void main()
{
	timer_Init();			//定时器初始化
	IN1 = 1;
	IN2 = 1;
	while(1)
	{
		SMG_display();		//数码管显示
		key_pron();			//按键扫描
	}
}
/***********************************
函数名:INT0_isr
功能:外部中断0入口,统计编码器脉冲
形参:无
返回值:无
************************************/
void INT0_isr() interrupt 0
{
	count_f++;//每秒脉冲数
}
/*****************************
函数名:timer0_isr
功能:定时器0中断入口,定时50ms
		每秒计算一次转速
形参:无
返回值:无
*****************************/
void timer0_isr() interrupt 1
{
	TL0 = 0xB0;				//设置定时初始值50ms
	TH0 = 0x3C;				//设置定时初始值
	count_50ms++;
	if(count_50ms == 20)
	{
		count_motor = count_f + count_motor;
		count_f = 0;
	}
	if(count_50ms == 40)
	{
		count_50ms = 0;
		count_motor = count_f + count_motor;
		speed_real = (count_motor * 0.0416666);//计算每秒圈数,每圈24个秒冲																//总脉冲/24 = 圈数
		speed = speed_real*300;//每秒转圈数(一位小数)
		count_f = 0;
		count_motor = 0;
	}
}
/*******************************
函数名:timer1_isr
功能:定时器1中断入口,定时10us,
	用于产生PWM波
形参:无
返回值:无
*******************************/
void timer1_isr() interrupt 3
{
	TL1 = 0xF6;				//设置定时初始值
	TH1 = 0xFF;				//设置定时初始值
	count_pwm ++;
	if(count_pwm == 100)
		count_pwm = 0;
	if(count_pwm>comper)
	{
		if(motor_flag == 0)  //正转对IN1输出PWM
			IN1 = 1;
		else				//反转对IN2输出PWM
			IN2 = 1;
	}
	else
	{
		if(motor_flag == 0)  
			IN1 = 0;
		else
			IN2 = 0;
	}
		
}

main.h

#ifndef __MAIN_H
#define __MAIN_H
#include <reg52.h>
#include <intrins.h>            		//nop所在头文件
//变量
unsigned char count_f = 0;				//编码器脉冲计数
unsigned char count_50ms = 0;			//50ms计数
char comper = 50;						//PWM占空比调节
float speed_real = 0;					//转速中间变量
unsigned int speed = 0;					//转速,r/min
unsigned char count_pwm = 0;			//pwm统计
bit motor_flag = 0;						//正反转标志
unsigned char count_motor = 0;			//滤波变量
//函数声明
void delay(unsigned char t);					//延时函数
/*************************************
函数名:delay
功能:t*10us延时
形参:t:延时次数
返回值:无
*************************************/
void delay(unsigned char t)
{
	unsigned char i;
	while(t--)
	{
		_nop_();
		i = 2;
		while (--i);
	}
}

#endif

以上就是全部代码了。仿真结果如下:

54a4f57a3d3d4d9795da5a180e07d8b3.png

 实际转速81.4,数码管81.2,低速误差是很小的。至于其他比如正反转、加减速不好展示,欢迎大家下载资源测试,仅需您小小的一个赞。

链接:https://pan.baidu.com/s/1A_J6M07DGOFI9C_JDM_NPQ?pwd=ae1v 
提取码:ae1v

 

  • 51
    点赞
  • 101
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sakura(划水)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值