欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

新手入门单片机实战避坑-避障小车3功能开发—蓝牙【更新中】

程序员文章站 2022-03-10 15:34:49
...

说两句

在我的小车能够经历没病走两步的阶段以后,博主的也慢慢萌发了一些大胆的想法。想在小车上加一些新的功能,第一个想到的就是蓝牙。用手机控制,应该会显得Bigger更高一点。甚至于还想将手机控制和自动避障结合起来,是不是有点无人驾驶内味儿了。(马斯克看完直呼:就这?)

开锤

首先是蓝牙模块,博主也是第一次尝试使用,网上主流的方案都是HC-05、HC-06,蓝牙模块除了HC还有JDY系列,价格浮动较大,其中区别不细分(毕竟我也不懂)。博主在某宝看到一个HC-04的初学者套餐就几块钱拿下了,还送个TTL转USB转接口,虽然只能作为从机但是实测挺好用的,真香!

手机遥控无非是通过蓝牙或者无线模块实现终端(例如手机)与单片机之间的通信,最简单的方式就是通过串口了。而串口通信的前提是波特率一致!!!(这点很重要)

收到蓝牙模块不要先急着做,一般店家都会给你配套的软件或者其他资料先检查下模块是否正常再考虑移植到单片机上。

建议顺序:检查蓝牙模块是否能正常收发数据 —> 通过店家提供的工具使用AT指令集进行配置(指令最好根据自己买的型号找淘宝卖家要资料),起码要查询一下波特率和密码等相关信息 —>然后就可以着手写代码了,串行口通信主要是对定时器作为波特率发生器的设置 —> 先通过烧录软件的串口助手测试一下波特率,如果可以正常收发数据就可以完善代码测试了 即 检查蓝牙模块、配置蓝牙模块、检查串口配置、硬件调试

遇到的坑

1.波特率发生器配置
大部分人都是直接用定时器0或者定时器1进行配置,而我的代码里面定时器0用于生成PWM调速,定时器1用于超声波传感器比较信号收发时间计算距离,所以没办法直接移植别人的代码。定时器2和定时器0、1颇有些区别,而且STC15系列和89C51系列也有不同。后来从想到这个问题,直接用修改别人代码的过程中想当然的把定时器初值寄存器从TH1、TL1改成了TH2、TL2,结果出大问题。

后来查阅手册找到了问题,建议大家确定错误位置以后排错的过程最好对照手册一一检查。
新手入门单片机实战避坑-避障小车3功能开发—蓝牙【更新中】

2.在使用烧录软件的波特率计算器的过程中发现存在和范例不同的问题
具体的计算方法没有深究,范例是通过预设初值给出公式的方法预设初值,发现和波特率计算器生成的结果不同,但是经测试两种结果都能进行通信。

带佬解释可能是串口带有校正的功能,当波特率接近发生不大的偏差会自动校正。

