51单片机蓝牙遥控小车 定时器 中断 PWM L298N调速控制

闲时偷学51一周 完成蓝牙小车定时器中断PWM调速控制

关键问题: 51蓝牙串口通信、定时器、中断、L298N、PWM调速

  • 先把我学习过程中的问题摆出来:89C52仅有的两个定时器在使用串口时的干扰问题,PWM产生
  • 一开始找资料看别人51控制L298N的例子,PWM调速的比较少,有的自己试了结果不太理想;
  • 还有就是 初来乍到,哪里有不对的地方 欢迎指正!

操作说明:

  • 打开蓝牙调试器,对应设置好控制字符按键名
  • 打开小车电源并将手机连接到蓝牙模块进行控制

一、前期硬件准备

  1. TT电机小车底板套件,随便买一个两个电机的51单片机蓝牙遥控小车 定时器 中断 PWM L298N调速控制_第1张图片

  2. 蓝牙模块(我是在淘宝找便宜便买的,啊?买HC-06毕竟咱穷呀~)默认波特率9600一定要对应单片机串口通信波特率51单片机蓝牙遥控小车 定时器 中断 PWM L298N调速控制_第2张图片
    单片机-----蓝牙模块连线:

  • RXD/P3.0----TXD *********** GND----GND
  • TXD/P3.1----RXD ***** VCC/5V/3V- -VCC/5V/3V
  • 用到的蓝牙软件:蓝牙调试器 一般手机应用商店可下载,我的按键设置51单片机蓝牙遥控小车 定时器 中断 PWM L298N调速控制_第3张图片
  1. STC89C52或者STC89C52RC最小系统板51单片机蓝牙遥控小车 定时器 中断 PWM L298N调速控制_第4张图片

  2. L298N电机驱动、杜邦线等。本次只用到298N的四个逻辑引脚,其与单片机具体连接看程序定义51单片机蓝牙遥控小车 定时器 中断 PWM L298N调速控制_第5张图片

  3. 5-12V电池,注意与298N正确连接,通过L298N的板载5V使能输出(插上跳线帽时5V输入端有5V输出)给单片机和蓝牙模块供电。

  4. 其他,还需要用到USB转TTL给单片机下载程序;TTL如图:51单片机蓝牙遥控小车 定时器 中断 PWM L298N调速控制_第6张图片

    最后就是编程环境 arm Keil了 。

二、51C语言程序编写

  • 废话不多说,看看怎么用当今官方都不建议使用的STC89C52这块单片机来实现蓝牙通信,定时器中断PWM调速小车的主体程序吧。
这里掏出我第一版的部分代码://就是为了举个例子,写的什么玩意? 后来的自己都看不下去了

