0
点赞
收藏
分享

微信扫一扫

清翔51智能小车.上(小学期作品,包含按键操作、红外循迹、红外避障、测距、超声波魔术手、蓝牙操作、OLED实现、APP制作)

笔者碎念:此作品为某大学大一小学期的项目作品,笔者从0基础开始学习制作,摸鱼几周完成,完成后并没有进行更多优化代码规范和功能,所以许多模块仅限于能用就行,本文只适合为刚接触的同学提供一些我的思路和心得。(比如说在某些部分代码没有问题,但执行起来有奇怪的问题,可能是单片机某些处理问题,这时使用延时函数可能解决)

接线操作https://www.bilibili.com/video/BV1Ca4y1s7JA/(有接线不同可能是一些模块接上时需要改线)

更多相关问题查询各类学习网站解答

关于两个不同代码版本

因为最初编写代码时没有蓝牙模块只有2个空余可操作按键(若是会该线应该可以有更多)而通过这2个按键实现了切换模块和一些操作,之后有蓝牙又对代码进行部分修改,增加了功能,修改使用蓝牙传输数据来实现切换模式,按钮只实现一些操作。

无蓝牙

main.c

主函数包括引入最基本的函数库REGX52.H

和关于一些引脚定义和基本操作定义

延时函数可以在STC-ISP中生成(在一些地方添加延时函数可能可以处理一些非代码引起的问题,比如突然修改速度等函数调用之后)

然后是计时器T0和T1的初始化,其中在T0用于前几个模块的PWM的计时,在T1用于测距(魔术手中是使用T2作为PWM的计时),因为两者共用一个寄存器(意味着不能同时使用),所以需要更改TMOD值来改变使用的计时器

然后是包含不同模块函数的头文件引入

主函数最开始初始化T0和T1以及LCD1602,然后使用一个无限循环包含在各个模块下的无限循环,使用Pattern.h中的Change_Pa()来break,进入下一个模块

#include <REGX52.H> 		// or #include <AT89x51.H>?
#include <INTRINS.h>
#include <QXA51.h>
void Delay1ms(int time)		//@11.0592MHz
{
	unsigned char data i, j;
	int a;
	for(a=0;a<time;a++)
	{
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}
}
void timer01_init()
{
	//TMOD = 0x10;	//8-bit自动重载模式,设T0,   
	//TMOD = 0x02;
	TH0 = 220;
	TL0 = 220;//11.0592M晶振下占空比最大是256,输出100HZ
	TR0 = 1;//启动定时器0
	ET0 = 1;//允许定时器0中断   
	TH1=0;		  
	TL1=0;
	ET1=1;//允许T1中断
	TR1=0;//关闭定时器1,在特定模块中才需要打开
	EA=1;//开启总中断
}
#include <PWM.h>
#include <LCD1602.h>
#include <Distance.h>
#include <Pattern.h> 
#include <Controll.h>
#include <Follow.h>
#include <Avoidance.h>
void main()			
{	
	timer01_init();
	Lcd1602_init();
	while(1)
	{
		while(1)
		{
			TMOD = 0x02;
			Delay1ms(100);
			LRspeed(150,150);
			controll();
			if(Change_Pa()==1)
				break;
		}
		while(1)
		{			
			TMOD = 0x02;
			Delay1ms(100);
			follow();			
			if(Change_Pa()==1)
				break;
		}
		while(1)
		{
			TMOD = 0x02;
			Delay1ms(100);
			avoidance();
			if(Change_Pa()==1)
				break;			
		}
		TMOD = 0x10;
		while(1)
		{
			LCD_ShowNum(distance());
			Delay1ms(100);
			Lcd1602_Write_Cmd(0x01);//清屏作用
			Delay1ms(200);		
			if(Change_Pa()==1)
				break;			
		}
		T2_Init();
		while(1)
		{
			flag_dis=distance();
			keep_dis();
			if(Change_Pa()==1)
				break;
		}
	}
}
//It is difficult to switch from mode 2 to mode 1
//You are advised to restart the power supply to return to mode 1

QXA51.h

主要是

#ifndef __QXA51_H__
#define __QXA51_H__

