基于51单片机的双模式蓝牙跟随智能小车设计详解
1. 功能介绍
智能小车有两种工作模式,分别是蓝牙避障,超声波跟随,其中模式的切换和小车的调速都是通过蓝牙完成。
2. 所需的材料
1,51单片机开发板x1
2,杜邦线(公对公,母对母,公对母)
3,L298N驱动模块
4,电池一枚,以作为电源(需要大于6v)
5,HC-05蓝牙模块
6,蓝牙小车底盘一个
7,面包板一个
8, 红外避障模块 * 4
9, 超声波模块(HC-SR04) * 1
10,stc89c516RD+(stc89c52不能够使用,定时器的个数不够)
3. 各个模块介绍
L298n
-
将左右马达分别接在A,B口,顺序无所谓,可以通过代码来调试。
-
将电源正极接到12v处,电源的负极街道GND,在引出5v为单片机供电。
-
5v输出使能的跳线帽一定要接上,A,B通道的使能与单片机连接(用于PWM)
红外避障模块
-
因为该模块对环境光有一定的感应能力,所以在使用时,尽量不要在阳光和强光下使用。
-
其有一个发射管和一个接受管,当遇到障碍物时,输出指示灯会亮起,并且输出接口输出低电平。
-
该模块检查距离为2~30cm,角度为35°,可以通过调节电位器来调节检测距离。
HC-05蓝牙模块
六个引脚,我们这里主要用其中的四个(GND,5V,RT,TX),其中RT,TX与单片机输入输出口反接,
我们主要是通过串口通信来实现信号的传递。
超声波模块(HC-R04)
该模块我们主要用于跟随模式,其采用Trig引脚触发,给至少10us的高电平脉冲信号
模块自动发送8个40kHz的方波,自动检测是否有信号返回
有信号返回,通过Echo引脚输出一个高电平脉冲,高电平脉冲持续的时间就是超声波从发射到反射返回的时间。距离=(高电平脉冲时间*340)/2
4. 蓝牙APP的开发
这里使用的是App Inventor进行开发,其主要原理是让手机向蓝牙模块发送一个单字节的数,单片机在通过这个信号进行相应的操作。
界面开发
这里要记得添加蓝牙客户端
逻辑开发
初始化
这一部分是比较通用的,大家可以直接抄写。
按钮的逻辑
这里要注意传输的信号与单片机程序里做出的反应要对应。
5. 代码
电机转动
这里主要是控制电机的的正反转和停止来实现小车的前进,后退,停止和左右转。
car.c
#include <REGX52.H>
sbit IN1 = P1^3;
sbit IN2 = P1^2;
sbit IN3 = P1^1;
sbit IN4 = P1^0;
void car_left_motor_forward() //左电机向前
{
IN3=0;
IN4=1;
}
void car_left_motor_back() //左电机向后
{
IN3=1;
IN4=0;
}
void car_right_motor_forward() //右电机向前
{
IN1=1;
IN2=0;
}
void car_right_motor_back() //右电机向后
{
IN1=0;
IN2=1;
}
void car_right_motor_stop() //右电机停止
{
IN1=1;
IN2=1;
}
void car_left_motor_stop() //左电机停止
{
IN4=1;
IN3=1;
}
car.h
#ifndef __CAR_H__
#define __CAR_H__
void car_left_motor_forward(); //左电机向前
void car_left_motor_back(); //左电机向后
void car_right_motor_forward(); //右电机向前
void car_right_motor_back(); //右电机向后
void car_right_motor_stop(); //右电机停止
void car_left_motor_stop(); //左电机停止
#endif
move.c
#include <REGX52.H>
#include "car.h"
void move_go() //前进
{
car_left_motor_forward(); //左电机向前
car_right_motor_forward(); //右电机向前
}
void move_back() //前退
{
car_left_motor_back(); //左电机向后
car_right_motor_back(); //右电机向后
}
void move_left() //左转
{
car_left_motor_stop(); //左电机停止
car_right_motor_forward(); //右电机向前
}
void move_right() //右转
{
car_left_motor_forward(); //左电机向前
car_right_motor_stop(); //右电机停止
}
void move_stop()
{
car_right_motor_stop(); //右电机停止
car_left_motor_stop(); //左电机停止
}
void move_back_left() //左转
{
car_left_motor_stop(); //左电机停止
car_right_motor_back(); //右电机向前
}
c
void move_back_right() //右转
{
car_left_motor_back(); //左电机向前
car_right_motor_stop(); //右电机停止
}
move.h
#ifndef __MOVE_H__
#define __MOVE_H__
void move_go() ; //前进
void move_back(); //后退
void move_left(); //左转
void move_right(); //右转
void move_stop();
void move_back_left(); //左转
void move_back_right(); //左转
#endifc
PWM调速
PWM调速主要使用到的是定时器0,通过控制L298N上ENL和ENR两对使能引脚,对电机进行控制。
timer_PWM.c
#include <REGX52.H>
sbit ENL1 = P1^4;
sbit ENL2 = P1^5;
sbit ENR1 = P1^7;
sbit ENR2 = P1^6;
int count = 0;
int speed_left = 0;
int speed_right = 0;
void Timer0Init(void) //定时器0初始化,100微秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xAE; //设置定时初始值
TH0 = 0xFB; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
}
void time() interrupt 1
{
TL0 = 0xAE; //设置定时初始值
TH0 = 0xFB; //设置定时初始值
count++;
count%=100;
if(count>=speed_left)
{
ENL1 = 0;
ENL2 = 0;
}
else
{
ENL1 = 1;
ENL2 = 1;
}
if(count>=speed_right)
{
ENR1 = 0;
ENR2 = 0;
}
else
{
ENR1 = 1;
ENR2 = 1;
}
}
###timer_PWM.h
#ifndef __TIMER_PWM_H__
#define __TIMER_PWM_H__
extern int count;
extern int speed_left;
extern int speed_right;
void Timer0Init(void); //定时器0初始化,100微秒@11.0592MHz
void time();
#endif
蓝牙模块
-
模式的转换,主要是通过pattern变量的值来判断,其为1时表示为蓝牙避障模式,其为2时为超声波跟随模式。
-
蓝牙主要使用的是串口通信,我们要先对串口进行初始化,在通过SBUF接收到值进行响应。
bluetooth.c
#include <REGX52.H>
#include "move.h"
#include "timer_PWM.h"
unsigned int key = 0; //接受蓝牙数据
unsigned int pattern = 0; //模式选择
unsigned int launch = 0; //跟随停止和开始
void bluetooth_UartInit() //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFD; //设置定时初始值
TH1 = 0xFD; //设置定时重载值
ET1 = 0; //禁止定时器%d中断
TR1 = 1; //定时器1开始计时
ES=1;
EA=1;
}
void bluetooth_move_take(unsigned int key)
{
switch(key)
{
case 1:if(pattern == 1){move_go();} break; //前进
case 2:if(pattern == 1){move_back();} break; //后退
case 3:if(pattern == 1){move_left();}break; //左转
case 4:if(pattern == 1){move_stop(); }break; //停止
case 5:if(pattern == 1){move_right(); }break; //右转
case 6:speed_left = 50;speed_right = 50;break; //速度1
case 7:speed_left = 100;speed_right = 100;break;//速度2
case 8:move_stop();pattern = 1; break; //蓝牙避障模式
case 9:launch = 0;move_stop();pattern = 2; break; //超声波跟随模式
case 10:if(pattern == 2){launch = 1;move_stop();}break; //开始跟随
case 11:if(pattern == 2){launch = 0;move_stop();} break; //停止跟随
}
}
void bluetooth_uart_pass() interrupt 4
{
if(RI==1)
{
key = SBUF;
if(key <= 5)
{
move_stop();
}
bluetooth_move_take(key);
RI = 0;
}
}
bluetooth.h
#ifndef __BLUETOOTH_H__
#define __BLUETOOTH_H__
void bluetooth_UartInit();
void bluetooth_move_take(unsigned int key);
void bluetooth_uart_pass();
extern unsigned int key;
extern unsigned int pattern;
extern unsigned int launch;
#endif
红外避障
红外模块四个角各一个,当前面遇到障碍时,小车后退并停止,当后面遇到障碍时,小车向前行驶并停止。
sbit PIR1 = P0^0;
sbit PIR2 = P0^1;
sbit PIR3 = P0^2;
sbit PIR4 = P0^3;
void PIR_work() //红外避障模块
{
if(PIR1 == 0 || PIR2 == 0)
{
move_stop();
move_back();
Delay(150);
move_stop();
}
if(PIR3 == 0 || PIR4 == 0)
{
move_stop();
move_go();
Delay(150);
move_stop();
}
}
超声波模块
该模块使用的是定时器2,但其只用于计时,不触发中断。
当Echo接受到高电平时,定时器开启,高电平结束,定时器关闭。
UT.C
#include <REGX52.H>
#include <INTRINS.H>
sbit Trig = P2^1;
sbit Echo = P2^0;
void Delay50ms() //@11.0592MHz
{
unsigned char i, j;
i = 90;
j = 163;
do
{
while (--j);
} while (--i);
}
void Delay15us() //@11.0592MHz
{
unsigned char i;
i = 4;
while (--i);
}
void Timer2_Init(void)
{
T2MOD = 0; //初始化模式寄存器
T2CON = 0; //初始化控制寄存器
TL2 = 0x00; //设置定时初值
TH2 = 0x00; //设置定时初值
TR2 = 0; //定时器2关闭计时
}
int judge(float cm , float number)
{
float n = cm - number;
if(n <= 3 && n >= -3)
{
return 1; //停止
}else if(n < -3)
{
return 2; //后退
}else if(n > 3)
{
return 3; //前进
}
return 1;
}
void Delay25ms() //@11.0592MHz
{
unsigned char i, j;
i = 45;
j = 208;
do
{
while (--j);
} while (--i);
}
int UT_work()
{
float number = 8;
float s = 0;
float cm = 0;
int n = 0;
Trig = 0;
Echo = 0;
Trig = 1;
Delay15us();
Trig = 0;
while(!Echo);
TR2 = 1;
while(Echo);
TR2 = 0; //关闭计时器
s = (float)((TH2 * 256 + TL2) * 0.000001);
cm = s * 340 / 2 * 100;
n =judge(cm , number);
cm = 0;
s = 0;
TH2 = 0x00;
TL2 = 0x00; //清除定时器数值
Delay25ms();
return n;
}
//
UT.H
#ifndef __UT_H__
#define __UT_H__
void Timer2_Init(void);
int judge(float cm , float number);
int UT_work();
#endif
跟随模式的红外转弯
该模式下,主要使用PIR1和PIR2前面两个红外避障模块。
PIR1和PIR2全部输出低电平或高电平时,则继续保持原来的运动。
PIR1为低电平时,PIR为高电平时,说明跟随物在小车左端,小车左转。
PIR1为高电平时,PIR为低电平时,说明跟随物在小车右端,小车右转。
sbit PIR1 = P0^0;
sbit PIR2 = P0^1;
void PIR_turn() //红外拐弯
{
if(PIR1 == 0 && PIR2 == 1) //右拐
{
move_left();
}
if(PIR1 == 1 && PIR2 == 0) //左拐
{
move_right();
}
}
延迟函数
Delay.c
#include <intrins.h>
#include <REGX52.H>
void Delay(unsigned int a) //@11.0592MHz
{
unsigned char i, j;
while(a--)
{
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i);
}
Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay(unsigned int a);
void Delay10us();
#endif
main.c
#include <REGX52.H>
#include "car.h"
#include "move.h"
#include "DELAY.H"
#include "TIMER_PWM.H"
#include "bluetooth.h"
sbit PIR1 = P0^0;
sbit PIR2 = P0^1;
sbit PIR3 = P0^2;
sbit PIR4 = P0^3;
void PIR_work() //红外避障模块
{
if(PIR1 == 0 || PIR2 == 0)
{
move_stop();
move_back();
Delay(150);
move_stop();
}
if(PIR3 == 0 || PIR4 == 0)
{
move_stop();
move_go();
Delay(150);
move_stop();
}
}
void PIR_turn() //红外拐弯
{
if(PIR1 == 0 && PIR2 == 1) //右拐
{
move_left();
}
if(PIR1 == 1 && PIR2 == 0) //左拐
{
move_right();
}
}
void main()
{
int n = 0;
Timer0Init();
bluetooth_UartInit();
Timer2_Init();
speed_left=50;
speed_right=50;
while(1)
{
if(pattern == 1)
{
PIR_work();
}
if(pattern == 2)
{
if(launch)
{
PIR_turn();
n = UT_work();
switch(n)
{
case 1:move_stop();break;
case 2:move_back();break;
case 3:move_go();break;
}
}
}
}
}
6. 成品展示
外观