基于AVR64DD32和ESP32-E的蓝牙小车控制系统(Funpack2-4)
本文使用AVR64DD32 Curiosity Nano开发板和 FireBeetle ESP32-E开发板,设计开发了一套蓝牙小车控制系统。该系统主要包括遥控和车体两部分,通过操作遥控手柄,可实现四驱车的前进、后退、转向、停止等功能。
标签
Arduino
蓝牙
ESP32-E
AVR64DD32
Funpack2-4
手势控制
chinaking
更新2023-04-23
上海大学
443

基于AVR64DD32和ESP32-E的蓝牙小车控制系统

0 引言

        本人曾于2022年3月Funpack第一季结束时,在电子森林发布了《基于Arduino Nano33 BLE和Wio Terminal的遥控小车设计》,当时使用的是Funpack第一季第8期和第12期的开发板。

        此次Funpack第二季第4期也是机缘巧合,准备把原遥控小车的硬件更新一下,于是将车体控制板Arduino Nano33 BLE换成了AVR64DD32 Curiosity Nano,将遥控器控制板由Wio Terminal换成了FireBeetle ESP32-E及扩展板,同时又增加了一点新功能,将Funpack2-3和Funpack2-4的开发板物尽其用。

1 概述

        本文使用Microchip公司AVR64DD32 Curiosity Nano开发板和DFRobot公司 FireBeetle ESP32-E开发板,设计开发了一套蓝牙小车控制系统。该系统主要包括遥控和车体两部分,通过操作遥控手柄,可实现四驱车的前进、后退、转向、停止等功能。

Flolr5Uv68myAmEQxLGrhoPhpd-I

图1  遥控车体及遥控器

       为便于测试,车体配备了一个手势传感器,可用手势操作,进行本地测试,验证电机驱动功能。

2  硬件设计

项目用到的主要硬件清单见表1

表1 项目关键硬件清单

序号

名称

型号

数量

供应商

1

车体控制板

AVR64DD32 Curiosity Nano

1

Microchip

2

电机驱动板

TB6612FNG

1

TOSHIBA(东芝)

3

手势传感器

PAJ7620U2

1

DFRobot

4

蓝牙接收模块

HC-05

1

汇承

5

遥控控制板

FireBeetle ESP32-E

1

DFRobot

6

扩展板

 

1

硬禾学堂

 

       车体以AVR64DD32开发板为核心,配合HC-05蓝牙模块、TB6612FNG电机驱动模块,共同完成蓝牙数据接收和电机控制功能。

车体部分的硬件连接图如图2所示。

FkAn3LmypuybpFFUC6GIGfYv-nGF

                                                                   图2 遥控车车体接线图

      AVR64DD32开发板的PC2、PC3、PD1控制1组电机。其中PC2、PC3作为开关量输出,连接到电机驱动板TB6612FNG的AIN1和AIN2,控制小车左侧电机的方向;PD1作为PWM输出,连接到电机驱动板的PWMA,控制小车左侧电机的速度。PF2、PF3、PD2控制小车右侧另1组电机。为便于观察开发板的输出信号,在PC2、PC3、PF2、PF3都接了LED指示灯,用于显示小车的状态,如前进、后退、转弯等。

        开发板的PF4脚接了一个开关,用来进行模式切换。当开关在本地模式时,可通过手势传感器对小车进行控制;当开关在远程模式时,通过蓝牙对小车进行控制。

        手势传感器通过I2C接口与AVR64DD32开发板进行连接;蓝牙模块HC-05连接在开发板的UART1端口。

        遥控器以ESP32-E开发板为核心,配合硬禾学堂的扩展板,完成遥控操作、蓝牙传输、状态显示等功能。遥控器的硬件连接图如图3所示.

FjAry_G9ue3aBz2GGoglkctfMVPx

图3 遥控器硬件接线图

        遥控器的主控板为ESP32-E,它与硬禾学堂扩展板进行连接。通过SPI口连接彩色显示屏、通过开关量输入端口采集手柄的PWM信号,此外扩展板的三色LED也由主控板进行控制,用来显示车体状态。

3  软件设计

编写代码前,需要先对HC-05蓝牙模块进行配置。首先通过USB转TTL模块连接电脑与蓝牙模块,然后使用XCOM串口调试软件对HC-05模块进行配置。