/*电机驱动IO定义*/
sbit IN1 = P1^2; //为1左电机反转
sbit IN2 = P1^3; //为1左电机正转
sbit IN3 = P1^6; //为1右电机正转
sbit IN4 = P1^7; //为1右电机反转
sbit EN1 = P1^4; //为1左电机使能
sbit EN2 = P1^5; //为1右电机使能
sbit TrackSensorLeft = P1^1;//左循迹信号为0则没有识别到黑线,为1则识别到黑线
sbit TrackSensorRight = P1^0;//右循迹信号
sbit left_avoid = P2^7;//左避障信号
sbit right_avoid = P2^6;//右避障信号为0,识别到障碍物,为1则没有识别到障碍物

/*按键定义*/
sbit key_s4 = P3^2;
sbit key_s5 = P3^3;
sbit beep = P2^3;//蜂鸣器

unsigned int flag_dis;//记录距离

#define left_motor_en		EN1 = 1	//左电机使能
#define right_motor_en		EN2 = 1	//右电机使能

#define left_motor_stops	IN1 = 0, IN2 = 0//左电机停止
#define right_motor_stops	IN3 = 0, IN4 = 0//右电机停止

#define left_motor_go		IN1 = 0, IN2 = 1//左电机正转
#define left_motor_back		IN1 = 1, IN2 = 0//左电机反转
#define right_motor_go		IN3 = 1, IN4 = 0//右电机正转
#define right_motor_back	IN3 = 0, IN4 = 1//右电机反转

	
#endif

Controll.h

按键按下,则对应引脚变成低电平

快速单按s5时,小车前进,长按时小车原地左转(长按时间大于200ms),快速单按s4时,小车后退,长按时小车原地右转。通过一个段较长的时间来区分想要进行的操作

void controll()
{ 
	if(key_s5==0&&key_s4==1)
	{
		Delay1ms(200);
		if(key_s5==1&&key_s4==1)
		{
			left_motor_en;
			right_motor_en;
			left_motor_go;
			right_motor_go;
			Delay1ms(500);
			left_motor_stops;
			right_motor_stops;
		}
		else if(key_s5==0&&key_s4==1)
		{
			while(!key_s5);
			left_motor_en;
			right_motor_en;
			left_motor_back;
			right_motor_go;
			Delay1ms(150);
			left_motor_stops;
			right_motor_stops;
		}
	}
	else if(key_s4==0&&key_s5==1)
	{
		Delay1ms(200);
		if(key_s4==1&&key_s5==1)
		{
			left_motor_en;
			right_motor_en;
			left_motor_back;
			right_motor_back;
			Delay1ms(500);
			left_motor_stops;
			right_motor_stops;
		}
		else if(key_s4==0&&key_s5==1)
		{
			while(!key_s4);
			left_motor_en;
			right_motor_en;
			left_motor_go;
			right_motor_back;
			Delay1ms(150);
			left_motor_stops;
			right_motor_stops;
		}
	}	
}

Pattern.h

当同时按下两个按键时,则返回1,主函数中对应的模块循环跳出循环

int Change_Pa() {
	if (key_s4==0||key_s5==0) {
		Delay1ms(5);
		if(key_s4==0&&key_s5==0)
		{
			while(!key_s4||!key_s5);
			right_motor_stops;
			left_motor_stops;
			return 1;
		}
	}			//More sensitive after removing "return 0"
	return 0;	//Very important for the second pattern
}

Follow.h

void follow()
{	
	//当小车检测到前方有障碍物时,自动掉头回到黑线上
	if(left_avoid == 0 && right_avoid == 0)		//Rotate slowly in place until a black line is detected
	{
		LRspeed(150,150);
		Delay1ms(1);
		left_motor_en;
		right_motor_en;
		left_motor_go;
		right_motor_back;
		Delay1ms(200);
		while(1)
	{
			if(TrackSensorLeft==1&&TrackSensorRight==1)
				break;
		}
	}
//前进
if(TrackSensorLeft==1&&TrackSensorRight==1)
	{
		LRspeed(150,150);
		Delay1ms(5);
		left_motor_en;
		right_motor_en;
		left_motor_go;
		right_motor_go;
	}
//左转
	else if(TrackSensorLeft==1&&TrackSensorRight==0)
	{
		/*LRspeed(250,20);			
		Delay1ms(10);
		left_motor_en;
		right_motor_en;
		left_motor_en;
		right_motor_go;
		注释的为两轮相差更大的差速转弯,实际可能出现一些问题*/
		LRspeed(250,170);
		Delay1ms(1);
		left_motor_stops;
		right_motor_en;
		right_motor_go;	
	}
//右转
	else if(TrackSensorLeft==0&&TrackSensorRight==1)
	{
		/*LRspeed(20,250);
		Delay1ms(10);
		right_motor_en;
		left_motor_en;
		left_motor_go;
		right_motor_go;	
		注释的为两轮相差更大的差速转弯,实际可能出现一些问题*/
		LRspeed(170,250);
		Delay1ms(1);
		right_motor_stops;
		left_motor_en;
		left_motor_go;			
	}
	else 
//防出线操作	if(TrackSensorLeft==0&&TrackSensorRight==0)
	{
		LRspeed(200,200);
		Delay1ms(1);						//Overheat protection
		left_motor_stops;
		right_motor_stops;
		Delay1ms(1);
		left_motor_en;				
		right_motor_en;
		left_motor_back;
		right_motor_back;	
	}
}