/*  * * 反面教材,大家笑笑就行了 ~ ~  * * *
**************************************************
*用延时函数模拟PWM输出,但后面实验发现这玩意太LOW了~
*运行下去都是个问题
**************************************************/
#include <reg52.h> 
#define uchar unsigned char
#define uint  unsigned int
sbit ENA=P1^0;		//使能端口A和B
sbit ENB=P1^5;				
sbit IN1=P1^1;		//以下四个逻辑端口
sbit IN2=P1^2;
sbit IN3=P1^3;
sbit IN4=P1^4;
void delay(uint n)	//延时函数
	{
     
 uint x,y;
 for(x=n;x>0;x--)
 for(y=114;y>0;y--);
}
void Speed1(uint b) // 定义一个速度函数模拟PWM输出 形参b
{
     
  ENB=1; 			// ENB使能或给高电平
 delay(b); 			//  延时
  ENB=0;  		 	//  ENB失能或给低电平
 delay(100-b);		
}
void Speed(uint a)  // 定义一个速度函数模拟PWM输出 形参
{
     
  ENA=1; 			// ENA使能或给高电平
 delay(a); 		    //  延时
  ENA=0;  		    //  ENA失能或给低电平
 delay(100-a);		
}
//L298N逻辑函数,控制电机正反转以及速度大小
void stop()		//小车停止函数
{
     
	IN1=0;
	IN2=0;
	IN3=0;
	IN4=0;
}
void forward()	//小车前进函数
{
     
	IN1=1;
	IN2=0;
	IN3=1;
	IN4=0;
  Speed(30);
	Speed1(30);
}
void back()	   //小车后退函数
{
     
	IN1=0;
	IN2=1;
	IN3=0;
	IN4=1;
	Speed(30);
	Speed1(30);
}
void left()	  //小车左转函数
{
     
	IN1=0;
	IN2=1;
	IN3=1;
	IN4=0;
	Speed(30);
	Speed1(30);
}
void right()	 //小车左转函数
{
     
	IN1=1;
	IN2=0;
	IN3=0;
	IN4=1;
	Speed(30);
	Speed1(30);
}
void UsartConfiguration()			//串口初始化
{
     	
	SCON=0X50;	//SCON 是一个特殊功能寄存器,用以设定串行口的工作方式	
	TMOD=0X20;	//和接收/发送控制以及设置状态标志			
	PCON=0X00;		
	TH1=0Xfd;		    
	TL1=0Xfd;   //波特率为9600,计算所得
	TR1= 1;					
	ES = 1;         //开启串口中断
    	EA = 1;     //开启总中断    
}
void Com_Int() interrupt 4
{
     
	uchar receive_data;	
	//EA = 0;
	if(RI == 1) //一次数据接收完成后RI由硬件自动置1,作接收完成的标志
	{
      				//初始设置电机停止
		ENA=0;	//PWM使能端口失能,电机停转
		ENB=0;
		receive_data = SBUF;		//将收/发数据缓存器SBUF中的数据存入变量	
		/*****电机逻辑判断函数*****/
		switch(receive_data)
		{
     
			case ('F'): forward();
				break;	
			case ('B'): back(); 
				break;	
			case ('L'): left();  
				break;
			case ('R'): right(); 
				break;
			case ('S'): stop();  
			  break;
		}	
	}	
		RI=0;
		EA = 1;			//开启总中断
}
void main() 	  //主函数
{
      
	UsartConfiguration();	
	while(1);
}
  • 由于我头一回的草率,后面又专门在学习网站~哔哩哔哩找了51定时器、中断系统的课看了几遍,哎枯燥难耐的我竟然还认真地做了笔记—但后面自己试验的多了,又觉得那些笔记记得都是废话,因为你永远不会在了解它之前觉得某个问题产生的几率有多大。
  • 关于51 89系列的芯片仅有的两个定时器不能同时工作的问题 :也许不知道是我看的教程有问题还是,为了考验学习者,如果某些教程书里有,当我没说。
    解决办法来源:当两个定时器只有一个工作另一个罢工时、当我实在没辙的时候,低头之际突然想到了来CSDN瞧瞧。在浏览了众多关于定时器的文章里,哎~~ 我TM终于找到了原因—两个定时器同时使用,在中断时会产生干扰,关键就在定时器中断控制寄存器这,程序里会提到。哎~~说多了都是泪啊,硬用时间换来的呢!?!!

以下 直接掏出我呕心沥血的最终代码

/**********************************************************
*	用定时器中断实现PWM输出控制L298N的蓝牙小车/51两个定时器同时使用冲突问题
*	单 片 机:STC89C52/STC89C52RC
*	作   者 :GUard_Byte
*	*	*	*	*	*	*	*	*   *   *   *   *   *   *   *		
*			单片机与蓝牙模块连线:
*	RXD----TXD			 GND----GND
*	TXD----RXD  	VCC/5V/3V--VCC/5V/3V
*
*  关键问题:中断系统优先级TMOD|与串口中断的设置,或运算使得后面中断时前面赋值不被改变
*  保证两个定时器不干扰,同时工作
***********************************************************/

#include <reg52.h> 
typedef unsigned int uint;
typedef unsigned char uchar;
// PWM输出I/O口定义与298N连接,注意我使用的最小系统P0有上拉电阻可置高,若无上拉电阻请换至其他
sbit PWM1 = P0^1;
sbit PWM2 = P0^2;
sbit PWM3 = P0^3;
sbit PWM4 = P0^4;

uchar receive_data;			//储存串口接收的数据
uint count0 = 0;			// 定时器T0中的计时
uint count1 = 0;			// 定时器T1中的计时
uint  flag1 = 0;			// 电机1正反转的标志位
uint  flag2 = 0;			// 电机2正反转的标志位
uint  rate1;				// 电机速度等级, 共10级
uint  rate2;				// 电机速度等级, 共10级