本系统将HC-05配置成从站

AT+ORGL   //恢复出厂设置,恢复后需重新连接

AT+NAME=LAOHU  //设备名称

AT+ROLE=0   //从站模式

AT+CMODE=1   //任意蓝牙地址连接模式(不受绑定指令设置地址的约束)

AT+PSWD=1234  //密码

AT+UART=9600,0,0  //设置波特率

AT+ADDR?   // 获取地址

然后打开Arduino软件,在首选项里配置开发板网址。

FtO9cEb5c2i7ZQ2NYTYjBjdGgvu2

添加下面的网址

http://download.dfrobot.top/FireBeetle/package_DFRobot_index.json

http://drazzy.com/package_drazzy.com_index.json

然后在Arduino软件中安装开发板和库文件。

FkUa-EyqyLN-ewVXNs04eeN0g7BZ

FqmhEOJuit7vUUkiLbpSCSSCNcup

FifmJGMQwVjpe2G62G5OdWZVuPTm

FqJe-64OQoyRTxAN7hcJctSxgE8n

根据扩展板屏幕硬件参数信息,修改TFT_eSPI库配置文件

打开C:\Users\Administrator\Documents\Arduino\libraries\TFT_eSPI目录,修改

User_Setup.h 文件

 

#define ST7735_DRIVER   //设置显示芯片型号

#define TFT_WIDTH  130  //设置屏幕大小,要设置的比128*128略大,否则会看到花屏的边线

#define TFT_HEIGHT 135

                                     //设置TFT屏控制脚对应的引脚

#define TFT_CS   D10  

#define TFT_DC   D11  

#define TFT_RST  -1   // -1表示使用硬件Reset脚

#define TOUCH_CS D9  

下面是主要代码内容

车体代码

#include <Wire.h>
#include <SoftwareSerial.h> 
#include "paj7620.h"

#define AVR_LED PIN_PF5
#define Drive_AIN1 PIN_PC2
#define Drive_AIN2 PIN_PC3
#define Drive_PWMA PIN_PD1

#define Drive_BIN1 PIN_PF2
#define Drive_BIN2 PIN_PF3
#define Drive_PWMB PIN_PD2

#define PIN_MODE PIN_PF4  //模式选择开关  0-本地模式  1-遥控模式

#define Speed_PWMA 100     //A通道速度 0-255
#define Speed_PWMB 100    //B通道速度  0-255

//SoftwareSerial UART0(PIN_PD5, PIN_PD4); //调试下载监控串口,不定义时用硬件串口,但硬件串口有时候不打印
 SoftwareSerial UART1(PIN_PC1, PIN_PC0); //AVR接蓝牙模块串口

char carRcvData;//蓝牙接收的原始数据
char carState;//蓝牙数据过滤处理 1-前进 2-后退 3-左转 4-右转 5-停止

int WorkMode=1;//工作模式 0-遥控 1-本地
int iCarCmd; //小车控制命令
void setup()
{
  Serial.begin(9600);// UART0.begin(9600)
  UART1.begin(9600);
  uint8_t error = 0;
  error = paj7620Init();      // initialize Paj7620 registers
      if (error) 
      {
        Serial.print("INIT ERROR,CODE:");
        Serial.println(error);
      }
      else
      {
        Serial.println("INIT OK");
      }
      Serial.println("Please input your gestures:\n");
 
  pinMode(PIN_MODE, INPUT); 
  pinMode(AVR_LED, OUTPUT);
  digitalWrite(AVR_LED, HIGH);////初始化,先把LED关掉
  
  pinMode(Drive_AIN1, OUTPUT); //小车左侧两个电机控制,电机并联。AIN1=1=1,AIN2=0正转;AIN1=1=0,AIN2=1反转;AIN1=1=0,AIN2=0,停止
  pinMode(Drive_AIN2, OUTPUT);
  pinMode(Drive_PWMA, OUTPUT);//电机转速控制,0-255对应转速0-100%
  
  pinMode(Drive_BIN1, OUTPUT); //小车右侧两个电机控制
  pinMode(Drive_BIN2, OUTPUT);
  pinMode(Drive_PWMB, OUTPUT);

  digitalWrite(Drive_AIN1, LOW);//初始化
  digitalWrite(Drive_AIN2, LOW);
  analogWrite(Drive_PWMA,Speed_PWMA);   

  digitalWrite(Drive_BIN1, LOW);//初始化
  digitalWrite(Drive_BIN2, LOW);
  analogWrite(Drive_PWMB,Speed_PWMB); 
}