Avoidance.h

int avoidance()
{
		if(left_avoid == 1 && right_avoid == 1)//左右都没有识别到障碍物
		{
			LRspeed(200,200);
			Delay1ms(10);
			left_motor_en;
			right_motor_en;
			left_motor_go;
			right_motor_go;
		}
		else if(left_avoid == 1 && right_avoid == 0)//小车右侧识别到障碍物
		{
			beep = 0;	//使能有源蜂鸣器
			Delay1ms(100);//100ms计时
			beep = 1;	//关闭蜂鸣器
			LRspeed(150,150);
			Delay1ms(10);
			left_motor_en;
			right_motor_en;
			left_motor_back;
			right_motor_go;
		}
		else if(left_avoid == 0 && right_avoid == 1)//小车左侧识别到障碍物
		{
			beep = 0;	//使能有源蜂鸣器
			Delay1ms(100);//100ms计时
			beep = 1;	//关闭蜂鸣器
			LRspeed(150,150);
			Delay1ms(10);
			left_motor_en;
			right_motor_en;
			left_motor_go;
			right_motor_back;
		}	
		else if(left_avoid == 0 && right_avoid == 0)//都识别障碍物,实现一个原地掉头,根据实际而时间会不同
		{
			beep = 0;	//ʹÄÜÓÐÔ´·äÃùÆ÷
			Delay1ms(100);//200ºÁÃëÑÓʱ
			beep = 1;	//¹Ø±ÕÓÐÔ´·äÃùÆ÷			
			LRspeed(150,150);
			Delay1ms(10);
			left_motor_en;
			right_motor_en;
			left_motor_go;
			right_motor_back;
			Delay1ms(250);
		}
			
}
//Issue forward and backward commands

LCD1602.h

sbit RS = P3^5;
sbit RW = P3^6;
sbit EN = P3^4;
void Lcd1602_Write_Cmd(unsigned char cmd)
{
	EN=0;
	RS=0;
	RW=0;
	Delay1ms(5);
	P0=cmd;
	Delay1ms(5);
	EN=1;
	Delay1ms(5);
	EN=0;
	Delay1ms(5);
}
void Lcd1602_Write_Data(unsigned char dat)
{
	EN=0;
	RS=1;
	RW=0;
	Delay1ms(5);
	P0=dat;
	Delay1ms(5);
	EN=1;
	Delay1ms(5);
	EN=0;
	Delay1ms(5);
}
void simle()
{
	Lcd1602_Write_Cmd(0x40);
	Lcd1602_Write_Data(0x00);
	Lcd1602_Write_Cmd(0x41);
	Lcd1602_Write_Data(0x00);
	Lcd1602_Write_Cmd(0x42);
	Lcd1602_Write_Data(0x0A);
	Lcd1602_Write_Cmd(0x43);
	Lcd1602_Write_Data(0x0A);
	Lcd1602_Write_Cmd(0x44);
	Lcd1602_Write_Data(0x00);
	Lcd1602_Write_Cmd(0x45);
	Lcd1602_Write_Data(0x11);
	Lcd1602_Write_Cmd(0x46);
	Lcd1602_Write_Data(0x0E);
	Lcd1602_Write_Cmd(0x47);
	Lcd1602_Write_Data(0x00);
	
	Lcd1602_Write_Cmd(0xC0);
	Lcd1602_Write_Data(0x00);
}//一个简单的笑脸实现
int LCD_Pow(unsigned int X,unsigned int Y)			//X^Y
{
	unsigned char i;
	unsigned int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}//实现一个数学POW的功能