/*******************************************************
*		函数名:void Time0_Int()
*		功  能:定时器0初始化,设定定时器0工作方式和初值
*******************************************************/
void Time0_Int()
{
     
    EA = 1;		          // 开启总中断
	EX0 = 1;	          // 允许外部中断0中断
	IT0 = 1;	          // 下降沿触发中断
	ET0 = 1;	          // 定时器0中断
	TH0 = (65536 - 10) / 256;		// 赋初值, 10us
	TL0 = (65536 - 10) % 256;
	TMOD|= 0x01;	      // 选择方式一
	TR0 = 1;			  // 打开定时器0中断
}
/*******************************************************
*		函数名:void T0_inter() ;中断号  1	
*		功  能:定时器0中断服务,及计数 PWM产生
*	+++++ PWM产生的部分思路借鉴网络 本来想贴链接,啊~后来找不到原文了
*	                         求原谅!++++++
*******************************************************/
void T0_inter() interrupt 1		
{
      
	TR0 = 0;
	TH0 = (65536 - 10)/256;
	TL0 = (65536 - 10)%256;
	TR0 = 1;
	count0 ++;
	if (count0 >= 100)
	{
     
		count0 = 0;
	}
	if (count0 < ( rate1 * 10))
	{
     
		if ( flag1 == 0)
		{
     
			PWM1 = 1;
			PWM2 = 0;
		}
		else
		{
     
			PWM1 = 0;
			PWM2 = 1;
		}
	}	
	else
	{
     
		PWM1 = 1;
		PWM2 = 1;
	}
	if (count0 < ( rate2 * 10))
	{
     
		if (flag2 == 0)
		{
     
			PWM3 = 1;
			PWM4 = 0;
		}
		else
		{
     
			PWM3 = 0;
			PWM4 = 1;
		}
	}
	else
	{
     
		PWM3 = 1;
		PWM4 = 1;
	}
}
// 小车前进函数, 速率为3
void forward()
{
      
	rate1 = rate2 = 2;
	flag1 = flag2 = 1;
}
// 小车后退函数, 速率为3  
void back()
{
     
	rate1 = rate2 = 2;
	flag1 = flag2 = 0;	
}
//小车右急转弯函数, 左边电机正转, 右边电机反转,行进过程有效
void turn_left()
{
     
	flag1 = 1;
	flag2 = 0;
}
// 小车左急转弯函数, 右边电机正转, 左边电机反转,行进过程有效
void turn_right()
{
     			
	flag1 = 0;
	flag2 = 1;
}
// 右边电机加速,实现左转
void right_add()
{
     	
	if (rate1 == 5)
	{
     
		rate1 = 3;
	} 
	  rate1++;	 
}
// 左边电机加速,实现右转
void left_add()
{
     
	if (rate2 == 5)
	{
     
		rate2 = 3;
	}
	rate2++;	 
}
// 电机加速,最大10,此处限制到5级
void moter_add()
{
     	
	if ((rate1==5)&&(rate2 == 5))
	{
     
		rate1 = 2;
		rate2 = 2;
	} 
	  rate1++;	
	  rate2++;
}
// 电机减速,最小为2
void moter_less()
{
     	
	if ((rate1==1)&&(rate2 == 1))
	{
     
		rate1 = 5;
		rate2 = 5;
	} 
	  rate1--;	
	  rate2--;
}
// 小车停止函数
void stop()
{
     
	rate1 = rate2 = 0;
}	
/*******************************************************
*		函数名:void Com_init()
*		功  能:串口初始化,设置串口和定时器1工作模式
*******************************************************/
 void Com_init()	     
 {
     
 	  ES=0;				// 关串口中断
	 SCON = 0x50;       // REN=1允许串行接受状态,串口工作模式1,波特率可变
	 TMOD|=0x20;        // 定时器1工作于方式2,8位自动重载模式, 用于产生波特率
     TH1=TL1=0xFD;     	// 波特率9600 (晶振为11.0592)
     PCON &= 0x7f;     	// 波特率不倍增
      TR1 = 1;			// 定时器1开始工作,产生波特率
	  RI = 0;		    // 接收标志位置0	  
	  EA=0;				//关总中断
	  ES=1;             // 开启串口中断
 }
/*******************************************************
 * 	函数名:void Datectr()
 * 	功  能:用以处理串口将接收到的数据
*******************************************************/
void DateCtrl()
	{
     
		switch(receive_data)
		{
     
			case ('F'): forward();
				break;	  		
			case ('B'):	back(); 
				break;	  		
			case ('L'): left_add();		      //left
				break;    		
			case ('l'): turn_left();
				break;
			case ('R'): right_add();		  //right 
				break;  
			case ('r'): turn_right();
				break;
			case ('u'): moter_add();
			  break;
			case ('d'): moter_less();
			  break;
			case ('S'): stop(); 
			  break;
		}	
	}
	
void main() 	     //主函数			     
{
      	
	Com_init();		 //串口初始化和定时器初始化
	Time0_Int();
	while(1){
      
	  if(RI==1){
     	 // 判断是否有数据到来
			receive_data = SBUF; //将收/发数据缓存器SBUF中的数据存入变量
		    DateCtrl();			 //调用数据处理函数
		    RI = 0;
		   }       
      } 
}
//欧克,收工;
  • 现在就可以愉快的玩耍自己亲手制作的蓝牙遥控小车了!最后别忘了给自己比个耶 ✌!哈哈~
  • 最后来张成品图圆满结束:51单片机蓝牙遥控小车 定时器 中断 PWM L298N调速控制_第7张图片

你可能感兴趣的:(嵌入式,单片机,蓝牙,串口通信)