void loop()
{ 
  WorkMode=digitalRead(PIN_MODE);//获取工作模式

  //接收手势指令
  if(WorkMode==1)  //本地模式,手势控制
  {
  digitalWrite(AVR_LED, HIGH);//LED灭
  uint8_t data = 0, error;
  error = paj7620ReadReg(0x43, 1, &data); 
      if (!error) 
      {
           delay(50);
          if (data==1)   //手势向右,小车右转
          { iCarCmd=4;  }
          else  if (data==2) //手势向左,小车左转
          { iCarCmd=3; }
          else  if (data==4) //手势向上,小车前进
          { iCarCmd=1; }
           else  if (data==8)   //手势向下  小车后退
           { iCarCmd=2;  }     
          else  if ((data==16)||(data==32)) //手势靠近或远离传感器,小车停止
           { iCarCmd=5;  }
        }
      delay(50);
      }
  else   //WorkMode==0  远程遥控模式    接收蓝牙遥控指令
  {
     digitalWrite(AVR_LED, LOW);//LED亮
             if (UART1.available()) 
           {
                 carRcvData=UART1.read();
                 if ((carRcvData=='1') || (carRcvData=='2') || (carRcvData=='3') || (carRcvData=='4') || (carRcvData=='5'))
                 {carState=carRcvData;
                  Serial.println(carState);
                  }
                  if (carState=='1')
                   {iCarCmd=1; }
                   else if (carState=='2')
                   { iCarCmd=2; }
                   else if (carState=='3')
                   { iCarCmd=3;}
                   else if (carState=='4')
                   {iCarCmd=4;}
                   else if (carState=='5')
                   {iCarCmd=5; }  
            }
             
   }

//控制小车运动
   if (iCarCmd==1)
   {       
        digitalWrite(Drive_AIN1, HIGH);
        digitalWrite(Drive_AIN2, LOW);
        analogWrite(Drive_PWMA,Speed_PWMA);

        digitalWrite(Drive_BIN1, HIGH);
        digitalWrite(Drive_BIN2, LOW);
        analogWrite(Drive_PWMB,Speed_PWMB);
    }

    else if (iCarCmd==2)
   {      
        digitalWrite(Drive_AIN1, LOW);
        digitalWrite(Drive_AIN2, HIGH);
        analogWrite(Drive_PWMA,Speed_PWMA);   

        digitalWrite(Drive_BIN1, LOW);
        digitalWrite(Drive_BIN2, HIGH);
        analogWrite(Drive_PWMB,Speed_PWMB);
   }
       else if (iCarCmd==3)
   {      
      
         digitalWrite(Drive_AIN1, LOW);
         digitalWrite(Drive_AIN2, HIGH);
         analogWrite(Drive_PWMA,Speed_PWMA);
         
         digitalWrite(Drive_BIN1, HIGH);
         digitalWrite(Drive_BIN2, LOW);
         analogWrite(Drive_PWMB,Speed_PWMB);
   }
       else if (iCarCmd==4)
   {      
       
         digitalWrite(Drive_AIN1, HIGH);
         digitalWrite(Drive_AIN2, LOW);
         analogWrite(Drive_PWMA,Speed_PWMA);
        
         digitalWrite(Drive_BIN1, LOW);
         digitalWrite(Drive_BIN2, HIGH);
         analogWrite(Drive_PWMB,Speed_PWMB);
   }
          else 
   {      
    
        digitalWrite(Drive_AIN1, LOW);
        digitalWrite(Drive_AIN2, LOW);
        analogWrite(Drive_PWMA,Speed_PWMA);   

        digitalWrite(Drive_BIN1, LOW);
        digitalWrite(Drive_BIN2, LOW);
        analogWrite(Drive_PWMB,Speed_PWMB);    
   }
    
}

遥控代码

#include "BluetoothSerial.h"

/*
 #########################################################################
 ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
 C:\Users\Administrator\Documents\Arduino\libraries\TFT_eSPI
 #########################################################################
 */