3.语法问题
基本实现蓝牙遥控以后,其实当时写的代码自带加减速功能,但是经过实测效果不明显看不出区别,初计划尝试在数码管上加显示PWM调速情况,后来发现显示始终为0。经排错发现 小车初始状态因为串口通信数据变量为0标记的是stop状态(通过控制PWM变量实现),然后显示函数放在了该函数后,就导致每次其实是置零以后才显示。(要注意语句/函数前后顺序

Switch Case语句中在case条件里面加了if语句是导致加减速失效的关键。(case语句不能嵌入if判断语句

最后不能简单在case语句中直接出现PWM±的情况(我用的蓝牙串口软件是通过设置几个按键发送的指令检测按下松手状态发送指令,类似于按键),所以加减速需要在switch之外(控制前进后退左转右转)单独写if语句,注意消抖。

软件名字: 蓝牙调试器
新手入门单片机实战避坑-避障小车3功能开发—蓝牙【更新中】

效果演示

新手入门单片机实战避坑-避障小车3功能开发—蓝牙【更新中】

代码展示

#include <reg52.h>
#include <intrins.h>
#include <stdio.h>

#define uint unsigned int
#define uchar unsigned char

//sbit  left_s = P0^0;   //左侧传感器检测到障碍物
//sbit  right_s = P0^1;  //右侧传感器检测到障碍物 
sbit IN1 = P1^0;
sbit IN2 = P1^1;
sbit ENA = P1^2;
sbit IN3 = P1^3;
sbit IN4 = P1^4;
sbit ENB = P1^5;
sbit Trig =P2^4;
sbit Echo =P2^5;

//sbit beep = P2^3;
uchar speed_l=50;
uchar speed_r=50;
uchar add;  //加附加变量  
uchar sub;  //减附加变量
uchar t;    
uint dis;   //超声波距离
uchar com_s; //串口信号

/************************************/
sbit dula=P2^6;		//段选信号的锁存器控制
sbit wela=P2^7;		//位选信号的锁存器控制

uchar  seq[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
uchar  seg[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,
                        0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
/*************************************/	

void Delayms(uint z);
void Delayus(uint time);
void Timer0Init();
void Timer1Init(void);
void UART_init();
void StartInit();
int Count();
void straight();
void back();
void run();
void stop();
void turn_left();
void turn_right();

/*******************************************************/
//毫秒级延时函数
void Delayms(uint z)
{
	uint x,y;
	for(x = z; x > 0; x--)
		for(y = 120; y > 0 ; y--);
}

/*******************************************************/
//微秒级延时函数
void Delayus(uint time)
{
	while(time--);
}

void display()
{ 
	uchar num;
	for(num=0;num<3;num++)
		{
			P0=seq[num];   
			wela=1;
			wela=0;
			switch(num)
			{
				case 0 : {			P0=seg[speed_l/100];	dula=1;dula=0;Delayms(2);} break;
				case 1 : {			P0=seg[speed_l%100/10];	dula=1;dula=0;Delayms(2);} break;
				case 2 : {			P0=seg[speed_l%10];	dula=1;dula=0;Delayms(2);} break;
			}
		}
		for(num=4;num<7;num++)
		{
			P0=seq[num];  
			wela=1;
			wela=0;
			switch(num)
			{
				case 4 : {			P0=seg[speed_r/100];	dula=1;dula=0;Delayms(2);} break;
				case 5 : {			P0=seg[speed_r%100/10];	dula=1;dula=0;Delayms(2);} break;
				case 6 : {			P0=seg[speed_r%10];	dula=1;dula=0;Delayms(2);} break;
			}
		}
}

/*******************************************************/
//定时器0初始化
void Timer0Init(void)		//100微秒@11.0592MHz
{
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x70;		//设置定时初值
	TH0 = 0xDD;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	EA  = 1;
	ET0 = 1;
}

void UART_init()
{
	T2CON = 0x34;  //启动定时器2,工作在16为位重装载模式且溢出不进入中断服务函数
	RCAP2L = 0xDC;		//设定定时初值
	RCAP2H = 0xFF;		//比特率为9600
	SM0 = 0;
	SM1 = 1; 		//串口工作方式1 10位异步
	REN = 1;		//串口允许接收
	EA  = 1;		//开总中断
	ES  = 1;		//串口中断打开
}


/*******************************************************/
//定时器1初始化,用于超声波传感器计算距离
void Timer1Init(void)		
{
		TMOD &= 0x0F;		//设置定时器模式
		TMOD |= 0x10;		//设置定时器模式
		TL1 = 0x00;		//设置定时初值
		TH1 = 0x00;		//设置定时初值
		TF1 = 0;		//清除TF1标志
//	TR1 = 1;		//定时器1开始计时
}


/*******************************************************/
//超声波传感器启动
void StartInit()
{
	  Trig = 1;
	  Delayus(50);    //发出超声波
	  Trig = 0;
}

/*******************************************************/
//超声波传感器计算
int Count()
{
	  uint time,distance;
		time=TH1*256+TL1;  //计算接收返回超声波时间
	  TH1=0;
	  TL1=0;
	  distance=(time*1.7)/100;    //计算距离 
	  return distance;	       
}

/*******************************************************/
//超声波传感器得到距离
void Measure_dis()
{
	 StartInit();
   while(!Echo);		//当RX为零时等待
   TR1=1;			    //开启计数
   while(Echo);		 //当RX为1计数并等待
   TR1=0;				//关闭计数
   dis=Count();
}

/*******************************************************/
//控制函数 
void straight()   //前进
{
	  IN1 = 1;
	  IN2 = 0;
	  IN3 = 1;
	  IN4 = 0;  
}

void back()      //后退
{
	  IN1 = 0;
	  IN2 = 1;
	  IN3 = 0;
	  IN4 = 1;   
}

void rotate_l()   //原地转圈(左转)
{
	  IN1 = 1;
	  IN2 = 0;
	  IN3 = 1;
	  IN4 = 1;  
}

void rotate_r()   //原地转圈(右转)
{
	  IN1 = 1;
	  IN2 = 1;
	  IN3 = 1;
	  IN4 = 0;  
}

void run()      //给占空比
{
	  speed_l = 50+ add - sub;
	  speed_r = 50+ add - sub;
}

void stop()
{
//	   ET0 = 0;      //关闭定时器,停止产生PWM
//	   ENA = 0;       
//	   ENB = 0;       //启停后重新启动需要重新配置定时器
	   IN1 = 0;
	   IN2 = 0;
     IN3 = 0;
     IN4 = 0;
//	     speed_l = 0;
//	     speed_r = 0;
}

void turn_left()    //左转右不转
{
	  speed_l = 30 + add - sub;
	  speed_r = 0;
}

void turn_right()   //右转左不转
{
	  speed_l = 0;
	  speed_r = 30 + add - sub;
}

void main()
{
/* 定时器0用于产生PWM用于电机调速  */
/* 定时器1用于记录超声波发送和接受间隔计算距离  */
/* 定时器2用于串口波特率产生    */
	  Timer1Init();	 //初始化
		Timer0Init();
		UART_init();
	  straight();
	    while(1)
		{
			  display();
        switch(com_s)
				{
					case 0: stop();break;
					case 1: straight();run(); break;
					case 2: rotate_l();run(); break;
					case 3: rotate_r();run(); break;
					case 4: back();run();break;
//					case 5: rotate();run();break;
//					case 6: add+=10; break;
//					case 7: sub+=10; break;
				}
				if(com_s==6&&speed_l<90&&speed_r<90)
				{
					Delayms(5);
					if(com_s==6&&speed_l<90&&speed_r<90)
					{	
						add+=10;
						while(com_s);
					}
				}
				if(com_s==7&&speed_l<90&&speed_r<90)
				{
					Delayms(5);
					if(com_s==7&&speed_l<90&&speed_r<90)
					{	
						sub+=10;
						while(com_s);
					}
				}
//			 Measure_dis();
//			if(dis>40)
//			{
//				  straight();
//					run();
//			}
//			else if(dis<40&&dis>20)
//			{ 
//				  straight();
//				  run();
//				  Delayms(10);
//				  turn_right();
//				  Delayms(500);
//			}
//			else
//			{
//					stop();
//				  back();
//				  ET0 = 1;
//				  run();
//				  Delayms(500);
//				  straight();
//					turn_right();
//				  Delayms(500);
//			}      
		}
}

/* 通过控制左右的使能实现PWM调速  */
void Timer0_isr() interrupt 1 
{
		TL0 = 0x70;		//设置定时初值
		TH0 = 0xDD;		//设置定时初值
    if(t < speed_l )
		{
			 ENA = 1;
		}
		else
		{
			 ENA = 0;
		}
		if(t < speed_r)
		{
			 ENB = 1;
		}
		else
		{
			 ENB = 0;
		}
		t++;
		if(t >= 100)
		{
			 t = 0;
		}		
}

void Uart_Isr() interrupt 4 
{
    if (RI)
    {
        RI = 0;             //标志位清零
        com_s = SBUF;       //接收串行口发来的数据
    }

}
	

** 欢迎讨论和批评指正,共同进步**

相关标签: 单片机