(SKU:DFR0302)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 桌面机器人底盘上
管脚定义
上面的图片显示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 2WD Plus上层板角度为90°,使得得到的数据范围在0~180°之间。当然,你也可以修改显示的数据,以及角度范围。样例代码
/*************************************************** MiniQ 2WD plus (With Stainless Steel Probe) <http://www.dfrobot.com.cn/goods-1074.html> *************************************************** This example show how to use ADXL345 and ITG3205 sensor. Created 2016-1-15 By Andy zhou <Andy.zhou@dfrobot.com> version:V1.0 GNU Lesser General Public License. See <http://www.gnu.org/licenses/> for details. All above must be included in any redistribution ****************************************************/ /***********Notice and Trouble shooting*************** 1.Connection and Diagram can be found here <http://wiki.dfrobot.com.cn/index.php?title=(SKU:DFR0302)MiniQ_2WD_Plus> 2.This code is tested on Arduino Uno, Leonardo, Mega boards. ****************************************************/ #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->gnd) #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; //******卡尔曼参数************ 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 } }; 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(); } /***************************************** * ITG3205 * G_SMPLRT_DIV:采样率 = 125Hz * G_DLPF_FS:+ - 2000度/秒、低通滤波器5HZ、内部采样率1kHz * G_INT_CFG:没有中断 * G_PWR_MGM:电源管理设定:无复位、无睡眠模式、无待机模式、内部振荡器 ******************************************/ void initGyro(){ 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 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,采用单线通信方式控制。样例展示灯珠多种颜色的变换、控制灯珠闪烁。实际上每个像素点都可显示三基色(RGB),每种颜色更可实现256级亮度显示,完成16777216种颜色的全真色彩显示,你可以在代码中修改三基色的数值来改变灯光色彩。
样例代码
/*************************************************** MiniQ 2WD plus (With Stainless Steel Probe) <http://www.dfrobot.com.cn/goods-1074.html> *************************************************** This example show how to control RGB LED. Created 2016-1-15 By Andy zhou <Andy.zhou@dfrobot.com> version:V1.0 GNU Lesser General Public License. See <http://www.gnu.org/licenses/> for details. All above must be included in any redistribution ****************************************************/ /***********Notice and Trouble shooting*************** 1.Connection and Diagram can be found here <http://wiki.dfrobot.com.cn/index.php?title=(SKU:DFR0302)MiniQ_2WD_Plus> 2.This code is tested on Arduino Uno, Leonardo, Mega boards. ****************************************************/ #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灯又会变为绿色)按下KEY1确定继续调节第二个。注意:中间那个传感器变为红色后,按下KEY3让它变为绿色,因为我们中间要用来巡黑线的。就这样一次调节完五个传感器。当然,我们在调节的时候,是将小车放在线上的。当五个传感器调节完成后,继续按下KEY1键,小车退出调节模式,而进入正常的巡线模式,如果想再次调节,则再次按下KEY1键,依照前面说的进行调节。遥控模式下使用的遥控器是MiniQ 2WD附带的红外遥控器,使用时“开关”、“VOL+”、“VOL—”、“上一曲”,“下一曲”键分别代表“停止”、“前进”、“后退”、“左转”、“右转”。
下面是样例代码,整个例程还需要直插“LCD12864显示屏扩展板”作为显示器,并使用该显示器扩展版上的集成摇杆进行操作。该样例展示了MiniQ 2WD常用功能:蜂鸣、灯光、巡线、寻光、红外壁障,遥控功能。通过该样例可以更
直观的感受产品的强大以及学习产品的使用方法
注意:程序分两部分,须分别烧录在miniQ 2WD Plus上层板和下层小车中。
miniQ 2WD PLUS样例代码
/*************************************************** MiniQ 2WD plus (With Stainless Steel Probe) <http://www.dfrobot.com.cn/goods-1074.html> *************************************************** This example show you how to use LCD and button to control miniq_2wd. Created 2016-1-15 By Andy zhou <Andy.zhou@dfrobot.com> version:V1.0 GNU Lesser General Public License. See <http://www.gnu.org/licenses/> for details. All above must be included in any redistribution ****************************************************/ /***********Notice and Trouble shooting*************** 1.Connection and Diagram can be found here <http://wiki.dfrobot.com.cn/index.php?title=(SKU:DFR0302)MiniQ_2WD_Plus> 2.This code is tested on miniQ 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样例代码
/*************************************************** MiniQ 2WD plus (With Stainless Steel Probe) <http://www.dfrobot.com.cn/goods-1074.html> *************************************************** This example show you how to use LCD and button to control miniq_2wd. Created 2016-1-15 By Andy zhou <Andy.zhou@dfrobot.com> version:V1.0 GNU Lesser General Public License. See <http://www.gnu.org/licenses/> for details. All above must be included in any redistribution ****************************************************/ /***********Notice and Trouble shooting*************** 1.Connection and Diagram can be found here <http://wiki.dfrobot.com.cn/index.php?title=(SKU:DFR0302)MiniQ_2WD_Plus> 2.This code is tested on miniQ. ****************************************************/ #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(); } } }
这是“miniQ 2WD Plus + miniQ 2wd + Blelink + 蓝牙4.0 无线手柄”的典型应用。组建自己的遥控车,怎么样,很高档大气吧~
注:蓝牙4.0 无线手柄的数据传输格式,请参照蓝牙4.0 无线手柄的手柄BLE透传协议。
开发资料
miniQ 2wd plus原理图
miniQ 2wd + miniQ 2wd plus程序
购买 MiniQ 2WD Plus (SKU:DFR0302)