void LCD_ShowNum(unsigned int Number)
{
	int i;//very important.Because then it have to get out of the loop
	simle();
	if(Number==0)
	{
		Lcd1602_Write_Cmd(0x80);
		Lcd1602_Write_Data(0x45);
		Lcd1602_Write_Data(0x52);
		Lcd1602_Write_Data(0x52);
		Lcd1602_Write_Data(0x4F);
		Lcd1602_Write_Data(0x52);
	}
	else
	{
		i=0;
		Lcd1602_Write_Cmd(0x80);
		while(1)
		{
			if(Number%LCD_Pow(10,i)==Number)
				break;	
			i++;
		}
		i--;
		Lcd1602_Write_Data(Number/LCD_Pow(10,i)+'0');
		i--;
		for(i=i;i>=0;i--)
			Lcd1602_Write_Data(Number/LCD_Pow(10,i)%10+'0');	
	}//显示数字
}//写入数据
void Lcd1602_init()
{
	Delay1ms(15);
	Lcd1602_Write_Cmd(0x38);
	Delay1ms(5);
	Lcd1602_Write_Cmd(0x38);
	Lcd1602_Write_Cmd(0x01);
	Lcd1602_Write_Cmd(0x06);
	Lcd1602_Write_Cmd(0x0c);
}//对1062初始化操作

Distance.h

此处使用定时器2作为PWM的计时器,之所以不使用一个定时器始终作为PWM定时器是因为原本的超声波魔术手并没有考虑控制速度,在之后添加控制速度的功能时因为改动整体比较麻烦,所以只把定时器2放在超声波魔术手模块中实现PWM

#define  RX  P2_0			//ECHO
#define  TX  P2_1			//TRIG
unsigned int  time=0;
unsigned int S=0;
bit flag=0;
unsigned int pwm_t2;
unsigned int pwm2_left_val;
unsigned int pwm2_right_val;
void T2_Init()
{
		T2MOD=0;
		T2CON=0;
		EXEN2=0;
		TH2=0xFF;
		TL2=0xCB;
		RCAP2L = 0xCB;
		RCAP2H = 0xFF;		
		TR2=1;
		ET2=1;
		EA=1;
}//T2初始化
void timer2() interrupt 5		//定时器2中断
{
	TF2=0;
	pwm_t2++;
	if(pwm_t2 == 255)
		pwm_t2 = EN1 = EN2 = 0;
	if(pwm2_left_val == pwm_t2)
		EN1 = 1;	
	if(pwm2_right_val == pwm_t2)
		EN2 = 1;		
}
int Conut()
{
	time=TH1*256+TL1;
	TH1=0;
	TL1=0;
	S=(int)((float)(time*1.085)*0.17);     //计算结果为mm
	if((S>=7000)||flag==1) 
	{	 
		flag=0;
		return 0;
	}
	return S;
}
void zd1() interrupt 3 		 //T1中断用来表示计数器溢出,超过测距范围
{						 //中断溢出标志
	flag=1;
}
int distance()
{
	TMOD = 0x10;
	Delay1ms(80);
	TX=1;			                //80MS启动一次模块
	Delay1ms(1);
	TX=0;	
	while(!RX);	//当RX(ECHO信号回响)为0时等待
	TR1=1;			    //开启计数
	while(RX);			//当RX为1是计数并等待
	TR1=0;				//关闭计数
  return Conut();			//计算
}
void keep_dis()
{
	pwm2_left_val=180;
	pwm2_right_val=180;
	
	if(flag_dis<130)	//A lot of speed, but good results
	{
		Delay1ms(10);
		left_motor_en;
		right_motor_en;
		right_motor_back;	
		left_motor_back;		
	}
	else if(flag_dis>230)
	{
		Delay1ms(10);
		left_motor_en;
		right_motor_en;
		right_motor_go;	
		left_motor_go;	
	}
	else
	{
		right_motor_stops;	
		left_motor_stops;		
	}
}

PWM.h

unsigned char pwm_left_val;//Left motor duty cycle
unsigned char pwm_right_val;//Right motor duty cycle
unsigned char pwm_t;//cycle
void timer0() interrupt 1		//¶¨Ê±Æ÷0ÖжÏ
{
	TF0=0;
	pwm_t++;
	if(pwm_t == 255)
		pwm_t = EN1 = EN2 = 0;
	if(pwm_left_val == pwm_t)
		EN1 = 1;	
	if(pwm_right_val == pwm_t)
		EN2 = 1;			 
}
void LRspeed(unsigned char L_speed,unsigned char R_speed)
{
	pwm_left_val=L_speed;
	pwm_right_val=R_speed;	
}

举报
0 条评论