[普中51单片机]L298N驱动两路电机(PWM)

L298N驱动两路电机

目录

main.c

main.h

delay.c

delay.h

pwm.c

pwm.h

motor.c

motor.h


首先是题外话,我在高中就接触了STM32,可是在学校的课程确是从最基础的C51学起。

至此,已经快到了结课的时间了,所以改写一个比较喜欢的电机模块。

话不多说,首先是

main.c

#include "main.h"
#include "motor.h"

u8 status = 0;

void main(){
	L298_Init();
	Motor_Stop();
	Delay_ms(50);
	

	while(1)
	{
		Motor_Forward(100);
	}
}

因为只是驱动并没有写完这些功能,所以这只有延时一些时间然后前进。

main.h

其实是为了将u8,u16这些写在这,让main.c的代码量看起来少一些。

我在这使用的是typedef,当然也可以使用#define

注意以下,两者使用的方法: 

typedef unsigned char     u8;

typedef unsigned int        u16;

typedef unsigned long     u32;

#define u8        unsigned char

#define u16      unsigned int

#define u32      unsigned long

#ifndef	_MAIN_H
#define _MAIN_H

typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

/* Includes	*/
#include "regx52.h"

/* Private includes User */
#include "delay.h"

/* Exported functions prototypes */


#endif

delay.c

我阅读过大多数在CSDN上的一些51单片机开发的例程并不是一定需要delay.c和delay.h的,因为他们的delay延时函数通常是放在自己那个模块的顶部,或者是一个main.c的头部。这种事情看个人,毕竟每个人的编程风格不一样。

#include "delay.h"
#include "intrins.h"

void Delay_ms(unsigned int time)
{
	unsigned char data i, j;
	for(time;time>0;time--){
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}
}

void Delay_us(unsigned int time)		//@11.0592MHz
{
	unsigned char data i;
	for(time;time>0;time--){
		_nop_();
		i = 25;
		while (--i);
	}
}
	

我的延时函数使用stc-isp进行生成并进行修改,但是我建议用到多长的延时就生成多少的延时,毕竟51单片机不像stm32,有时候你觉得没bug或者是语法并没有错误,但是它就是给你报错。

delay.h

#ifndef _DELAY_H
#define _DELAY_H


void Delay_ms(unsigned int time);
void Delay_us(unsigned int time);

#endif // _DELAY_H

pwm.c

然后就是我使用了普中提供的PWM函数,因为我需要操控两个电机,也就是ENA和ENB两个使能端,所以我就多加了一个IO口分配给ENB,定义为PWM1。当然,毕竟是单片机,IO口大多是自己设置的,设置什么都可行,建议不要占用专用的IO口,如:

IO口特殊功能解析
P3.0RXD串口数据接收
P3.1TXD串口数据发送
P3.2INT0外部中断0
P3.3INT1外部中断1
P3.4T0定时器0或计数器0输入
P3.5T1定时器1或计数器1输入
P3.6WR外部RAM写
P3.7RD外部RAM读
#include "pwm.h"

//全局变量定义
u8 gtim_h=0;//保存定时器初值高8位
u8 gtim_l=0;//保存定时器初值低8位
u8 gduty=0;//保存PWM占空比
u8 gtim_scale=0;//保存PWM周期=定时器初值*tim_scale


/*******************************************************************************
* 函 数 名       : pwm_init
* 函数功能		 : PWM初始化函数
* 输    入       : tim_h:定时器高8位
				   tim_l:定时器低8位
				   tim_scale:PWM周期倍数:定时器初值*tim_scale
				   duty:PWM占空比(要小于等于tim_scale)
* 输    出    	 : 无
*******************************************************************************/
void pwm_init(u8 tim_h,u8 tim_l,u16 tim_scale,u8 duty)
{
	gtim_h=tim_h;//将传入的初值保存在全局变量中,方便中断函数继续调用
	gtim_l=tim_l;
	gduty=duty;
	gtim_scale=tim_scale;

	TMOD|=0X01;	//选择为定时器0模式,工作方式1
	TH0 = gtim_h;	//定时初值设置 
	TL0 = gtim_l;		
	ET0=1;//打开定时器0中断允许
	EA=1;//打开总中断
	TR0=1;//打开定时器
}

/*******************************************************************************
* 函 数 名       : pwm_set_duty_cycle
* 函数功能		 : PWM设置占空比
* 输    入       : duty:PWM占空比(要小于等于tim_scale)
* 输    出    	 : 无
*******************************************************************************/
void pwm_set_duty_cycle(u8 duty)
{
	gduty=duty;	
}

void pwm(void) interrupt 1	//定时器0中断函数
{
	static u16 time=0;

	TH0 = gtim_h;	//定时初值设置 
	TL0 = gtim_l;
	
	time++;
	if(time>=gtim_scale)//PWM周期=定时器初值*gtim_scale,重新开始计数
		time=0;
	if(time<=gduty)//占空比	
	{
		PWM=1;
		PWM1=1;
	}
	else
	{
		PWM=0;
		PWM1=0;
	}
				
}

