“(SKU:DFR0302)MiniQ 2WD Plus”的版本间的差异

来自DFRobot Product Wiki
跳转至: 导航搜索
miniQ 2WD样例代码
miniQ 2WD PLUS样例代码
第414行: 第414行:
 
byte CheckData1[]={1,3,5};
 
byte CheckData1[]={1,3,5};
 
byte CheckData2[]={1,3,5,7};
 
byte CheckData2[]={1,3,5,7};
 
 
char StartData[]={
 
char StartData[]={
 
0x40,0x42,0x42,0x42,0x42,0xFE,0x42,0x42,0x42,0x42,0xFE,0x42,0x42,0x42,0x42,0x00,/*--  文字:  开  --*/
 
0x40,0x42,0x42,0x42,0x42,0xFE,0x42,0x42,0x42,0x42,0xFE,0x42,0x42,0x42,0x42,0x00,/*--  文字:  开  --*/
第788行: 第787行:
 
             lcd.clear();
 
             lcd.clear();
 
             lcd.graphic_16x16(1,29,RemoteControlModel,4);
 
             lcd.graphic_16x16(1,29,RemoteControlModel,4);
         // IICWritData(SendCommandData[6]);
+
         IICWritData(SendCommandData[6]);
 
             //send Remote data
 
             //send Remote data
 
             while(1)
 
             while(1)
第819行: 第818行:
 
void SerialReadWriteData(void)
 
void SerialReadWriteData(void)
 
{
 
{
  int num=0;
+
   if(Serial1.available())
   if((num = Serial1.available())>0)
+
 
   {
 
   {
     char SerialData[20];
+
     char SerialData = Serial1.read();
    for(int i=0;i<num;i++)
+
     switch(SerialData)
  {
+
    SerialData[i] = Serial1.read();
+
    delay(2);
+
  }
+
     switch(SerialData[5])
+
 
     {
 
     {
       case 0x04:
+
       case 'w':
 
         IICWritData('w');
 
         IICWritData('w');
 
         break;
 
         break;
     case 0x06:
+
     case 'a':
 
         IICWritData('a');
 
         IICWritData('a');
 
         break;
 
         break;
     case 0x05:
+
     case 's':
 
         IICWritData('s');
 
         IICWritData('s');
 
         break;
 
         break;
     case 0x07:
+
     case 'd':
 
         IICWritData('d');
 
         IICWritData('d');
 
         break;
 
         break;

2015年1月20日 (二) 10:52的版本

MiniQ 2WD Plus

目录

概述

miniQ 2WD Plus是一款基于Atmega32u4单片机设计的机器人小车控制板,完全兼容Arduino Leonardo以及各种sheild扩展板接口。

小伙伴们可以在上面搭建各种传感器模块,更难得的是,模块还留有很多焊接口,再也不用自己去买万能板了!

miniQ 2WD Plus即可以作为桌面机器人小车的主控制器,搭建2轮自平衡机器人,也可以做为miniQ 2WD的端口扩展板,通过板子之间的Gadgeteer接口进行通信,怎么样?神奇吧!!!

性能描述

  • 主控芯片: Atmega 32u4
  • 加速度计芯片:ADXL345
  • 陀螺仪芯片: ITG3205
  • 数字IO口:23(其中D17是RX指示灯)
  • 5V数字/模拟口最大允许电流:40 mA
  • 兼容标准Arduino接口
  • 支持USB程序下载
  • 7路PWM通道
  • 具有1个xbee接口(Serial1)
  • 具有1个单总线RGB灯
  • 具有1个9g舵机安装孔
  • 具有万用板功能
  • 可直接安装到MiniQ 2WD和MiniQ 桌面机器人底盘上



管脚定义

Fig1: miniQ 2WD Plus

上面的图片显示miniQ 2wd Plus控制器上所有的功能和扩展接口说明,其中包括:

  • 电源:使用USB供电或电源供电。
  • RGB灯珠:内置驱动IC-WS2811,采用单线式数据线控制
  • 模拟输入接口:完全兼容Leonardo
  • 数字接口:完全兼容Leonardo
  • 复位按键:采用微动独立按键,手感更舒适
  • 2个Gadgteer接口:用于连接各种Gadgteer接口的传感器,也可以用于连接miniQ 2WD进行通信控制
  • 加速度计芯片:采用ADXL345
  • 陀螺仪芯片: 采用ITG3205
  • Xbee接口: 用于无线数据传输
  • 预留TowerPro SG90舵机安装孔
  • 预留UART接口



控制器应用

使用教程

首先我们先单独学习上层板的应用。


加速度计&陀螺仪

MiniQ 2WD Plus集成加速度计,陀螺仪功能。加速度计&陀螺仪数据的读取是通过IIC获取的,输出的数据是相对于X,Y,Z轴的,如果想要获取相对于坐标系的角度值,则要进行换算。当然,由于加速度计过于灵敏,相信很多小伙伴也遇到这样的困难,模块一点儿小抖动,数据的拨动幅度就很大,这里用一个简单的例子,用陀螺仪的数据进行角度修正,并采用软件滤波器滤波,得到相对稳定的角度数据。实验结果可以通过串口助手观察到。
MiniQ plus accel.png
样例代码

#include <Wire.h>
#include <math.h>
#define DEVICE (0x53)    //ADXL345 device address
#define TO_READ (6)        //num of bytes we are going to read each time (two bytes for each axis)

//陀螺仪传感器ITG3205
#define ITGAddress   0x68    //ITG3205的I2C地址(AD0接地)
#define G_SMPLRT_DIV 0x15    //设置采样率的寄存器
#define G_DLPF_FS 0x16     //设置量程、低通滤波带宽、时钟频率的寄存器
#define G_INT_CFG 0x17     //设置中断的寄存器
#define G_PWR_MGM 0x3E    //设置电源管理的寄存器

float xGyro, yGyro, zGyro;      //存放角速度值,温度
int buff[6];                  //存放寄存器高低位值,X、Y、Z轴共6个
byte buff1[6];
// 陀螺仪传感器误差修正的偏移量
int g_offx = -35;
int g_offy = -9;
int g_offz = -30;

void writeRegister(int deviceAddress, byte address, byte val)
{
  Wire.beginTransmission(deviceAddress); 
  Wire.write(address);       
  Wire.write(val);        
  Wire.endTransmission();
}

void readRegister(int deviceAddress, byte address) 
{
  Wire.beginTransmission(deviceAddress);  
  Wire.write(address);        
  Wire.endTransmission(); 
  Wire.beginTransmission(deviceAddress); 
  Wire.requestFrom(deviceAddress, 6);   

  int i = 0;
  while(Wire.available())    
  { buff[i++] = Wire.read(); }
  Wire.endTransmission(); 
}
 
void initGyro()
{
  /*****************************************
 * ITG3205
 * G_SMPLRT_DIV:采样率 = 125Hz
 * G_DLPF_FS:+ - 2000度/秒、低通滤波器5HZ、内部采样率1kHz
 * G_INT_CFG:没有中断
 * G_PWR_MGM:电源管理设定:无复位、无睡眠模式、无待机模式、内部振荡器
   ******************************************/
   
  writeRegister(ITGAddress, G_SMPLRT_DIV, 0x07); //设置采样率
  writeRegister(ITGAddress, G_DLPF_FS, 0x1E); //设置量程、低通滤波带宽、内部采样率
  writeRegister(ITGAddress, G_INT_CFG, 0x00); //设置中断(默认值)
  writeRegister(ITGAddress, G_PWR_MGM, 0x00);    //设置电源管理(默认值)
  
   //Turning on the ADXL345
   writeTo(DEVICE, 0x2D, 0);      
   writeTo(DEVICE, 0x2D, 16);
   writeTo(DEVICE, 0x2D, 8);
}

float getGyroValues()
{
  readRegister(ITGAddress, 0x1D); //读取陀螺仪ITG3205的数据
  xGyro = ((buff[0] << 8) | buff[1]) + g_offx;
  yGyro = ((buff[2] << 8) | buff[3]) + g_offy;
  zGyro = ((buff[4] << 8) | buff[5]) + g_offz;
  
  return xGyro;
}

float getadxvalues()
{
  int regAddress = 0x32;    //first axis-acceleration-data register on the ADXL345
  int x, y, z;
 
 readFrom(DEVICE, regAddress, TO_READ, buff1); //read the acceleration data from the ADXL345
  //each axis reading comes in 10 bit resolution, ie 2 bytes.  Least Significat Byte first!!
  //thus we are converting both bytes in to one int
 x = (((int)buff1[1]) << 8) | buff1[0];   
 y = (((int)buff1[3])<< 8) | buff1[2];
 z = (((int)buff1[5]) << 8) | buff1[4];

 float Rx = x * 0.0039 + 0.035;
 float Ry = y * 0.0039 + 0.035;
 float Rz = z * 0.0039 + 0.040;
// Rx = abs(Rx);
 float R = sqrt( Rx*Rx + Ry*Ry + Rz*Rz );
 float Axr = acos(Rx/R) * 57.2958;
 
 return Axr;
}

 //******卡尔曼参数************
float Gyro_y;        //Y轴陀螺仪数据暂存
float Angle_gy;      //由角速度计算的倾斜角度
float Accel_x;             //X轴加速度值暂存
float Angle_ax;      //由加速度计算的倾斜角度
float Angle;         //小车最终倾斜角度
//uchar value;                 //角度正负极性标记                  
float  Q_angle=0.001;  
float  Q_gyro=0.003;
float  R_angle=0.5;
float  dt=0.01;                          //dt为kalman滤波器采样时间;
char   C_0 = 1;
float  Q_bias, Angle_err;
float  PCt_0, PCt_1, E;
float  K_0, K_1, t_0, t_1;
float  Pdot[4] ={0,0,0,0};
float  PP[2][2] = { { 1, 0 },{ 0, 1 } };
float kalmanUpdate(float Accel,float Gyro)
{
   Angle+=(Gyro - Q_bias) * dt; //先验估计
     
   Pdot[0]=Q_angle - PP[0][1] - PP[1][0]; // Pk-先验估计误差协方差的微分

   Pdot[1]=- PP[1][1];
   Pdot[2]=- PP[1][1];
   Pdot[3]=Q_gyro;
   
   PP[0][0] += Pdot[0] * dt;   // Pk-先验估计误差协方差微分的积分
   PP[0][1] += Pdot[1] * dt;   // =先验估计误差协方差
   PP[1][0] += Pdot[2] * dt;
   PP[1][1] += Pdot[3] * dt;
           
   Angle_err = Accel - Angle;        //zk-先验估计
  
   PCt_0 = C_0 * PP[0][0];
   PCt_1 = C_0 * PP[1][0];
   
   E = R_angle + C_0 * PCt_0;
   
   K_0 = PCt_0 / E;
   K_1 = PCt_1 / E;
   
   t_0 = PCt_0;
   t_1 = C_0 * PP[0][1];

   PP[0][0] -= K_0 * t_0;                 //后验估计误差协方差
   PP[0][1] -= K_0 * t_1;
   PP[1][0] -= K_1 * t_0;
   PP[1][1] -= K_1 * t_1;
           
   Angle        += K_0 * Angle_err;         //后验估计
   Q_bias        += K_1 * Angle_err;         //后验估计
   Gyro_y   = Gyro - Q_bias;         //输出值(后验估计)的微分=角速度             
}

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  initGyro();
  delay(50);
}


void loop()
{
  float Accel_x = getadxvalues();
  float Gyro_x = getGyroValues()/14.375;
  
  kalmanUpdate( Accel_x, Gyro_x );
  
  Angle = Angle + (((Accel_x-Angle)*0.5 + Gyro_y)*0.001);
  Serial.print(Accel_x);
  Serial.print("   ");
  Serial.println(Angle);
}

//---------------- Functions
//Writes val to address register on device
void writeTo(int device, byte address, byte val) {
  Wire.beginTransmission(device); //start transmission to device 
  Wire.write(address);        // send register address
  Wire.write(val);        // send value to write
  Wire.endTransmission(); //end transmission
}

//reads num bytes starting from address register on device in to buff array
void readFrom(int device, byte address, int num, byte buff[])
{
 Wire.beginTransmission(device); //start transmission to device 
 Wire.write(address);        //sends address to read from
 Wire.endTransmission(); //end transmission
 
 Wire.beginTransmission(device); //start transmission to device
 Wire.requestFrom(device, num);    // request 6 bytes from device
 
 int i = 0;
 while(Wire.available())    //device may send less than requested (abnormal)
 { 
   buff[i] = Wire.read(); // receive a byte
   i++;
 }
 Wire.endTransmission(); //end transmission
}




RGB控制

MiniQ 2WD plus 内置一个RGB灯珠WS2812B-4,其内置驱动IC-WS2811,采用单线通信方式控制灯珠。
样例展示灯珠颜色变换、闪烁。

DFR0302 light test.JPG
样例代码
#include <Adafruit_NeoPixel.h>

#define PIN 4

Adafruit_NeoPixel strip = Adafruit_NeoPixel(150, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}

void loop() {
  // Some example procedures showing how to display to the pixels:
  colorWipe(strip.Color(255, 0, 0), 50); // Red
  colorWipe(strip.Color(0, 255, 0), 50); // Green
  colorWipe(strip.Color(0, 0, 255), 50); // Blue
  // Send a theater pixel chase in...
  theaterChase(strip.Color(127, 127, 127), 50); // White
  theaterChase(strip.Color(127,   0,   0), 50); // Red
  theaterChase(strip.Color(  0,   0, 127), 50); // Blue

  rainbow(20);
 // rainbowCycle(20);
 // theaterChaseRainbow(50);
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
}

void rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) 
  {
    for(i=0; i<strip.numPixels(); i++)
    {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();
     
      delay(wait);
     
      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) {
        for (int i=0; i < strip.numPixels(); i=i+3) {
          strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
        }
        strip.show();
       
        delay(wait);
       
        for (int i=0; i < strip.numPixels(); i=i+3) {
          strip.setPixelColor(i+q, 0);        //turn every third pixel off
        }
    }
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) 
{
  if(WheelPos < 85)
  {
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } 
  else if(WheelPos < 170) 
  {
   WheelPos -= 85;
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } 
  else 
  {
   WheelPos -= 170;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}





综合应用



miniQ 2WD PLUS & miniQ 2WD

MiniQ 2WD与PLUS上层板是天生的一对。MiniQ 2WD PLUS 和 MiniQ 2WD采用IIC通信,通过Gadgteer连接上下板。MiniQ 2WD PLUS作为IIC主机,MiniQ 2WD作为IIC从机,MiniQ 2WD PLUS通过无线接收命令数据,然后解析,通过IIC发送命令MiniQ 2WD小车底板,作 出相应的动作命令。

俺写了个两个板子的应用程序,希望多小伙伴们有用。
这里我想特别说下在“运动功能”中,有“巡线模式”、“寻光模式”、“壁障模式”、“遥控模式”,其中巡线采用的是传感器可调节原理,也就是说,小车可以根据每次使用的巡线场景不同而进行实地调节的。具体操作如下,在进入到巡线模式后,小车
并没有直接进入巡线,而是要线调节,调节按钮采用底板的KEY1、KEY2、KEY3三个控制,按下KEY1,对第一个巡线传感器调节(从小车的左往右数,第一个巡线传感器),然后KEY2、KEY3是对传感器的值进行调节,不断按下KEY2,知道RGB灯变为红色,如果
接着按KEY3,RGB灯又会变为绿色,就这样一次调节五个传感器,注意,中间那个传感器变为红色后,按下KEY3让它变为绿色,因为我们中间要用来巡黑线的,当然,我们在调节的时候,是将小车放在线上的。当五个传感器调节完成后,继续按下KEY1键,小
车推出调节模式,而进入正常的巡线模式,如果想再次调节,则再次按下KEY1键,依照前面说的进行调节,怎么样?懂了吗?

下面是样例代码,整个例程还需要直插“LCD12864显示屏扩展板”作为显示器,并使用该显示器扩展版上的集成摇杆进行操作。该样例展示了MiniQ 2WD常用功能:蜂鸣、灯光、巡线、寻光、红外壁障,遥控功能。通过该样例可以更直观的感受产品的强大以及学习产品的使用方法
注意:程序分两部分,须分别烧录在miniQ 2WD Plus上层板和下层小车中。


MiniQ 2WD+ Plus上层板+LCD12864显示屏扩展板

miniQ 2WD PLUS样例代码


#include<Wire.h>
#include<JLX12864G.h>

JLX12864G lcd(8,9,10,11,13);

long TimeNum=0;
char SendCommandData[]={'S','M','L','A','E','O','R','#'};//
char xReadData=0;
int Key_Num=0;
int Key_Up=0,Key_Down=0,Key_Left=0,Key_Right=0,Key_Ok=0;
byte CheckData1[]={1,3,5};
byte CheckData2[]={1,3,5,7};
char StartData[]={
0x40,0x42,0x42,0x42,0x42,0xFE,0x42,0x42,0x42,0x42,0xFE,0x42,0x42,0x42,0x42,0x00,/*--  文字:  开  --*/
0x00,0x40,0x20,0x10,0x0C,0x03,0x00,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x10,0x90,0x70,0x1F,0x12,0xF0,0x00,0x20,0x70,0x28,0x27,0x22,0x28,0x70,0x20,0x00,/*--  文字:  始  --*/
0x40,0x21,0x12,0x0C,0x06,0x09,0x30,0x00,0x7F,0x21,0x21,0x21,0x21,0x7F,0x00,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
};
char StopData[]={
0x20,0x30,0xAC,0x63,0x10,0x00,0x08,0x48,0x48,0x48,0x7F,0x48,0x48,0x48,0x08,0x00,/*--  文字:  结  --*/
0x22,0x23,0x22,0x12,0x12,0x00,0x00,0x7E,0x22,0x22,0x22,0x22,0x22,0x7E,0x00,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x04,0x04,0xF4,0x94,0x94,0x94,0x94,0xFF,0x94,0x94,0x94,0x94,0xF4,0x04,0x04,0x00,/*--  文字:  束  --*/
0x20,0x20,0x11,0x10,0x08,0x06,0x01,0xFF,0x02,0x04,0x08,0x08,0x11,0x30,0x10,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
};
char dfrobotlogo[]={
0x08,0xF8,0xF8,0x08,0x18,0xF0,0xE0,0x00,0x08,0x0F,0x0F,0x08,0x0C,0x07,0x03,0x00,
0x08,0xF8,0xF8,0x88,0xC8,0xC8,0x18,0x10,0x08,0x0F,0x0F,0x08,0x01,0x01,0x00,0x00,
0x08,0xF8,0xF8,0xC8,0xC8,0x78,0x30,0x00,0x08,0x0F,0x0F,0x08,0x03,0x0F,0x0C,0x08,
0xE0,0xF0,0x18,0x08,0x18,0xF0,0xE0,0x00,0x03,0x07,0x0C,0x08,0x0C,0x07,0x03,0x00,
0x08,0xF8,0xF8,0x48,0x48,0xF8,0xB0,0x00,0x08,0x0F,0x0F,0x08,0x08,0x0F,0x07,0x00,
0xE0,0xF0,0x18,0x08,0x18,0xF0,0xE0,0x00,0x03,0x07,0x0C,0x08,0x0C,0x07,0x03,0x00,
0x18,0x18,0x08,0xF8,0xF8,0x08,0x18,0x18,0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
};
char check[]={0xFF,0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0xFF,0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01,};    //It is a function switch indicator
char Spacebar[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};
char FullPower[]={0xFF,0x01,0xFD,0xFD,0xFD,0xFD,0x01,0xFF,0xFF,0x80,0xBF,0xBF,0xBF,0xBF,0x80,0xFF,};//It is a full power indicator
char HalfPower[]={0xFF,0x01,0x81,0x81,0x81,0x81,0x01,0xFF,0xFF,0x80,0xBF,0xBF,0xBF,0xBF,0x80,0xFF,};//
char LowPower[]={0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF,0x80,0xB8,0xB8,0xB8,0xB8,0x80,0xFF,};
char ShortagePower[]={0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF,0x80,0x80,0x80,0x80,0x80,0x80,0xFF,};
char PatrolLineModel[]={
0x40,0x42,0x4C,0xC0,0x40,0xA0,0x18,0x07,0x62,0x98,0x07,0x62,0x98,0x07,0x02,0x00,/*--  文字:  巡  --*/
0x80,0x40,0x20,0x1F,0x20,0x40,0x43,0x4C,0x40,0x41,0x4E,0x44,0x41,0x4E,0x44,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x40,0x60,0x58,0xC7,0x62,0x00,0x90,0x90,0x90,0xFF,0x90,0x92,0x9C,0x94,0x80,0x00,/*--  文字:  线  --*/
0x20,0x22,0x23,0x12,0x12,0x12,0x20,0x20,0x10,0x13,0x0C,0x14,0x22,0x40,0xF8,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x10,0xD0,0xFF,0x50,0x90,0x04,0xF4,0x54,0x5F,0x54,0x54,0x5F,0xF4,0x04,0x00,0x00,/*--  文字:  模  --*/
0x03,0x00,0xFF,0x00,0x00,0x84,0x85,0x45,0x35,0x0F,0x15,0x25,0x65,0xC4,0x44,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x08,0x88,0x88,0x88,0x88,0x88,0x08,0xFF,0x08,0x09,0x0E,0x0A,0x08,0x00,0x00,/*--  文字:  式  --*/
0x00,0x20,0x60,0x30,0x1F,0x10,0x08,0x08,0x00,0x07,0x18,0x20,0x40,0x80,0x70,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
};
char SearchLightModel[]={
0x00,0x00,0x42,0x4A,0x4A,0x4A,0x4A,0x4A,0x4A,0x4A,0xCA,0x4A,0x7E,0x00,0x00,0x00,/*--  文字:  寻  --*/
0x00,0x01,0x01,0x01,0x05,0x39,0x11,0x01,0x41,0x81,0x7F,0x01,0x01,0x01,0x01,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x40,0x42,0x44,0x5C,0xC8,0x40,0x7F,0x40,0xC0,0x50,0x4E,0x44,0x60,0x40,0x00,/*--  文字:  光  --*/
0x00,0x80,0x40,0x20,0x18,0x07,0x00,0x00,0x00,0x3F,0x40,0x40,0x40,0x40,0x78,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x10,0xD0,0xFF,0x50,0x90,0x04,0xF4,0x54,0x5F,0x54,0x54,0x5F,0xF4,0x04,0x00,0x00,/*--  文字:  模  --*/
0x03,0x00,0xFF,0x00,0x00,0x84,0x85,0x45,0x35,0x0F,0x15,0x25,0x65,0xC4,0x44,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x08,0x88,0x88,0x88,0x88,0x88,0x08,0xFF,0x08,0x09,0x0E,0x0A,0x08,0x00,0x00,/*--  文字:  式  --*/
0x00,0x20,0x60,0x30,0x1F,0x10,0x08,0x08,0x00,0x07,0x18,0x20,0x40,0x80,0x70,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
};
char ObstacleAvoidanceModel[]={
0x40,0x42,0xCC,0x00,0xFE,0x92,0x92,0x9E,0x40,0x4C,0x55,0xE6,0x5C,0x44,0x40,0x00,/*--  文字:  避  --*/
0x40,0x20,0x1F,0x24,0x43,0x4F,0x48,0x4F,0x40,0x42,0x42,0x5F,0x42,0x42,0x40,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0xFE,0x22,0x5A,0x86,0x10,0xD2,0x56,0x5A,0x53,0x52,0x5A,0xD6,0x12,0x10,0x00,/*--  文字:  障  --*/
0x00,0xFF,0x02,0x04,0x13,0x10,0x17,0x15,0x15,0xFD,0x15,0x15,0x17,0x10,0x10,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x10,0xD0,0xFF,0x50,0x90,0x04,0xF4,0x54,0x5F,0x54,0x54,0x5F,0xF4,0x04,0x00,0x00,/*--  文字:  模  --*/
0x03,0x00,0xFF,0x00,0x00,0x84,0x85,0x45,0x35,0x0F,0x15,0x25,0x65,0xC4,0x44,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x08,0x88,0x88,0x88,0x88,0x88,0x08,0xFF,0x08,0x09,0x0E,0x0A,0x08,0x00,0x00,/*--  文字:  式  --*/
0x00,0x20,0x60,0x30,0x1F,0x10,0x08,0x08,0x00,0x07,0x18,0x20,0x40,0x80,0x70,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
};
char RemoteControlModel[]={
0x40,0x42,0xCC,0x00,0x00,0x4A,0x32,0x26,0x2A,0xE1,0x31,0x29,0x25,0x00,0x00,0x00,/*--  文字:  遥  --*/
0x40,0x20,0x1F,0x20,0x40,0x41,0x5D,0x51,0x51,0x5F,0x51,0x51,0x5D,0x41,0x40,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x08,0x08,0x08,0xFF,0x88,0x48,0x00,0x98,0x48,0x28,0x0A,0x2C,0x48,0xD8,0x08,0x00,/*--  文字:  控  --*/
0x02,0x42,0x81,0x7F,0x00,0x00,0x40,0x42,0x42,0x42,0x7E,0x42,0x42,0x42,0x40,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x10,0xD0,0xFF,0x50,0x90,0x04,0xF4,0x54,0x5F,0x54,0x54,0x5F,0xF4,0x04,0x00,0x00,/*--  文字:  模  --*/
0x03,0x00,0xFF,0x00,0x00,0x84,0x85,0x45,0x35,0x0F,0x15,0x25,0x65,0xC4,0x44,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x08,0x88,0x88,0x88,0x88,0x88,0x08,0xFF,0x08,0x09,0x0E,0x0A,0x08,0x00,0x00,/*--  文字:  式  --*/
0x00,0x20,0x60,0x30,0x1F,0x10,0x08,0x08,0x00,0x07,0x18,0x20,0x40,0x80,0x70,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
};
char MusicFunction[]={
0x40,0x40,0x44,0x44,0x4C,0x74,0x44,0x45,0x46,0x64,0x5C,0x44,0x44,0x44,0x40,0x00,/*--  文字:  音  --*/
0x00,0x00,0x00,0xFF,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0xFF,0x00,0x00,0x00,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x00,0x40,0xFC,0x44,0x44,0x44,0x46,0xFA,0x42,0x43,0x43,0x42,0x40,0x00,0x00,/*--  文字:  乐  --*/
0x00,0x20,0x18,0x0C,0x07,0x12,0x20,0x40,0x3F,0x00,0x00,0x02,0x0C,0x38,0x10,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x04,0x04,0x04,0xFC,0x04,0x14,0x14,0x10,0x90,0x7F,0x10,0x10,0xF0,0x00,0x00,/*--  文字:  功  --*/
0x04,0x0C,0x04,0x04,0x03,0x42,0x22,0x11,0x0C,0x23,0x20,0x60,0x20,0x1F,0x00,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x10,0xB8,0x97,0x92,0x90,0x94,0xB8,0x10,0x00,0x7F,0x48,0x48,0x44,0x74,0x20,0x00,/*--  文字:  能  --*/
0x00,0xFF,0x0A,0x0A,0x4A,0x8A,0x7F,0x00,0x00,0x3F,0x44,0x44,0x42,0x72,0x20,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
};
char LightsFunction[]={
0x80,0x70,0x00,0xFF,0x40,0x30,0x00,0x04,0x04,0x04,0x04,0xFC,0x04,0x04,0x04,0x00,/*--  文字:  灯  --*/
0x40,0x30,0x0C,0x03,0x02,0x04,0x08,0x00,0x00,0x20,0x40,0x3F,0x00,0x00,0x00,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x40,0x42,0x44,0x5C,0xC8,0x40,0x7F,0x40,0xC0,0x50,0x4E,0x44,0x60,0x40,0x00,/*--  文字:  光  --*/
0x00,0x80,0x40,0x20,0x18,0x07,0x00,0x00,0x00,0x3F,0x40,0x40,0x40,0x40,0x78,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x04,0x04,0x04,0xFC,0x04,0x14,0x14,0x10,0x90,0x7F,0x10,0x10,0xF0,0x00,0x00,/*--  文字:  功  --*/
0x04,0x0C,0x04,0x04,0x03,0x42,0x22,0x11,0x0C,0x23,0x20,0x60,0x20,0x1F,0x00,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x10,0xB8,0x97,0x92,0x90,0x94,0xB8,0x10,0x00,0x7F,0x48,0x48,0x44,0x74,0x20,0x00,/*--  文字:  能  --*/
0x00,0xFF,0x0A,0x0A,0x4A,0x8A,0x7F,0x00,0x00,0x3F,0x44,0x44,0x42,0x72,0x20,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
};
char MovementFunction[]={
0x40,0x41,0xCE,0x04,0x00,0x20,0x22,0xA2,0x62,0x22,0xA2,0x22,0x22,0x22,0x20,0x00,/*--  文字:  运  --*/
0x40,0x20,0x1F,0x20,0x28,0x4C,0x4A,0x49,0x48,0x4C,0x44,0x45,0x5E,0x4C,0x40,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x20,0x24,0x24,0xE4,0x24,0x24,0x24,0x20,0x10,0x10,0xFF,0x10,0x10,0xF0,0x00,0x00,/*--  文字:  动  --*/
0x08,0x1C,0x0B,0x08,0x0C,0x05,0x4E,0x24,0x10,0x0C,0x03,0x20,0x40,0x3F,0x00,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x04,0x04,0x04,0xFC,0x04,0x14,0x14,0x10,0x90,0x7F,0x10,0x10,0xF0,0x00,0x00,/*--  文字:  功  --*/
0x04,0x0C,0x04,0x04,0x03,0x42,0x22,0x11,0x0C,0x23,0x20,0x60,0x20,0x1F,0x00,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x10,0xB8,0x97,0x92,0x90,0x94,0xB8,0x10,0x00,0x7F,0x48,0x48,0x44,0x74,0x20,0x00,/*--  文字:  能  --*/
0x00,0xFF,0x0A,0x0A,0x4A,0x8A,0x7F,0x00,0x00,0x3F,0x44,0x44,0x42,0x72,0x20,0x00,/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
};
void setup()
{
  pinMode(7,OUTPUT);
  lcd.init();
  lcd.clear();
  Wire.begin();
  Serial1.begin(57600);
}

void loop()
{
 int n=0;
  lcd.graphic_8x16(3,2,dfrobotlogo,7);
  lcd.string_5x7(5,5,"miniQ 2WD V3");
  lcd.string_5x7(6,15,"www.dfrobot.com");
  lcd.string_5x7(8,1,"Please press any key!");
 digitalWrite(7,HIGH);
 while(Key_Num==0)
 {
   Key_Scan();
 }
  lcd.clear();
  FunctionMenu();
  while(1)//Enter the cycle does not return
  {
    Key_Scan();
    Key_Deal();
   if(Key_Down==4)
  {
    Key_Down=1;
  }
   ReadPowerData();
  } 
}

void FunctionMenu(void)
{
  lcd.graphic_16x16(1,9,MusicFunction,4);
  lcd.graphic_16x16(3,9,LightsFunction,4);
  lcd.graphic_16x16(5,9,MovementFunction,4);
}
void MusicFunctionMode(void)
{
  lcd.graphic_16x16(1,29,MusicFunction,4);
}

void LightFunctionMode(void)
{
  lcd.graphic_16x16(1,29,LightsFunction,4);
}

void MovementFunctionMode(void)
{
  lcd.graphic_16x16(1,9,PatrolLineModel,4);
  lcd.graphic_16x16(3,9,SearchLightModel,4);
  lcd.graphic_16x16(5,9,ObstacleAvoidanceModel,4);
  lcd.graphic_16x16(7,9,RemoteControlModel,4);
}
void Key_Scan(void)
{
  int analog = analogRead(A0);
  if(analog>=950)
  {
    Key_Num=0;
    return;
  }
  else
  {
     Key_Num=1;
    if(analog>=750&&analog<950)//Press up and key_num=1
    {
      delay(2);
      if(analog>=750&&analog<950)
      {
        while(analog<950)
         analog = analogRead(A0);
         Key_Up++;
      }
    }
    else if(analog>=550&&analog<750)//Press right and key_num=2
    {
      delay(2);
      if(analog>=550&&analog<750)
      {
        while(analog<950)
         analog = analogRead(A0);
        Key_Right=1;
      }
    }
    else if(analog>=350&&analog<550)//Press down and key_num=3
    {
      delay(2);
      if(analog>=350&&analog<550)
      {
        while(analog<950)
         analog = analogRead(A0);
       Key_Down++;
      }
    }
    else if(analog>=150&&analog<350)//Press ok and key_num=4
    {
      delay(2);
      if(analog>=150&&analog<350)
      {
        while(analog<950)
         analog = analogRead(A0);
       Key_Ok=1;
      }
    }
    else if(analog<150)//Press left and key_num=5
    {
      delay(2);
      if(analog<150)
      {
        while(analog<950)
         analog = analogRead(A0);
         Key_Left=1;
      }
    }
  } 
}

void Key_Deal(void)
{
  if(Key_Down==1)
  {
    Lcd_Check_Graphic(1,0);
    if(Key_Ok==1)//进入音乐功能界面
    {
      lcd.clear();
      MusicFunctionMode();
      IICWritData(SendCommandData[1]);
      while(1)
      {
        Key_Scan();
        Key_Ok=0;
        if(Key_Left==1)
        {
          Key_Left=0;
          Key_Down=1;
          IICWritData(SendCommandData[0]);
          goto Back_One_Level;//跳回功能界面
        }
        ReadPowerData();
      }
    }
  }
  if(Key_Down==2)
  {
    Lcd_Check_Graphic(3,0);
   if(Key_Ok==1)//进入灯光功能界面
    {
      lcd.clear();
      LightFunctionMode();
      IICWritData(SendCommandData[2]);
      while(1)
      {
        Key_Scan();
        Key_Ok=0;
        if(Key_Left==1)
        {
          Key_Left=0;
          Key_Down=2;
          IICWritData(SendCommandData[0]);
          goto Back_One_Level;//跳回功能界面
        }
        ReadPowerData();    
      }
    }
  }
  if(Key_Down==3)
  {
    Lcd_Check_Graphic(5,0);
    if(Key_Ok==1)//进入运动功能界面
    {
      Key_Ok=0;
      Key_Down=1;
      lcd.clear();
      MovementFunctionMode();
      while(1)
      {
        Key_Scan();
        if(Key_Left==1)
        {
          Key_Left=0;
          Key_Down=3;
          goto Back_One_Level;
        }
        if(Key_Down==1)
        {
            Lcd_Check_Graphic(1,1);
            if(Key_Ok==1)
            {
              Key_Ok=0;
              Key_Up=0;
              lcd.clear();
              lcd.graphic_16x16(1,29,PatrolLineModel,4);//巡线模式
             // lcd.graphic_16x16(3,1,StartData,2);
              IICWritData(SendCommandData[3]);
              //send line data
              while(1)
              {
                Key_Scan();
                if(Key_Left==1)
                {
                  Key_Ok=0;
                  Key_Left=0;
                  Key_Down=1;
                  IICWritData(SendCommandData[0]);
                  goto Back_Two_Level_Move;//跳回到运动功能界面
                }  
                ReadPowerData();   
              }
              Back_Two_Level_Move://运动功能界面
                lcd.clear();
                MovementFunctionMode();
            }
        }
        if(Key_Down==2)
        {
          Lcd_Check_Graphic(3,1);
          if(Key_Ok==1)
          {
            Key_Ok=0;
            lcd.clear();
            lcd.graphic_16x16(1,29,SearchLightModel,4);
            IICWritData(SendCommandData[4]);
            //send light data
            while(1)
            {
              Key_Scan();
              if(Key_Left==1)
              {
                 Key_Ok=0;
                 Key_Left=0;
                 Key_Down=2;
                 IICWritData(SendCommandData[0]);
                 goto Back_Two_Level_Move;//跳回到运动功能界面
              }
              ReadPowerData();
            }
          }
        }
        if(Key_Down==3)
        {
          Lcd_Check_Graphic(5,1);
          if(Key_Ok==1)
          {
            Key_Ok=0;
            lcd.clear();
            lcd.graphic_16x16(1,29,ObstacleAvoidanceModel,4);
            IICWritData(SendCommandData[5]);
            //send OBSTACLEAVOIDANCE data
            while(1)
            {
              Key_Scan();
              if(Key_Left==1)
              {
                 Key_Ok=0;
                 Key_Left=0;
                 Key_Down=3;
                 IICWritData(SendCommandData[0]);
                 goto Back_Two_Level_Move;//跳回到运动功能界面
              }
              ReadPowerData();
            }
          }
        }  
        if(Key_Down==4)
        {
          Lcd_Check_Graphic(7,1);
          if(Key_Ok==1)
          {
            Key_Ok=0;
            lcd.clear();
            lcd.graphic_16x16(1,29,RemoteControlModel,4);
         IICWritData(SendCommandData[6]);
            //send Remote data
            while(1)
            {
              Key_Scan();
              if(Key_Left==1)
              {
                 Key_Ok=0;
                 Key_Left=0;
                 Key_Down=4;
                 IICWritData(SendCommandData[0]);
                 goto Back_Two_Level_Move;//跳回到运动功能界面
              }
              SerialReadWriteData();
              ReadPowerData();
            }
          }
        }
        if(Key_Down==5)
          Key_Down=1;
       ReadPowerData();
      }
      Back_One_Level://功能界面,如果功能跳转函数放在if的外面,lcd显示会闪烁
         lcd.clear();
         FunctionMenu();
    }
  }
}

void SerialReadWriteData(void)
{
  if(Serial1.available())
  {
    char SerialData = Serial1.read();
    switch(SerialData)
    {
      case 'w':
        IICWritData('w');
        break;
     case 'a':
        IICWritData('a');
        break;
     case 's':
        IICWritData('s');
        break;
     case 'd':
        IICWritData('d');
        break;
     default:
       IICWritData('S');
       break;
    }
  }
}

void Lcd_Check_Graphic(byte CheckNum,char RowsNum)//The check of indicator switch function
{
  if(RowsNum==0)
  {
    for(int i=0;i<3;i++)
    {
      if(CheckData1[i]==CheckNum)
        lcd.graphic_8x16(CheckNum,1,check,1);
      else
        lcd.graphic_8x16(CheckData1[i],1,Spacebar,1);
    }
  }
  else if(RowsNum==1)
  {
    for(int j=0;j<4;j++)
    {
    if(CheckData2[j]==CheckNum)
        lcd.graphic_8x16(CheckNum,1,check,1);
    else
        lcd.graphic_8x16(CheckData2[j],1,Spacebar,1);
    }
  }
}

void IICWritData(char data)
{
  Wire.beginTransmission(4); //发送数据到设备号为4的从机
  Wire.write(data);              // 发送变量x中的一个字节  
  Wire.endTransmission();    // 停止发送
}

void IICReadData(void)
{
  Wire.requestFrom(4, 1);    //通知4号从机上传1个字节
  while(Wire.available()>0)    // 当主机接收到从机数据时
  { 
    xReadData = Wire.read(); //接收一个字节赋值给c
  }
}

//读取电池电量函数
void ReadPowerData(void)
{
    TimeNum++;
    if(TimeNum==10)
    {  
      TimeNum=0;
      IICReadData();
      switch(xReadData)
      {
        case'3':
        {
          lcd.graphic_8x16(1,121,FullPower,1);
          digitalWrite(7,HIGH);
          break;
        }
        case'2':
        {
          lcd.graphic_8x16(1,121,HalfPower,1);
          digitalWrite(7,HIGH);
          break;
        }
        case'1':
        {
          lcd.graphic_8x16(1,121,LowPower,1);
          digitalWrite(7,HIGH);
          break;
        }
        case'0':
        {
          lcd.graphic_8x16(1,121,ShortagePower,1);
          digitalWrite(7,LOW);
          break;
        }
      }
    }
}




miniQ 2WD样例代码


#include <Wire.h> 
#include <Adafruit_NeoPixel.h>
#include <IRremote.h>
#if defined(ARDUINO) && ARDUINO >= 100
#define printByte(args)  write(args);
#else
#define printByte(args)  print(args,BYTE);
#endif
uint8_t empty[8] = {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f};
uint8_t heart[8] = {0x0,0xa,0x1f,0x1f,0xe,0x4,0x0,0x0};
#define address 0x1e

int length;
#define RGB_ws2812 10
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, RGB_ws2812, NEO_GRB + NEO_KHZ800);

#define EN1 6//右侧电机使能端
#define IN1 7//右侧电机方向端
#define EN2 5//左侧电机使能端
#define IN2 12//左侧电机方向端

#define FORW 0//前进
#define BACK 1//后退

#define IR_IN  17//红外接收
#define L_IR 13//左红外发送
#define R_IR 8//右红外发送

#define BUZZER 16//蜂鸣器

#define Vr   5//参考电压值

#define NTD0 -1
#define NTD1 294
#define NTD2 330
#define NTD3 350
#define NTD4 393
#define NTD5 441
#define NTD6 495
#define NTD7 556

#define NTDL1 147
#define NTDL2 165
#define NTDL3 175
#define NTDL4 196
#define NTDL5 221
#define NTDL6 248
#define NTDL7 278

#define NTDH1 589
#define NTDH2 661
#define NTDH3 700
#define NTDH4 786
#define NTDH5 882
#define NTDH6 990
#define NTDH7 112
//列出全部D调的频率
#define WHOLE 1
#define HALF 0.5
#define QUARTER 0.25
#define EIGHTH 0.25
#define SIXTEENTH 0.625
//列出所有节拍
int tune1[]=                 //根据简谱列出各频率
{
  NTDH3,NTDH3,NTDH2,NTDH3,NTD6,NTDH3,NTD6,NTD5,
  NTDH2,NTDH1,NTDH2,NTDH3,NTDH2,NTDH1,
  NTD6,NTDH3,NTDH2,NTDH3,NTDH2,NTDH1,NTD7,
  NTD6,NTD5,NTD6,NTD7,NTD6,NTD5
};
float durt1[]=                   //根据简谱列出各节拍
{
  0.5,0.5,1,0.5,1,1.5,1,1,
  0.5,0.5,1,0.5,1,2,
  1,1,0.5,0.5,1,0.5,1,
  0.5,0.5,1,0.5,1,2
};

int tonepin=16;   //得用16号接口
char xReadWriteData='S';
int   count;//对返回的脉冲进行计数
float data[7]={0X00,0X00,0X00,0X00,0x00,0xff,0x00};//存储8个通道ad转换的值
char  value[5]={0x00,0x00,0x00,0x00,0x00};//五个寻线传感器的电压值
char  key_1=0x00,key_2=0x00,key_3=0x00;//按键的计数值
//计数里程
int    count_r=0,count_l=0;//对左右轮返回的脉冲进行计数
//遥控参数
IRrecv irrecv(IR_IN);
decode_results results;
int SpeedNumL=100;
int SpeedNumR=100;
//控制电机转动
void Motor_Control(int M1_DIR,int M1_EN,int M2_DIR,int M2_EN)
{
  //////////M1////////////////////////
  if(M1_DIR==FORW)//M1方向为FORW
      digitalWrite(IN1,FORW); //向前
  else
    digitalWrite(IN1,BACK);//向后   
  if(M1_EN==0) //M1速度为0
      analogWrite(EN1,LOW);//置低,停止
  else
    analogWrite(EN1,M1_EN);//设置给定的数值

  ///////////M2//////////////////////
  if(M2_DIR==FORW) //M2方向为FORW
      digitalWrite(IN2,FORW);//向前
  else
    digitalWrite(IN2,BACK);//向后   
  if(M2_EN==0) //M2速度为0
      analogWrite(EN2,LOW);//置低,停止
  else//否则
  analogWrite(EN2,M2_EN);//设置给定的数值 
}
//避障
void L_Send40KHZ(void)//左发射管发射频率为40kHZ脉冲

{
  int i;
  for(i=0;i<24;i++)
  {
    digitalWrite(L_IR,LOW);//设置左发射管为高电平
    delayMicroseconds(8);//延时
    digitalWrite(L_IR,HIGH);//设置左发射管为低电平
    delayMicroseconds(8);//延时
  }
}
void R_Send40KHZ(void)//右发射管发射频率为40kHZ脉冲
{
  int i;
  for(i=0;i<24;i++)
  {
    digitalWrite(R_IR,LOW);//设置右发射管为高电平
    delayMicroseconds(8);//延时
    digitalWrite(R_IR,HIGH);//设置右发射管为低电平
    delayMicroseconds(8);//延时
  }
}
//计数里程
void LEFT(void)
{
 //左侧车轮返回脉冲计数
 //if(++count_l=100)
    count_l++;
}
void RIGHT(void)
{
 //右侧车轮返回脉冲计数
// if(++count_r=100)
    count_r++;
}
void pcint0_init(void)//引脚变化中断初始化
{
  PCICR = 0X01;//使能第0组引脚变化中断
  PCMSK0 = 0X01;//使能第0组的第0个引脚变化中断
}
ISR(PCINT0_vect)//PB0引脚变化中断
{
  count++;//计数接收到的脉冲
}
void Obstacle_Avoidance(void)//避障子函数
{   
  char i;
  count=0;
  for(i=0;i<20;i++)//左发射管发射20个脉冲
  {
    L_Send40KHZ();
    delayMicroseconds(600);    
  }
  if(count>20)//接收超过10个脉冲,判断有障碍物 
  {
      Motor_Control(BACK,SpeedNumR,BACK,SpeedNumL);//后退
      delay(50);//延时
      Motor_Control(BACK,SpeedNumR,FORW,SpeedNumL);//右转
      delay(50);//延时
  }
  else
  {
      Motor_Control(FORW,SpeedNumR,FORW,SpeedNumL);//前进
  }

  count=0;
  for(i=0;i<20;i++)//右发射管发射20个脉冲
  {
    R_Send40KHZ(); 
    delayMicroseconds(600);        
  }
  if(count>20)
  {
      Motor_Control(BACK,SpeedNumR,BACK,SpeedNumL);//后退
      delay(50);//延时
      Motor_Control(FORW,SpeedNumR,BACK,SpeedNumL);//左转
      delay(50);//延时
  }
  else
  {
      Motor_Control(FORW,SpeedNumR,FORW,SpeedNumL);//前进
  }
  
}
//读取模拟端口电压值
void Read_Value(void)
{  
  int i;
  for(i=0;i<7;i++)
  {
    data[i]=analogRead(i);//读取模拟i口电压值
    data[i]= ((data[i]*Vr)/1024); //转换成模拟值
  }
}
//寻线
void value_adjust(unsigned char num)//调整寻线传感器的值
{
  if(num==1)//调节第一个寻线传感器
  {
    if(data[0]>value[0])
    {
      colorWipe(strip.Color(0, 255, 0), 5); // Red
    }
    else
    {
      colorWipe(strip.Color(255, 0, 0), 1); // Green
    } 
  }
  if(num==2)//调节第二个寻线传感器
  {
    if(data[1]>value[1])
    {
      colorWipe(strip.Color(0, 255, 0), 1); // Red
    }
    else
    {
      colorWipe(strip.Color(255, 0, 0), 1); // Green
    } 
  }
  if(num==3)//调节第三个寻线传感器
  {
    if(data[2]>value[2])
    {
      colorWipe(strip.Color(0, 255, 0), 1); // Red
    }
    else
    {
      colorWipe(strip.Color(255, 0, 0), 1); // Green
    } 
  }
  if(num==4)//调节第四个寻线传感器
  {
    if(data[3]>value[3])
    {
      colorWipe(strip.Color(0, 255, 0), 1); // Red
    }
    else
    {
      colorWipe(strip.Color(255, 0, 0), 1); // Green
    } 
  }
  if(num==5)//调节第五个寻线传感器
  {
    if(data[4]>value[4])
    {
      colorWipe(strip.Color(0, 255, 0), 1); // Red
    }
    else
    {
      colorWipe(strip.Color(255, 0, 0), 1); // Green
    } 
  }
}
void huntline_deal(void)
{ 
 if(data[0]>(value[0]-1)&&data[1]>(value[1]-1)&&data[2]<(value[2])&&data[3]>(value[3]-1)&&data[4]>(value[4]-1))//测一下实际值
    Motor_Control(FORW,100,FORW,100);//前进
  else if(data[0]>(value[0]-1)&&data[1]>(value[1]-1)&&data[2]<(value[2]-1)&&data[3]<(value[3]-1)&&data[4]>(value[4]-1))
    Motor_Control(BACK,20,FORW,100);//右转
  else if(data[0]>(value[0]-1)&&data[1]>(value[1]-1)&&data[2]>(value[2]-1)&&data[3]<(value[3]-1)&&data[4]>(value[4]-1))
    Motor_Control(BACK,100,FORW,100);//快速右转	
  else if(data[0]>(value[0]-1)&&data[1]>(value[1]-1)&&data[2]>(value[2]-1)&&data[3]<(value[3]-1)&&data[4]<(value[4]-1))
    Motor_Control(BACK,100,FORW,100);//快速右转
  else if(data[0]>(value[0]-1)&&data[1]>(value[1]-1)&&data[2]>(value[2]-1)&&data[3]>(value[3]-1)&&data[4]<(value[4]-1))
    Motor_Control(BACK,100,FORW,100);//快速右转
  else if(data[0]>(value[0]-1)&&data[1]<(value[1]-1)&&data[2]<(value[2]-1)&&data[3]>(value[3]-1)&&data[4]>(value[4]-1))
    Motor_Control(FORW,100,BACK,20);//左转
  else if(data[0]>(value[0]-1)&&data[1]<(value[1]-1)&&data[2]>(value[2]-1)&&data[3]>(value[3]-1)&&data[4]>(value[4]-1))
    Motor_Control(FORW,100,BACK,100);//快速左转
  else if(data[0]<(value[0]-1)&&data[1]<(value[1]-1)&&data[2]>(value[2]-1)&&data[3]>(value[3]-1)&&data[4]>(value[4]-1))
    Motor_Control(FORW,100,BACK,100);//快速左转
  else if(data[0]<(value[0]-1)&&data[1]>(value[1]-1)&&data[2]>(value[2]-1)&&data[3]>(value[3]-1)&&data[4]>(value[4]-1))
    Motor_Control(FORW,100,BACK,100);//快速左转
}
//寻光
void hunt_light(void)//寻光子函数
{
  if (data[5]>3.5)   //根据实际的实验环境进行测量
    Motor_Control(BACK,SpeedNumR,FORW,SpeedNumL);//右转
  else if (data[5]< 1.5)
    Motor_Control(FORW,SpeedNumR,BACK,SpeedNumL);//
  else
    Motor_Control(FORW,0,FORW,0);//停止
}
//遥控功能函数
void dump(decode_results *results)
{
  if(results->value==0x00fd00ff)
  {
    Motor_Control(FORW,0,FORW,0);//停止   
  }
  if(results->value==0x00fd807f)
  {
    Motor_Control(FORW,100,FORW,100);//前进 
  }
  if(results->value==0x00fd906f)
  {
    Motor_Control(BACK,100,BACK,100);//后退
  }
  if(results->value==0x00fd20df)
  {
    Motor_Control(FORW,100,BACK,100);//左转 
  }
  if(results->value==0x00fd609f)
  {
    Motor_Control(BACK,100,FORW,100);//右转
  }
}
//蜂鸣器声音
void buzzer(void)//蜂鸣器发出一种声音
{
  digitalWrite(BUZZER,HIGH);//置高,蜂鸣器响
  delay(1);
  digitalWrite(BUZZER,LOW);//置低,蜂鸣器不响
  delay(10);
}
//按键扫描
void key_scan(void)
{
  if(data[6]>4.50&&data[6]<6.00)//没有按键按下
    return;//返回
  else
  {
    if(data[6]>=0.00&&data[6]<0.80)//按键1按下
    {
      delay(10);//延时消抖
      if(data[6]>=0.00&&data[6]<0.80)//按键1确实按下
      {
        buzzer();//蜂鸣器响
        while(data[6]>=0.00&&data[6]<0.80)
         Read_Value();
        key_1++;//按键1计数
        if(key_1>=1&&key_1<=5) 
          value_adjust(key_1);//寻线传感器的值调整
      }
    }
    else if(data[6]>=0.80&&data[6]<3)//按键2按下
    {
      delay(10);//延时消抖
      if(data[6]>=0.80&&data[6]<3)
      {
        buzzer();//蜂鸣器响
        while(data[6]>=0.50&&data[6]<3)
         Read_Value();
        if(key_1>=1&&key_1<=5)//按键1的值在1~5之间
        {
          value[key_1-1]++;//传感器的分辨轨迹的界限值加加(调整)
          value_adjust(key_1);//跟实际值对比
        }
        else
        {
          key_2++;//key2计数
        }
      }
    }
    else if(data[6]>=3&&data[6]<4)//按键3按下
    {
      delay(10);//延时消抖
      if(data[6]>=3&&data[6]<4)
      {
        buzzer();//蜂鸣器响
        while(data[6]>=3&&data[6]<4)
          Read_Value();
        if(key_1>=1&&key_1<=5)//按键1的值在1~5之间
        {
          value[key_1-1]--;//传感器的分辨轨迹的界限值减减(调整)
          value_adjust(key_1);//跟实际值对比
        }
        else
        {
          key_3++;//key3计数
        }
      }
    }
  }
}
void key_deal()
{
  if(key_1==6)//寻线
  {
    huntline_deal();//调用寻线处理子函数
    key_2=0x00;
    key_3=0x00;
  }
  else if(key_1 == 7)
  {
     key_1 = 0;
     Motor_Control(FORW,0,FORW,0);//停止
  }
}
//低电压检测
void low_voltage_check(void)
{ 
    float voltage_num=analogRead(A9);
    voltage_num=(15*voltage_num)/2048;
   if(voltage_num<4.0||voltage_num>7.0)
   {  
     while(1)
     {
       voltage_num=analogRead(A9);
        voltage_num=(15*voltage_num)/2048;
        if(voltage_num>=4.0&&voltage_num<=7.0)
          break;
        buzzer(); //蜂鸣器响
        colorWipe(strip.Color(0, 255, 0), 1); // Red
        colorWipe(strip.Color(0, 0, 0), 1);
     }
   }
}
void Velocity_function(void)//速度处理函数
{
  if(count_l>count_r)
  {
    SpeedNumL=SpeedNumL-1;
    SpeedNumR=SpeedNumR+1;
  }
  else if(count_l<count_r)
  {
    SpeedNumL=SpeedNumL+1;
    SpeedNumR=SpeedNumR-1;
  }
  count_l=0;
  count_r=0;  
}
void colorWipe(uint32_t c, uint8_t wait)
{
  for(uint16_t i=0; i<strip.numPixels(); i++)
 {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
  }
}

void setup()
{
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
  pinMode(12,OUTPUT);
  
  pinMode(8,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(13,OUTPUT);
  pinMode(14,OUTPUT);
  pinMode(16,OUTPUT);
  pinMode(17,INPUT);
  irrecv.enableIRIn();
  strip.begin();
  strip.show();
  
  length=sizeof(tune1)/sizeof(tune1[0]);   //计算长度
  
  Wire.begin(4);// 加入 i2c 总线,设置从机地址为 #4
  Wire.onReceive(receiveEvent);//注册接收到主机字符的事件
  Wire.onRequest(requestEvent);// 注册主机通知从机上传数据的事件
  
  attachInterrupt(2,RIGHT,FALLING);
  attachInterrupt(3,LEFT,FALLING);
  Motor_Control(FORW,0,FORW,0);
}
void loop()
{
  switch(xReadWriteData)
  {
    case'S':
      Motor_Control(FORW,0,FORW,0);
      break;
    case'M':
      MusicFunction();
      break;
    case'L':
      RGBFunction();
      break;
    case'A':
      LineFunction();
      break;
    case'E':
      LightFuntion();
      break;
    case'O':
      ObstacleFunction();
      break;
    case'R':
      RemoteFunction();
      break;
    case'w':
      Motor_Control(FORW,SpeedNumR,FORW,SpeedNumL);//前进
      break;
    case'a':
      Motor_Control(FORW,SpeedNumR,BACK,SpeedNumL);//左转
      break;
    case's':
      Motor_Control(BACK,SpeedNumR,BACK,SpeedNumL);//后退
      break;
    case'd':
      Motor_Control(BACK,SpeedNumR,FORW,SpeedNumL);//右转
      break;
  }
  Velocity_function();
//  low_voltage_check();
  colorWipe(strip.Color(0, 0, 0), 1);
}

// 当从机接收到主机字符,执行该事件
void receiveEvent(int howMany)
{
  while( Wire.available()>1) // 循环执行,直到数据包只剩下最后一个字符
  {
    char c = Wire.read(); // 作为字符接收字节
  }
   //接收主机发送的数据包中的最后一个字节
  xReadWriteData = Wire.read();    // 作为整数接收字节
}

//当主机通知从机上传数据,执行该事件
void requestEvent()
{
  float analog = analogRead(A9);
  analog =(15*analog)/2048;
  if(analog>4.7)
    Wire.write( '3'); 
  else if(analog>4.3)
    Wire.write( '2'); 
  else if(analog>3.9)
    Wire.write( '1'); 
  else
    Wire.write( '0'); 
 // 响应主机的通知,向主机发送一个字节数据
}

uint32_t Wheel(byte WheelPos) 
{
  if(WheelPos < 85)
  {
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } 
  else if(WheelPos < 170)
  {
   WheelPos -= 85;
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  else 
  {
   WheelPos -= 170;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}

void rainbow(uint8_t wait)
{
  uint16_t i, j;

  for(j=0; j<256; j++)
  {
    for(i=0; i<strip.numPixels(); i++)
    {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) 
{
  uint16_t i, j;

  for(j=0; j<256*5; j++) 
  { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) 
    {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

void MusicFunction(void)//
{
  for(int x=0;x<length;x++)
  {
    tone(tonepin,tune1[x]);
    delay(350*durt1[x]);   //这里用来根据节拍调节延时,500这个指数可以自己调整,在该音乐中,我发现用500比较合适。
    noTone(tonepin);
  }
}

void RGBFunction(void)//
{
  rainbow(5);
  rainbowCycle(5);
}

void LineFunction(void)//
{
  //buzzer();//蜂鸣器响
  Read_Value();
  key_scan();
  key_deal();
}

void LightFuntion(void)//
{
   Read_Value();
   hunt_light();//调用寻光子函数
   Velocity_function();//速度处理函数
}

void ObstacleFunction(void)//
{
  pcint0_init();//引脚变化中断初始化
  sei();     //全局中断使能
  Obstacle_Avoidance();
  Velocity_function();
}
void RemoteFunction(void)
{  Motor_Control(FORW,0,FORW,0);//run motor
  while(1)
  {
   if(irrecv.decode(&results))
   {
      dump(&results);
      irrecv.resume();
   }
  }
}


Fig1: miniQ 2WD Plus&miniQ 2wd

这是“miniQ 2WD Plus + miniQ 2wd + Blelink + 蓝牙4.0 无线手柄”的典型应用。组建自己的遥控车,怎么样,很高档大气吧~


注:蓝牙4.0 无线手柄的数据传输格式,请参照蓝牙4.0 无线手柄的手柄BLE透传协议。




开发资料

miniQ 2wd plus原理图
miniQ 2wd + miniQ 2wd plus程序

个人工具
名字空间

变换
操作
导航
工具箱