#include <TFT_eSPI.h>        //TFT屏驱动
TFT_eSPI tft; 
TFT_eSprite spr=TFT_eSprite(&tft);

int PIN_LED1 = D12;//蓝
int PIN_LED2 = D13;//绿
int PIN_LED3 = D2;//红
int LED_State;
int iAdd;

int PIN_Encoder = A0;//编码器引脚
int EncoderValue;

int PIN_PWM_IN = D3;  //手柄引脚  也可以#define PIN_PWM_IN D3  //传感器的输出脚
int cycleTime,time_L,time_H;//PWM周期,低电平时长ms,高电平时长ms
float PWM_DutyCycle;  //PWM占空比
float PWM_Frequency;  //频率
float PWM_CycleTime;  //周期

char CAR_State;//1-前进,2-后退,3-左转,4-右转,5-停止

BluetoothSerial SerialBT;
String MACadd = "00:21:07:00:0B:84";//00,21,07,00,0B,84 蓝牙MAC地址
uint8_t address[6]  = {0x00, 0x21, 0x07, 0x00, 0x0B, 0x84};
String name = "LAOHU"; //蓝牙从站名称
const char *pin = "1234";  //<- 默认情况下将提供标准管脚
bool connected;

void setup() {
pinMode(PIN_LED1, OUTPUT); 
pinMode(PIN_LED2, OUTPUT); 
pinMode(PIN_LED3, OUTPUT); 

pinMode(PIN_Encoder, INPUT); 
pinMode(PIN_PWM_IN, INPUT);

tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
//spr.createSprite(TFT_HEIGHT,TFT_WIDTH);
spr.createSprite(128,128);

  Serial.begin(9600);
  
  SerialBT.begin("ESP32test", true);   
  Serial.println("The device started in master mode, make sure remote BT device is on!");

  // 连接(address)速度快(最多10秒),连接(name)速度慢(最多30秒)
  // 首先将名称解析为地址,但它允许连接到同名的不同设备。
  // 将CoreDebugLevel设置为Info以查看设备蓝牙地址和设备名称
  connected = SerialBT.connect(name);
  if(connected) {
    Serial.println("Connected Succesfully!");
  } else {
    while(!SerialBT.connected(10000)) {
      Serial.println("Failed to connect. Make sure remote device is available and in range, then restart app."); 
    }
  }
  // disconnect() 最多可能需要10秒
  if (SerialBT.disconnect()) {
    Serial.println("Disconnected Succesfully!");
  }
  // 这将重新连接到名称(如果解析,将使用地址)或与connect一起使用的地址(名称/地址)。
  SerialBT.connect();

  
}