像我这样直接把另外一个PWM加进来,会出现一些小情况。我更倾向于将两个PWM分别控制,因为可以一定程度上缓解驱动模块的两路输出不一致的问题,但是却无法完全根治。

pwm.h

#ifndef _PWM_H
#define _PWM_H

#include "main.h"


//管脚定义
sbit PWM=P2^2;
sbit PWM1=P2^5;

//变量声明
extern u8 gtim_scale;

//函数声明
void pwm_init(u8 tim_h,u8 tim_l,u16 tim_scale,u8 duty);
void pwm_set_duty_cycle(u8 duty);

#endif

最后是重头戏

motor.c

按键检测其实可以放其他地方的,因为是调试代码,所以可以删掉。亦可以转为红外或者蓝牙、WiFi等方式控制。

通过了解L298N的H桥逻辑,可以得出以下内容:

#include "motor.h"
#include "pwm.h"

sbit L298_IN1 = P2^0;
sbit L298_IN2 = P2^1;
sbit L298_ENA = P2^2;

sbit L298_IN3 = P2^3;
sbit L298_IN4 = P2^4;
sbit L298_ENB = P2^5;

sbit KEY1 = P3^1; //正反转切换
sbit KEY2 = P3^0;	//加速
sbit KEY3 = P3^2;	//减速
sbit KEY4 = P3^3;	//停止


void L298_Init()
{
	pwm_init(0xFF,0xF6,100,0);
	
}


void Motor_Forward(char Speed)
{
	L298_IN1 = 1;
	L298_IN2 = 0;
	L298_IN3 = 1;
	L298_IN4 = 0;

	pwm_set_duty_cycle(Speed);
}

void Motor_Retreat(char Speed)
{
	L298_IN1 = 0;
	L298_IN2 = 1;
	L298_IN3 = 0;
	L298_IN4 = 1;

	pwm_set_duty_cycle(Speed);
}

void Motor_TurnLeft(char Speed)
{
	L298_IN1 = 1;
	L298_IN2 = 0;
	L298_IN3 = 0;
	L298_IN4 = 0;

	pwm_set_duty_cycle(Speed);
}

void Motor_TurnRight(char Speed)
{
	L298_IN1 = 0;
	L298_IN2 = 0;
	L298_IN3 = 1;
	L298_IN4 = 0;

	pwm_set_duty_cycle(Speed);
}

void Motor_Stop()
{
	L298_IN1 = 0;
	L298_IN2 = 0;
	L298_IN3 = 0;
	L298_IN4 = 0;

	pwm_set_duty_cycle(0);
}

//返回0表示没有按键按下
//返回1表示按键KEY1被按下
//返回2表示按键KEY2被按下
//返回3表示按键KEY3被按下
u8 Key_Sacn()
{
	static unsigned char key_press_state = 0;//表示按键按下状态 0 表示按键没有按下  1 表示按键被按下
	//如果没有按键被按下
	if(key_press_state == 0)
	{
		//如果KEY1-KEY4任何一个被按下
		if(KEY1 == 0 || KEY2 == 0 ||KEY3 == 0 || KEY4 == 0)
		{
			//延时10ms
			Delay_ms(10);
			//逐个判断,确定具体是哪一个被按下
			if(KEY1 == 0)
			{
				key_press_state = 1;
				return 1;
			}
			if(KEY2 == 0)
			{
				key_press_state = 1;
				return 2;
			}
			if(KEY3 == 0)
			{
				key_press_state = 1;
				return 3;
			}
			if(KEY4 == 0)
			{
				key_press_state = 1;
				return 4;
			}
		}
	}
	//KEY1-KEY4全部松开,才能恢复状态
	if(KEY1 == 1 && KEY2==1 && KEY3 ==1 && KEY4 ==1)
	{
		key_press_state = 0;
	}
	
	return 0;
}

motor.h

#ifndef	_MOTOR_H
#define _MOTOR_H

/*	Includes	*/
#include "main.h"

/*	Private includes User	*/
#include "delay.h"
#include "regx52.h"

/*	Exported functions prototypes	*/
void L298_Init();
void Motor_Forward(char Speed);	//Speed(0-100)
void Motor_TurnRight(char Speed);
void Motor_TurnLeft(char Speed);
void Motor_Retreat(char Speed);
void Motor_Stop();
u8 Key_Scan();


#endif

总的来说,还是很简单的一个项目。可以多多锻炼,练多了就可以慢慢的读懂别人的代码。我认为还是得从基础的事物学起,因为可以知道比较底层的一些东西。

另外,本人有些厌恶二次开发,导致做一些项目的时候进度比别人慢的很多,因为需要搞懂工作原理,以及进行相对应的代码调试,直到能用的地步。不过我的性格是不能止步于能用,还得好用。

PWM使用的是普中提供的例程,并进行了小修改。

#一课一得#

  • 46
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值