序言
制作小车前,我们需要先明确自己的需求:
通信:BT-04 蓝牙模块
电机驱动:L298N电机驱动模块
底盘架构:三个轮子,呈三角形排布。其中两个轮子与电机相连,另一个为万向轮。
蓝牙通信
小车计划采用“蓝牙调试器”软件的专业调试模式进行控制。我们需要先熟悉该软件发送的数据包结构。
包头 |
原数据 |
校验 |
包尾 |
1字节 |
自定义 |
1字节 |
1字节 |
0xA5 |
自定义 |
“原数据”所有字节之和的低8位 |
0x5A |
传输的数据类型及位数我们可以在软件中自行设置。
Arduino代码
基础配置信息
1 2 3 4 5 6 7 8 9 10 11 12
| #include "HardwareSerial.h"
#define rxDataLenth 5 #define bufferSize 10
bool isReading = false; int length = 0; char rxHead = 0xA5; char rxTail = 0x5A;
|
数据包结构体
1 2 3 4
| typedef struct { int go; int turn; } rxTypedef;
|
初始化函数
BT-04蓝牙模块的波特率为9600 bit/s,串口的波特率应与蓝牙模块相同,设置为9600 bit/s。
1 2 3 4 5 6 7 8
| void bluetoothInit(rxTypedef* remoteInfo) { remoteInfo->go = 0; remoteInfo->turn = 0; Serial.begin(9600); }
|
串口数据接收函数
每当Arduino接收到数据时,会调用 serialEvent函数,我们在该函数中对接收到的数据进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| char rxBuffer[bufferSize];
void serialEvent() { while (Serial.available()) { char tmp = Serial.read(); if (tmp == rxHead) { length = 0; isReading = true; } else if (isReading == false) { continue; }
rxBuffer[length++] = tmp;
if (tmp == rxTail) { isReading = false; remoteInfoUpdate(); } else if (length >= bufferSize) { length = 0; isReading = false; } } }
|
数据包解包函数
从缓存数组中取出每个变量的数据。此处未进行数据校验。
1 2 3 4 5 6 7 8
| void remoteInfoUpdate() { if (length != rxDataLenth) { return; } else { remoteInfo.go = rxBuffer[1]; remoteInfo.turn = rxBuffer[2]; } }
|
至此,蓝牙通信的有关代码全部完成。
电机驱动模块
电机基础信息配置
1 2 3 4 5 6 7 8
| #define Motor1_M1_Port 12 #define Motor1_M2_Port 13 #define Motor1_PWM_Port 3
#define Motor2_M1_Port A4 #define Motor2_M2_Port A5 #define Motor2_PWM_Port 11
|
电机初始化函数
1 2 3 4 5 6 7 8 9 10 11 12 13
| void motorInit() { pinMode(Motor1_M1_Port, OUTPUT); pinMode(Motor1_M2_Port, OUTPUT); pinMode(Motor2_M1_Port, OUTPUT); pinMode(Motor2_M2_Port, OUTPUT);
digitalWrite(Motor1_M1_Port, LOW); digitalWrite(Motor1_M2_Port, LOW); digitalWrite(Motor2_M1_Port, LOW); digitalWrite(Motor2_M2_Port, LOW); }
|
电机速度设置函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void motorSetSpeed(int v1, int v2) { if (v1 >= 0) { digitalWrite(Motor1_M1_Port, LOW); digitalWrite(Motor1_M2_Port, HIGH); } else { digitalWrite(Motor1_M1_Port, HIGH); digitalWrite(Motor1_M2_Port, LOW); } if (v2 >= 0) { digitalWrite(Motor2_M1_Port, LOW); digitalWrite(Motor2_M2_Port, HIGH); } else { digitalWrite(Motor2_M1_Port, HIGH); digitalWrite(Motor2_M2_Port, LOW); }
analogWrite(Motor1_PWM_Port, abs(v1)); analogWrite(Motor2_PWM_Port, abs(v2)); }
|
电机控制函数
1 2 3 4 5 6 7
| void motorCtrl(int go, int turn) { if (turn < 0) { motorSetSpeed(go * 2 + turn, go * 2); } else { motorSetSpeed(go * 2, go * 2 - turn); } }
|
至此,电机的驱动代码全部完成。
setup函数和loop函数
setup函数只会运行一次。在此函数中,我们进行各类初始化操作。
1 2 3 4 5 6
| void setup() {
motorInit(); bluetoothInit(&remoteInfo); }
|
loop函数会循环运行。在此函数中,我们执行需要循环的操作。
1 2 3 4 5
| void loop() { motorCtrl(remoteInfo.go, remoteInfo.turn); delay(100); }
|