void loop() {
    spr.createSprite(128, 128); 
    spr.drawString("LED State",10,10);   // spr.print("LED State:");   带冒号会闪屏
    spr.drawNumber(LED_State,80,10);

//处理编码器模拟量数值或按键按下后的模拟量电压值
    EncoderValue = analogRead(PIN_Encoder);
    spr.drawString("Encoder Value",10,30); 
    spr.drawNumber(EncoderValue,100,30);

 
    
    //通过检测高低电平长度,计算周期
    time_L = pulseIn(PIN_PWM_IN, LOW); //检测低电平的时间长度 ms
    time_H = pulseIn(PIN_PWM_IN, HIGH);//检测高电平的时间长度 ms
    cycleTime=time_L+time_H;
    PWM_CycleTime=float(cycleTime);//周期
    PWM_Frequency=10000.0/float(cycleTime);//频率
    
    float f_timeL, f_timeH;
    f_timeL=time_L/10.0;
    f_timeH=time_H/10.0;
    PWM_DutyCycle=f_timeH/(f_timeL+f_timeH); //占空比

    spr.drawString("CycleTime",10,60); 
    spr.drawNumber(cycleTime,70,60);   

    spr.drawString("DutyCycle",10,90); 
    spr.drawFloat(PWM_DutyCycle,2,70,90);   



     spr.drawChar(CAR_State,110,110);
    //动画显示  编码器模拟量
    int EncoderRectWidth;
    EncoderRectWidth= int(EncoderValue/40.95);   //0-4095 转换成0-100
    spr.fillRect(5, 45, EncoderRectWidth, 10, TFT_RED);   //屏幕矩形显示电压  x坐标 y坐标 宽度  高度 
    
    //周期
    int PwmT_RectWidth;
    PwmT_RectWidth= int(222-PWM_CycleTime*0.047);   //线性化 4700-2444     转换成0-100
    spr.fillRect(5, 75, PwmT_RectWidth, 10, TFT_GREEN);   //屏幕矩形显示电压  x坐标 y坐标 宽度  高度 

    //动画显示 PWM 占空比  Y轴 
    int PwmD_RectWidth;
    PwmD_RectWidth= int((PWM_DutyCycle-0.31)*212.77); //线性化 0.33-0.80 转换成0-100
    spr.fillRect(5, 105, PwmD_RectWidth, 10, TFT_BLUE);   //屏幕矩形显示电压  x坐标 y坐标 宽度  高度 

      if ((PwmD_RectWidth>0) && (PwmD_RectWidth<40))
    {CAR_State='1';//前进
     LED_State=2; //绿灯
    }
    else if (PwmD_RectWidth>60)
    {CAR_State='2';//后退
    LED_State=3; //红灯
    }
    else if ((PwmT_RectWidth>0) && (PwmT_RectWidth<40))
    {CAR_State='3';
     LED_State=4; //黄灯
    }
    else if (PwmT_RectWidth>60)
    {CAR_State='4';
     LED_State=4; //黄灯
    }
    else
    {CAR_State='5';
     LED_State=1; //蓝灯
      }

    
    if  (LED_State==1 )
    {
    digitalWrite(PIN_LED1, LOW);//低电平有效 蓝
    digitalWrite(PIN_LED2, HIGH);
    digitalWrite(PIN_LED3, HIGH);}
    else if  (LED_State==2 )
    {
    digitalWrite(PIN_LED1, HIGH);
    digitalWrite(PIN_LED2, LOW);//绿
    digitalWrite(PIN_LED3, HIGH);}
    else if  (LED_State==3 )
    {
    digitalWrite(PIN_LED1, HIGH);
    digitalWrite(PIN_LED2, HIGH);
    digitalWrite(PIN_LED3, LOW);}//红
    else  if  (LED_State==4 )
    {
    digitalWrite(PIN_LED1, HIGH);
    digitalWrite(PIN_LED2, LOW);//绿     红绿组合成黄灯
    digitalWrite(PIN_LED3, LOW);//红
    }
    
    spr.pushSprite(0,0);
    spr.deleteSprite();//刷新屏幕

   

   SerialBT.write(CAR_State);
   Serial.println(CAR_State);
   delay(20);
   
}

4  演示效果

静止状态时,车体开发板两侧四个指示灯都熄灭,遥控器指示灯蓝色;

小车前进时,车体开发板两侧绿色指示灯点亮,遥控器指示灯绿色;

小车后退时,车体开发板两侧红色指示灯点亮,遥控器指示灯红色;

小车左转时,车体开发板左侧红色指示灯点亮,右侧绿色指示灯点亮,遥控器指示灯黄色;

小车右转时,车体开发板左侧绿色指示灯点亮,右侧红色指示灯点亮,遥控器指示灯黄色。

FuTnwAUfMH8Lph5EpGmoomuKALdd

5  心得体会

      蓝牙模块的波特率需设置一致,开始时HC-05使用的默认波特率38400,而手机蓝牙和ESP32-E都是9600,测试时出现收发数据不一致的情况,后来重新设置了HC-05的波特率为9600,解决了上述问题。

     蓝牙数据过滤、数据格式。开发板接收蓝牙数据时,应进行过滤,只接收1、2、3、4、5等指定数据,并以字符形式发送,防止受到干扰。使用蓝牙透明传输和BLE还是有所区别,BLE相当于带协议,可以直接对应数据,如果用蓝牙传输多个数据时,还需要自己定义数据格式。

     Arduino对AVR64DD32开发板编程时,建议使用版本1.8.19,使用高版本时会出现无法下载的问题。AVR64DD32使用硬件串口时,有时串口无输出,需改用softwareSerial再定义一次,具体原因不明。

 

 

软硬件
电路图
附件下载
AVR_PAJ7620U2.ino
车体程序
Bluetooth_Radio.ino
遥控程序
团队介绍
老胡,2012年硕士毕业于上海某高校,控制理论与控制工程专业,电子爱好者。
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号