清翔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;	
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 以下是基于STM32库函数的智能小车程序,实现红外避障红外跟随、红外循迹超声波避障功能,并且支持按键配置。 ```c++ #include "stm32f10x.h" #include "delay.h" #include "lcd.h" #include "usart.h" #include "key.h" #include "ir.h" #include "ultrasonic.h" #include "motor.h" void System_Init(void); void Key_Configuration(void); int main(void) { System_Init(); Key_Configuration(); LCD_Init(); USART1_Init(9600); IR_Init(); Ultrasonic_Init(); Motor_Init(); while (1) { if (Key_Scan(KEY1)) { while (Key_Scan(KEY1)); IR_Avoid(); } if (Key_Scan(KEY2)) { while (Key_Scan(KEY2)); IR_Follow(); } if (Key_Scan(KEY3)) { while (Key_Scan(KEY3)); IR_Track(); } if (Key_Scan(KEY4)) { while (Key_Scan(KEY4)); Ultrasonic_Avoid(); } } } void System_Init(void) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOC | RCC_AHBPeriph_GPIOD | RCC_AHBPeriph_GPIOE, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); SysTick_Config(SystemCoreClock / 1000); } void Key_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOB, &GPIO_InitStructure); } ``` 其中,`System_Init()`函数用于初始化系统时钟和各个IO口,`Key_Configuration()`函数用于配置按键GPIO口为上拉输入模式。 在`main()`函数中,通过扫描按键状态来选择不同的功能。当按下`KEY1`时,调用`IR_Avoid()`函数实现红外避障功能;当按下`KEY2`时,调用`IR_Follow()`函数实现红外跟随功能;当按下`KEY3`时,调用`IR_Track()`函数实现红外循迹功能;当按下`KEY4`时,调用`Ultrasonic_Avoid()`函数实现超声波避障功能。 以上只是代码的框架,具体的红外避障红外跟随、红外循迹超声波避障功能实现可以参考其他相关资料。 ### 回答2: STM32智能小车是一款具备红外避障红外跟随、红外循迹超声波避障功能的智能小车。通过使用库函数程序和按键配置c,可以实现对这些功能的控制。以下是具体配置方法: 首先,通过使用库函数,我们可以方便地使用MCU的GPIO接口、定时器等硬件资源,来控制小车的红外传感器和超声波传感器。 针对红外避障功能,我们可以使用库函数对红外传感器进行初始化,使其能够正常工作。随后,通过编写中断服务函数,当红外传感器检测到障碍物时,可以触发相应的动作,例如停车或改变方向。同时,我们还可以利用库函数的定时器功能,实现红外传感器的周期性检测。 针对红外跟随功能,我们可以使用库函数对红外传感器进行初始化,并编写中断服务函数来实现小车根据检测到的红外信号来调整方向。通过按键配置c,我们可以在运行过程中灵活切换红外跟随功能的启用和禁用。 针对红外循迹功能,我们可以使用库函数对红外传感器进行初始化,并编写中断服务函数来实现小车跟随指定路径。通过按键配置c,我们可以在运行过程中切换循迹路径,例如实现左转、右转等操作。 针对超声波避障功能,我们可以使用库函数初始化超声波传感器,并编写中断服务函数来实现小车对前方障碍物的检测。当检测到障碍物时,我们可以触发相应的动作,例如停车或改变方向。同样,按键配置c可以用来在运行过程中启用或禁用超声波避障功能。 总的来说,通过库函数程序的配置和按键c的设定,STM32智能小车红外避障红外跟随、红外循迹超声波避障功能可以得到灵活的控制和调整,使智能小车能够更好地应对不同的运行环境和任务需求。 ### 回答3: STM32智能小车是一种基于STM32单片机的智能控制系统,具备红外避障红外跟随、红外循迹超声波避障等功能。这些功能是通过程序控制和配置实现的,而使用库函数可以简化开发过程。 红外避障是利用红外传感器来检测前方障碍物,并通过控制电机的转向来避开障碍物。库函数可以提供红外传感器接口函数和相关算法,使得开发者只需要通过简单的配置即可实现红外避障功能。 红外跟随是利用红外传感器来检测前方物体的位置,并通过电机的转向控制来始终跟随物体。库函数可以提供红外传感器接口函数和跟随算法,使得开发者只需要按照需求进行配置,即可实现红外跟随功能。 红外循迹是利用红外传感器来检测路径上的黑线,并通过电机的转向控制来沿着黑线行驶。库函数可以提供红外传感器接口函数和循迹算法,开发者只需要设置黑线的阈值和电机的转向规则,即可实现红外循迹功能。 超声波避障是利用超声波传感器来检测前方障碍物的距离,并通过电机的转向控制来避开障碍物。库函数可以提供超声波传感器接口函数和避障算法,使得开发者只需要简单的配置超声波传感器的参数和电机的转向规则,即可实现超声波避障功能。 按键配置是指通过按键来控制小车的运动。库函数可以提供按键扫描函数和相应的中断处理函数,开发者只需要配置按键的引脚和中断触发条件,即可实现按键配置功能。 通过库函数的支持,开发者可以更加轻松地实现STM32智能小车红外避障红外跟随、红外循迹超声波避障功能,并可以通过按键配置来灵活地控制小车的运动。同时,库函数的使用也可以大大缩短开发周期,提高开发效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值