Arduino/stm32 智能小车设计(二)

Arduino/stm32 智能小车设计(二)

本节进行Arduino智能小车的代码讲解。

一、I/O口定义与初始化函数

如下图RGB指示灯用到10、11、12三个I/O口,分别对应蓝色、红色、绿色。当给对应的I/O口低电平时,对应颜色的灯亮。也可以用PWM进行调色,实现不同的颜色。这里先把控制端的原理图粘贴上来以便后面原理说明。
在这里插入图片描述
Arduino封装了很多底层的细节,使得学习者不需要太多的关注底层电路的原理。
Arduino一共有14个数字I/O口,分别为0-13,定义与初始化看代码;6个模拟I/O口,分别为A0-A5,定义与初始化看代码。
已下是所有I/O端口定义:

//0:TDX蓝牙  	1:RXD蓝牙  		2:DATA红外遥控
//3:蜂鸣器   	4:按键     		5:IN2左电机  
//6:IN3右电机 	7:IN4右电机     8:IN1左电机 
//9:悬空      	10:RGB_B     	11:RGB_R
//12:RGB_G     				13:流水灯
//A0:Echo超声波回声脚       		A1:Trig超声波触发脚  
//A2:左循迹输入   			    A3:右循迹输入
//A4:右壁障输入   				A5:左壁障输入

//I/O口 0、1 是Arduino唯一的一组串口通信口(代码里无需定义)。用于蓝牙数据的接发,具体串口通信的知识可以看我的另外一篇博客了解。链接:
https://blog.csdn.net/weixin_43729724/article/details/101287363
//数字端口定义
int DATA= 2;		//红外数据输入端口
int BEEP=3;        	//蜂鸣器输出 
int KEY=4;         	//按键输入  
int IN2=5;          //左电机
int IN3=6;          //右电机 
int IN4=7;         	//右电机
int IN1=8;        	//左电机
         			//9脚悬空,不需要定义
int RGB_B=10;       //RGB蓝色 
int RGB_R=11;		//RDB红色
int RGB_G=12;  		//RGB绿色     
int Water_L=13;     //流水灯I/O
//模拟I/O口定义
int Trig =A0;  			//Trig 触发脚输出
int Echo = A1;  		//Echo回声脚输入
int XunJi_R = A3;     	//右寻迹输入
int XunJi_L = A2;     	//左寻迹输入
int BiZhang_R = A4;   	//右红外避障输入
int BiZhang_L = A5;     //左红外避障输入

/************************************************************************
  * @描述:  Arduino初始化函数
  * @参数:  None
  * 功能:	定义I/O口模式,并赋初值
  * @返回值: None
**********************************************************************/
void setup()
{
	  Serial.begin(9600);	//波特率9600 (蓝牙通讯设定波特率)
	  pinMode(DATA,INPUT);
	  pinMode(BEEP,OUTPUT); 
	  pinMode(KEY,INPUT); 
	  pinMode(IN2,OUTPUT);
	  pinMode(IN3,OUTPUT);
	  pinMode(IN4,OUTPUT);
	  pinMode(IN1,OUTPUT);  
	  pinMode(RGB_B,OUTPUT);
	  pinMode(RGB_R,UTPUT); 
	  pinMode(RGB_G,UTPUT); 
	  pinMode(Water_L,OUTPUT);
	  pinMode(Trig,OUTPUT); 
	  pinMode(Echo,INPUT);
	  pinMode(XunJi_R,INPUT);
	  pinMode(XunJi_L,INPUT);
	  pinMode(BiZhang_R,INPUT);
	  pinMode(BiZhang_L,INPUT);
	  
	  digitalWrite(BEEP,LOW);  
	  digitalWrite(RGB_B,HIGH);
	  digitalWrite(RGB_R,HIGH); 
	  digitalWrite(RGB_G,HIGH); 
	  digitalWrite(Water_L,HIGH);
}

二、前进、后退、左转、右转、左旋转、右旋转API函数

原理图如下:在这里插入图片描述
电机驱动原理:L293DD是一款专用的电机驱动芯片,因为I/O口驱动电流受限,而电机运转需要高电流驱动。当8高5低时,电流从OUT1->OUT2驱动做电机正转,反之亦凡。I/O口6、7驱动右电机运动。
代码如下:

/************************************************************************
  * @描述:  
  * @参数:  speed:控制速度0-255
  * 功能:	小车前进
  * @返回值: None
**********************************************************************/
void run(int speed) 
{
  analogWrite(IN1,speed);//PWM比例0~255调速,左右轮差异略增减
  digitalWrite(IN2,LOW);
  analogWrite(IN4,speed);//PWM比例0~255调速,左右轮差异略增减
  digitalWrite(IN3,LOW); 
}
/************************************************************************
  * @描述:  
  * @参数:  None
  * 功能:	刹车
  * @返回值: None
**********************************************************************/
void brake() 
{
  digitalWrite(IN1,LOW);
  digitalWrite(IN2,LOW);
  digitalWrite(IN3,LOW);
  digitalWrite(IN4,LOW);
}
/************************************************************************
  * @描述:  左转(左轮不动,右轮前进)
  * @参数:  speed:控制速度0-255
  * 功能:	小车左转
  * @返回值: None
**********************************************************************/
void left(int speed)
{
  digitalWrite(IN1,LOW);
  digitalWrite(IN2,LOW);
  analogWrite(IN4,speed);//PWM比例0~255调速,右轮前进
  digitalWrite(IN3,LOW); 
}
/************************************************************************
  * @描述:  左旋转(左轮后退,右轮前进)
  * @参数:  speed:控制速度0-255
  * 功能:	小车左旋转
  * @返回值: None
**********************************************************************/
void spin_left(int speed) 
{
  digitalWrite(IN1,LOW);
  analogWrite(IN2,speed);
  analogWrite(IN4,speed);
  digitalWrite(IN3,LOW); 
}
/************************************************************************
  * @描述:  右转(左轮前进,右轮不动)
  * @参数:  speed:控制速度0-255
  * 功能:	小车右转
  * @返回值: None
**********************************************************************/
void right(int speed) 
{
  analogWrite(IN1,speed);
  digitalWrite(IN2,LOW);
  digitalWrite(IN4,LOW);
  digitalWrite(IN3,LOW); 
}
/************************************************************************
  * @描述:  右旋转(左轮前进,右轮后退)
  * @参数:  speed:控制速度0-255
  * 功能:	小车右旋转
  * @返回值: None
**********************************************************************/
void spin_right(int speed)
{
  analogWrite(IN1,speed);
  digitalWrite(IN2,LOW);
  digitalWrite(IN4,LOW);
  analogWrite(IN3,speed); 
}
/************************************************************************
  * @描述:  
  * @参数:  speed:控制速度0-255
  * 功能:	小车后退
  * @返回值: None
**********************************************************************/
void back(int speed) 
{
  digitalWrite(IN1,LOW);
  analogWrite(IN2,speed);
  digitalWrite(IN4,LOW);
  analogWrite(IN3,speed);  
}

三、红外避障与红外寻迹API函数

红外避障原理:如图红外对管D24由一个发射管和一个接收管组成,发射管发出红外线,当遇到障碍物时反射红外线由接收管接收,使运放IN1-端口电压变化,当调节R16电位器使IN1+电压变化,当IN1+ 的电压大于IN1-的电压时,OUT1输出高电平,被A5接收。
红外寻迹原理:如图红外对管U8由一个发射管和一个接收管组成,发射管发出红外线,当没有探测到黑线时,一直反射红外线被接收管接受,IN4-端口输入固定电压,当探测到黑线时,红外线被吸收,接受管接受不到红外线,IN4-电压降低,对比IN4-、IN4+两端电压,OUT4输出高低电平,被A2接收。
在这里插入图片描述
代码如下:

int BiZhang_L_S;    //左红外传感器状态
int BiZhang_R_S;    //右红外传感器状态
/************************************************************************
  * @描述:  当没有探测到障碍物时,A4、A5为高电平,当探测到高电平时为低电平
  * @参数:  speed:控制速度0-255
  * 功能:	红外避障
  * @返回值: None
**********************************************************************/
void Infrared_Avoid(int speed)
{
    //有信号为LOW  没有信号为HIGH
    BiZhang_R_S = digitalRead(BiZhang_R);
    BiZhang_L_S = digitalRead(BiZhang_L);
    if (BiZhang_L_S == HIGH && BiZhang_R_S == HIGH)
    	run(speed);
    else if (BiZhang_L_S == HIGH & BiZhang_R_S == LOW)
        left(speed);
    else if (BiZhang_R_S == HIGH & BiZhang_L_S == LOW)
      	right(speed);
    else // 都是有障碍物
      	spin_right(speed);
}
int XunJi_L_S;    	//左循迹红外传感器状态
int XunJi_R_S;    	//右循迹红外传感器状态
/************************************************************************
  * @描述:  当没有探测到黑线时,A4、A5为高电平
  * @参数:  speed:控制速度0-255
  * 功能:	红外寻迹
  * @返回值: None
**********************************************************************/
void Find_Tracking(int speed)
{ 
	XunJi_R_S = digitalRead(XunJi_R);
	XunJi_L_S = digitalRead(XunJi_L);
	if (XunJi_R_S ==LOW && XunJi_L_S == LOW)
		run(speed); 
	else if (XunJi_L_S == LOW & XunJi_R_S == HIGH)
		right(speed);
	else if (XunJi_R_S == LOW & XunJi_L_S ==  HIGH) 
		left(speed);
	else
		brake();
}

四 、超声波测距原理

超声波模块如图:
在这里插入图片描述
产品特色:
1、典型工作用电压:5V。
2、超小静态工作电流:小于2mA。
3、感应角度:不大于15 度。
4、探测距离:2cm-400cm
5、高精度:可达0.3cm。
6、盲区(2cm)超近。
超声波时序如图:
在这里插入图片描述
原理图如下:
在这里插入图片描述
代码如下:

/************************************************************************
  * @描述:  
  * @参数:  None
  * 功能:	量出前方距离
  * @返回值: int:距离(单位:CM)
**********************************************************************/
int Ultrasonic_Distance() 
{
  digitalWrite(Trig, LOW);   //给触发脚低电平2μs
  delayMicroseconds(2);
  digitalWrite(Trig, HIGH);  //给触发脚高电平12μs,这里至少是10μs
  delayMicroseconds(12);
  digitalWrite(Trig, LOW);   //持续给触发脚低电
  float Time = pulseIn(Echo, HIGH);  //读取高电平时间(单位:微秒)
  return Time/58; //公式:us/58 = CM
}  

五、蓝牙控制原理

由于蓝牙通信用的是 0、1两个通用串行通信I/O口,Arduino官方提供了,串口通信的专用API函数供用户使用,这里我们用的是Serial.available()
函数说明见链接:https://www.arduino.cc/reference/en/language/functions/communication/serial/available/
手机APP或者电脑上位机通过蓝牙发送字符串给给蓝牙模块(蓝牙模块就相当于是一个中转站),Arduino控制板用过0、1串行端口读取到来自蓝牙的数据。然后,我们根据来自APP或者上位机的字符串来判断功能。比如,手机发送:0X00代表前进、0X01 代表后退等等。这样我们只需要用一个switch语句实现蓝牙API函数。
我的代码如下:

int speed = 150;	//小车运动速度
/************************************************************************
  * @描述:  
  * @参数:  None
  * 功能:	蓝牙控制
  * @返回值: int:距离(单位:CM)
**********************************************************************/
void Bluetooth_Control() 
{     
    if (Data_Received)
    {
    	switch(IN_String[1])
      	{
        	case '1':	run(speed);
        				break;
        	case '2': 	back(speed);
        				break;
        	case '3':   left(speed);
        				break;
	        case '4':   right(speed);
	        			break;
	        case '0':  	brake();
	        			break;
	        default:	brake();
	        			break;
       }
       if(IN_String[3] == '1')  //旋转
       {
          	spin_left();
       }
       else if(IN_String[3] == '2')  //旋转
       {
          	spin_right();
       }
       if(IN_String[5] == '1')  //鸣笛0.5S
       {
          	digitalWrite(BEEP,HIGH);//发声音
      		delay(500);
	      	digitalWrite(BEEP,LOW);//不发声音
       }
       if(IN_String[7] == '1')  //加速
       {
          	speed +=50;
          	if(speed > 255)  speed = 255;
       }
       if(IN_String[9] == '1')  //减速
       {
          	speed -= 50;
          	if(speed < 50)	speed = 100;
       }           
 	   IN_String = "";   // clear the string
       Data_Received = false;    
     }  
}
int Bluetooth_Data; 			//接收蓝牙的数据
String IN_String = "";  		//用来储存接收到的内容
boolean Start_flag = false;		//数据开始接收的状态标志
boolean Data_Received = false; 	//接收数据结束标志
/************************************************************************
  * @描述:  串口中断函数
  * @参数:  None
  * 功能:	接收来自蓝牙模块的数据
  * @返回值: None
**********************************************************************/
void serialEvent()
{
  while (Serial.available()) 
  {    
    Bluetooth_Data= Serial.read();
    if(Bluetooth_Data== '$')
    {
      Start_flag = true;
    }
    if(Start_flag == true)
    {
       IN_String += (char) Bluetooth_Data;
    }  
    if (Bluetooth_Data== '#') 
    {
       Data_Received = true; 
       Start_flag = false;
    }
  }
}

还有红外遥控的代码不想敲了,个人感觉蓝牙遥控更加方便实用。
接下来写一个综合例程:

#define KEYMODE_1   1  // 定义模式1
#define KEYMODE_2   2  // 定义模式2
#define KEYMODE_3   3  // 定义模式3
#define KEYMODE_4   4  // 定义模式4
int keyMode = 1; 
/************************************************************************
  * @描述:  按键模式切换
  * @参数:  None
  * 功能:	按键子程序
  * @返回值: None
**********************************************************************/
void KeyScanTask()
{
	static u8 keypre = 0;
	if((keypre == 0 ) && !digitalRead(KEY))
	{
		keypre = 1; //置1,避免持续按下按键时再次进入此函数。
		switch(keyMode)
		{
			case KEYMODE_1:keyMode = KEYMODE_2; break;
			case KEYMODE_2:keyMode = KEYMODE_3; break;
			case KEYMODE_3:keyMode = KEYMODE_4; break;
			case KEYMODE_4:keyMode = KEYMODE_1; break;
			default: break;
		}
	}
	if(digitalRead(KEY))  //按键被放开
	{
		keypre = 0;//置0,允许再次切换LED模式
	}
}
/************************************************************************
  * @描述: 模式处理函数
  * @参数:  None
  * 功能:	任务程序
  * @返回值: None
**********************************************************************/
void LEDTask()
{
	switch(keyMode)
	{
		case KEYMODE_1:	digitalWrite(RGB_B,LOW);
			  			digitalWrite(RGB_R,HIGH); 
	  					digitalWrite(RGB_G,HIGH);
						Infrared_Avoid(200); //红外避障
			  			break;
		case KEYMODE_2:	digitalWrite(RGB_B,HIGH);
			  			digitalWrite(RGB_R,LOW); 
	  					digitalWrite(RGB_G,HIGH);
						Find_Tracking(200); //红外寻迹
			  			break;
		case KEYMODE_3:	digitalWrite(RGB_B,HIGH);
			  			digitalWrite(RGB_R,HIGH); 
	  					digitalWrite(RGB_G,LOW);
						Bluetooth_Control(); //红外寻迹
			  			break;
		case KEYMODE_4:	digitalWrite(RGB_B,LOW);
			  			digitalWrite(RGB_R,LOW); 
	  					digitalWrite(RGB_G,LOW);
						 //红外寻迹,此处可以写一个超声波避障的API 
			  			break;
			default:
         break;
		}
}
/************************************************************************
  * @描述: 
  * @参数:  None
  * 功能:	loop函数相当于 Arm的main函数
  * @返回值: None
**********************************************************************/
void loop()
{ 
	KeyScanTask();
	LEDTask();
}
发布了5 篇原创文章 · 获赞 10 · 访问量 2768

猜你喜欢

转载自blog.csdn.net/weixin_43729724/article/details/101450630