电机使用文档

历代代码

阶段一

CRC-8/MAXIM校验码计算算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// CRC-8/MAXIM 计算函数
unsigned char crc8_maxim(unsigned char *ptr, unsigned char len)
{
  unsigned char crc = 0x00; // 初始值
  while (len--)
  {
    crc ^= *ptr++; // 逐字节异或
    for (int i = 0; i < 8; i++)
    { // LSB-first 处理
      if (crc & 0x01)
      {
        crc = (crc >> 1) ^ 0x8C; // 反转多项式 0x8C
      }
      else
      {
        crc >>= 1;
      }
    }
  }
  return crc; // 无最终异或
}

发送动态速度

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <Arduino.h>
unsigned char buffer[9]; // 接收缓冲区

// 定义两个变量,用于替换原数据帧中的 0x01 0x2C
unsigned char dynamic_byte1 = 0x01; // 默认值 0x01(可修改)
unsigned char dynamic_byte2 = 0x2C; // 默认值 0x2C(可修改)

// CRC-8/MAXIM 计算函数
unsigned char crc8_maxim(unsigned char *ptr, unsigned char len);

void setup()
{
  Serial2.begin(115200); // 初始化串口(根据实际使用的串口调整)
}

void loop()
{
  // 原始数据帧(9字节)
  unsigned char data_frame[] = {
      0x01, 0x64,
      dynamic_byte1, // 动态字节1
      dynamic_byte2, // 动态字节2
      0x00, 0x00, 0x00, 0x00, 0x00};
  // 计算 CRC-8/MAXIM
  unsigned char crc = crc8_maxim(data_frame, 9);
  // 发送完整帧(9数据 + 1CRC)
  Serial2.write(data_frame, sizeof(data_frame)); // 发送前9字节
  Serial2.write(crc);                            // 发送CRC字节
  delay(1000); // 每秒发送一次
}

unsigned char crc8_maxim(unsigned char *ptr, unsigned char len)
{
  unsigned char crc = 0x00; // 初始值
  while (len--)
  {
    crc ^= *ptr++; // 逐字节异或
    for (int i = 0; i < 8; i++)
    { // LSB-first 处理
      if (crc & 0x01)
      {
        crc = (crc >> 1) ^ 0x8C; // 反转多项式 0x8C
      }
      else
      {
        crc >>= 1;
      }
    }
  }
  return crc; // 无最终异或
}

速度限幅

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <Arduino.h>

// ================= 发送端配置 =================
int target_rpm = 200; // 示例:目标转速(超限值将被约束)

// ================= 接收端配置 =================
int actual_rpm = 0;            // 存储解析后的实际转速
unsigned char recv_buffer[10]; // 接收缓冲区
bool data_valid = false;       // 数据有效性标志

// ================= CRC函数 =================
unsigned char crc8_maxim(unsigned char *ptr, unsigned char len);

// ================= 初始化 =================
void setup()
{
  Serial.begin(115200);  // 调试输出
  Serial2.begin(115200); // 主串口(全双工)
}

// ================= 主循环 =================
void loop()
{
  // ----------------- 发送逻辑 -----------------
  static unsigned long last_send = 0;
  if (millis() - last_send >= 1000) // millis()返回的是从 Arduino 开发板开始运行当前程序起,经过的毫秒数。
  {// 每秒发送
    // 速度限幅:约束在 [-150, 150] RPM 之间
    int clamped_rpm = target_rpm;
    if (clamped_rpm > 150)
      clamped_rpm = 150;
    else if (clamped_rpm < -150)
      clamped_rpm = -150;
   
    // 转换为带符号的 16 位整数(10倍值)
    int16_t speed_value = clamped_rpm * 10;

    // 分解为两个字节(大端序)
    unsigned char byte1 = (speed_value >> 8) & 0xFF; // 高位字节
    unsigned char byte2 = speed_value & 0xFF;        // 低位字节

    // 构造数据帧
    unsigned char data_frame[] = {
        0x01, 0x64,
        byte1, byte2, // 动态字节
        0x00, 0x00, 0x00, 0x00, 0x00};

    // 计算并发送
    Serial2.write(data_frame, 9);             // 发送数据
    Serial2.write(crc8_maxim(data_frame, 9)); // 发送CRC

    // 调试输出(实际应用可删除)
    Serial.print("[SEND] Target RPM: ");
    Serial.print(target_rpm);
    Serial.print(" -> Clamped RPM: ");
    Serial.print(clamped_rpm);
    Serial.print(" -> HEX: 0x");
    Serial.print(byte1, HEX);
    Serial.print(byte2, HEX);
    Serial.println();
   
    last_send = millis();
  }

  // ----------------- 接收逻辑 -----------------
  static uint8_t recv_index = 0;
  while (Serial2.available() > 0)
  {
    recv_buffer[recv_index++] = Serial2.read();

    // 收满10字节后处理
    if (recv_index == 10)
    {
      // 验证帧头 + CRC
      if (recv_buffer[0] == 0x01 &&
          recv_buffer[1] == 0x64 &&
          crc8_maxim(recv_buffer, 9) == recv_buffer[9])
      {
        // 合并两字节(注意符号扩展)
        int16_t raw_value = (recv_buffer[2] << 8) | recv_buffer[3];
       
        // 转换为实际 RPM(考虑符号)
        actual_rpm = raw_value / 10; // 还原10倍关系
        data_valid = true;

        // 调试输出
        Serial.print("[RECV] Actual RPM: ");
        Serial.print(actual_rpm);
        Serial.print(" (HEX: 0x");
        Serial.print(recv_buffer[2], HEX);
        Serial.print(recv_buffer[3], HEX);
        Serial.println(")");
      }
      else
      {
        data_valid = false;
        Serial.println("[ERROR] Invalid Data!");
      }
      recv_index = 0; // 重置接收索引
    }
  }
}



unsigned char crc8_maxim(unsigned char *ptr, unsigned char len)
{
  unsigned char crc = 0x00;
  while (len--)
  {
    crc ^= *ptr++;
    for (int i = 0; i < 8; i++)
    {
      crc = (crc & 0x01) ? (crc >> 1) ^ 0x8C : crc >> 1;
    }
  }
  return crc;
}

蓝牙反馈数据

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <Arduino.h>
#include <BluetoothSerial.h>

BluetoothSerial SerialBT;
// ================= 发送端配置 =================
int target_rpm = 200; // 示例:目标转速(超限值将被约束)

// ================= 接收端配置 =================
int actual_rpm = 0;            // 存储解析后的实际转速
unsigned char recv_buffer[10]; // 接收缓冲区
bool data_valid = false;       // 数据有效性标志

// ================= CRC函数 =================
unsigned char crc8_maxim(unsigned char *ptr, unsigned char len);

// ================= 初始化 =================
void setup()
{
  Serial.begin(115200);      // 调试输出
  Serial2.begin(115200);     // 主串口(全双工)
  SerialBT.begin("BAKUMAN"); // 如果没有参数传入则默认是蓝牙名称是: "ESP32"
}

// ================= 主循环 =================
void loop()
{
// ----------------- 发送逻辑 -----------------
  static unsigned long last_send = 0;
  if (millis() - last_send >= 1000) // millis()返回的是从 Arduino 开发板开始运行当前程序起,经过的毫秒数。
  {// 每秒发送
// 速度限幅:约束在 [-150, 150] RPM 之间
    int clamped_rpm = target_rpm;
    if (clamped_rpm > 150)
      clamped_rpm = 150;
    else if (clamped_rpm < -150)
      clamped_rpm = -150;
// 转换为带符号的 16 位整数(10倍值)
    int16_t speed_value = clamped_rpm * 10;
// 分解为两个字节(大端序)
    unsigned char byte1 = (speed_value >> 8) & 0xFF; // 高位字节
    unsigned char byte2 = speed_value & 0xFF;        // 低位字节
// 构造数据帧
    unsigned char data_frame[] = {
        0x01, 0x64,
        byte1, byte2, // 动态字节
        0x00, 0x00, 0x00, 0x00, 0x00};
// 计算并发送
    Serial2.write(data_frame, 9);             // 发送数据
    Serial2.write(crc8_maxim(data_frame, 9)); // 发送CRC
    last_send = millis();
  }



  // ----------------- 接收逻辑 -----------------
  static uint8_t recv_index = 0;
  while (Serial2.available() > 0)
  {
    recv_buffer[recv_index++] = Serial2.read();
// 收满10字节后处理
    if (recv_index == 10)
    {
// 验证帧头 + CRC
      if (recv_buffer[0] == 0x01 &&
          recv_buffer[1] == 0x64 &&
          crc8_maxim(recv_buffer, 9) == recv_buffer[9])
      {
// 合并两字节(注意符号扩展)
        int16_t raw_value = (recv_buffer[2] << 8) | recv_buffer[3];
// 转换为实际 RPM(考虑符号)
        actual_rpm = raw_value / 10; // 还原10倍关系
        data_valid = true;
// 调试输出
        SerialBT.print("[RECV] Actual RPM: ");
        SerialBT.print(actual_rpm);
        SerialBT.print(" (HEX: 0x");
        SerialBT.print(recv_buffer[2], HEX);
        SerialBT.print(recv_buffer[3], HEX);
        SerialBT.println(")");
      }
      else
      {
        data_valid = false;
        SerialBT.println("[ERROR] Invalid Data!");
      }
      recv_index = 0; // 重置接收索引
    }
  }
}

unsigned char crc8_maxim(unsigned char *ptr, unsigned char len)
{
  unsigned char crc = 0x00;
  while (len--)
  {
    crc ^= *ptr++;
    for (int i = 0; i < 8; i++)
    {
      crc = (crc & 0x01) ? (crc >> 1) ^ 0x8C : crc >> 1;
    }
  }
  return crc;
}

加入模式切换

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <Arduino.h>
#include <BluetoothSerial.h>

BluetoothSerial SerialBT;

// ================= 预定义协议帧 =================
// 开环指令帧:01 A0 00 00 00 00 00 00 00 9E
const uint8_t open_loop_frame[10] = {
    0x01, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E};
   
// 速度环指令帧:01 A0 02 00 00 00 00 00 00 E4
const uint8_t speed_loop_frame[10] = {
    0x01, 0xA0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4};

// ================= 控制模式定义 =================
enum ControlMode
{
  SPEED_LOOP, // 速度环模式
  OPEN_LOOP   // 开环模式
};
ControlMode current_mode = SPEED_LOOP; // 初始模式

// ================= 发送端配置 =================
int target_rpm = 30; // 目标转速(速度环模式有效)

// ================= 接收端配置 =================
int actual_rpm = 0;               // 实际转速
unsigned char recv_buffer[10];    // 接收缓冲区
unsigned char bt_recv_buffer[10]; // 蓝牙接收缓冲区
bool data_valid = false;          // 数据有效性标志

// ================= CRC函数 =================
unsigned char crc8_maxim(unsigned char *ptr, unsigned char len)
{
  unsigned char crc = 0x00;
  while (len--)
  {
    crc ^= *ptr++;
    for (int i = 0; i < 8; i++)
    {
      crc = (crc & 0x01) ? (crc >> 1) ^ 0x8C : crc >> 1;
    }
  }
  return crc;
}

// ================= 初始化 =================
void setup()
{
  Serial.begin(115200);      // USB调试
  Serial2.begin(115200);     // 主串口(全双工)
  SerialBT.begin("BAKUMAN"); // 蓝牙名称
}

// ================= 主循环 =================
void loop()
{
// ----------------- 发送逻辑 -----------------
  static unsigned long last_send = 0;
  if (millis() - last_send >= 1000) // millis()返回的是从 Arduino 开发板开始运行当前程序起,经过的毫秒数。
  {// 每秒发送一次
    unsigned char data_frame[9];
// 根据模式构造数据帧
    if (current_mode == SPEED_LOOP)
    {// 速度环模式:发送转速指令
      int clamped_rpm = constrain(target_rpm, -150, 150); // 进行速度限幅
      int16_t speed_value = clamped_rpm * 10;
      data_frame[0] = 0x01;
      data_frame[1] = 0x64;
      data_frame[2] = (speed_value >> 8) & 0xFF;
      data_frame[3] = speed_value & 0xFF;
      memset(&data_frame[4], 0x00, 5); // 填充后续字节
      SerialBT.print("[SpeedLoop] Sent RPM: ");
      SerialBT.println(clamped_rpm);
    }
    else
    {
// 开环模式:不主动发送数据(或发送特定指令)
// 此处根据需求可填充开环控制参数
      memset(data_frame, 0x00, 9);
      SerialBT.println("[OpenLoop] No Data Sent");
    }
// 发送数据
    if (current_mode == SPEED_LOOP)
    { // 速度环模式发送
      unsigned char crc = crc8_maxim(data_frame, 9);
      Serial2.write(data_frame, 9);
      Serial2.write(crc);
    }
    last_send = millis();
  }

  // ----------------- 接收逻辑 -----------------
  static uint8_t recv_index = 0;
  while (Serial2.available() > 0)
  {
    recv_buffer[recv_index++] = Serial2.read();
// 完整帧处理
    if (recv_index == 10)
    {// CRC校验
      bool crc_ok = (crc8_maxim(recv_buffer, 9) == recv_buffer[9]);
// 速度数据解析(仅在速度环模式处理)
      if (crc_ok && current_mode == SPEED_LOOP &&
          recv_buffer[0] == 0x01 && recv_buffer[1] == 0x64)
      {
        int16_t raw_value = (recv_buffer[2] << 8) | recv_buffer[3];
        actual_rpm = raw_value / 10;
        data_valid = true;
        SerialBT.print("[RECV] Actual RPM: ");
        SerialBT.println(actual_rpm);
      }
      else if (!crc_ok)
      {
        data_valid = false;
        SerialBT.println("[ERROR] CRC Check Failed!");
      }
      recv_index = 0; // 重置接收
    }
  }

  // ----------------- 蓝牙指令处理 -----------------
  while (SerialBT.available() > 0)
  {
    char cmd = SerialBT.read();
    switch (toupper(cmd))
    {
    case 'S': // 切换速度环模式
      current_mode = SPEED_LOOP;
      Serial2.write(speed_loop_frame, sizeof(speed_loop_frame));
      SerialBT.println("Mode switched to SpeedLoop");
      break;

    case 'O': // 切换开环模式
      current_mode = OPEN_LOOP;
      Serial2.write(open_loop_frame, sizeof(open_loop_frame));
      SerialBT.println("Mode switched to OpenLoop");
      break;
     
    default:
      SerialBT.print("Unknown command: ");
      SerialBT.println(cmd);
      break;
    }
  }
}

上电默认开环

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include <Arduino.h>
#include <BluetoothSerial.h>

BluetoothSerial SerialBT;

// ================= 预定义协议帧 =================
// 上电反馈数据
const uint8_t POWER_ON_ACK[3] = {0xAA, 0x55, 0xFF};

// 开环指令帧:01 A0 00 00 00 00 00 00 00 9E
const uint8_t open_loop_frame[10] = {
0x01, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E};

// 速度环指令帧:01 A0 02 00 00 00 00 00 00 E4
const uint8_t speed_loop_frame[10] = {
0x01, 0xA0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4};

// ================= 控制模式定义 =================
enum ControlMode
{
SPEED_LOOP, // 速度环模式
OPEN_LOOP // 开环模式
};
ControlMode current_mode = OPEN_LOOP; // 初始模式

// ================= 发送端配置 =================
int target_rpm = 30; // 目标转速(速度环模式有效)

// ================= 接收端配置 =================
int actual_rpm = 0; // 实际转速
unsigned char recv_buffer[10]; // 接收缓冲区
unsigned char bt_recv_buffer[10]; // 蓝牙接收缓冲区
bool data_valid = false; // 数据有效性标志

// ================= CRC函数 =================
unsigned char crc8_maxim(unsigned char *ptr, unsigned char len)
{
unsigned char crc = 0x00;
while (len--)
{
crc ^= *ptr++;
for (int i = 0; i < 8; i++)
{
crc = (crc & 0x01) ? (crc >> 1) ^ 0x8C : crc >> 1;
}
}
return crc;
}

// ================= 初始化 =================
void setup()
{
Serial.begin(115200); // USB调试
Serial2.begin(115200); // 主串口(全双工)
SerialBT.begin("BAKUMAN"); // 蓝牙名称
delay(5000); // 因为我人手不能同时给电机和单片机上电以及连接蓝牙,所以此处有一个延时
// 等待电机上电反馈
unsigned long start = millis();
bool ack_received = false;

SerialBT.println("Waiting for motor initialization...");

// while (!ack_received && (millis() - start < 5000))
// { // 5秒超时
// if (Serial2.available() >= sizeof(POWER_ON_ACK))
// {
// uint8_t tmp[3];
// Serial2.readBytes(tmp, 3);

// if (memcmp(tmp, POWER_ON_ACK, 3) == 0)
// {
// ack_received = true;
// SerialBT.println("Motor Ready!");

// // 发送电流环指令
// Serial2.write(open_loop_frame, 10);
// delay(50);
// Serial2.flush(); // 清空缓冲区
// }
// }
// }
Serial2.write(open_loop_frame, 10);
delay(50);
Serial2.flush(); // 清空缓冲区
}

// ================= 主循环 =================
void loop()
{
// ----------------- 发送逻辑 -----------------
static unsigned long last_send = 0;
if (millis() - last_send >= 1000) // millis()返回的是从 Arduino 开发板开始运行当前程序起,经过的毫秒数。
{ // 每秒发送一次
unsigned char data_frame[9];

// 根据模式构造数据帧
if (current_mode == SPEED_LOOP)
{
// 速度环模式:发送转速指令
int clamped_rpm = constrain(target_rpm, -150, 150); // 进行速度限幅
int16_t speed_value = clamped_rpm * 10;
data_frame[0] = 0x01;
data_frame[1] = 0x64;
data_frame[2] = (speed_value >> 8) & 0xFF;
data_frame[3] = speed_value & 0xFF;
memset(&data_frame[4], 0x00, 5); // 填充后续字节

SerialBT.print("[SpeedLoop] Sent RPM: ");
SerialBT.println(clamped_rpm);
}
else
{
// 开环模式:不主动发送数据(或发送特定指令)
// 此处根据需求可填充开环控制参数
memset(data_frame, 0x00, 9);
SerialBT.println("[OpenLoop] No Data Sent");
}

// 发送数据
if (current_mode == SPEED_LOOP)
{ // 速度环模式发送
unsigned char crc = crc8_maxim(data_frame, 9);
Serial2.write(data_frame, 9);
Serial2.write(crc);
}

last_send = millis();
}

// ----------------- 接收逻辑 -----------------
static uint8_t recv_index = 0;
while (Serial2.available() > 0)
{
recv_buffer[recv_index++] = Serial2.read();

// 完整帧处理
if (recv_index == 10)
{
// CRC校验
bool crc_ok = (crc8_maxim(recv_buffer, 9) == recv_buffer[9]);

// 速度数据解析(仅在速度环模式处理)
if (crc_ok && current_mode == SPEED_LOOP &&
recv_buffer[0] == 0x01 && recv_buffer[1] == 0x64)
{
int16_t raw_value = (recv_buffer[2] << 8) | recv_buffer[3];
actual_rpm = raw_value / 10;
data_valid = true;
SerialBT.print("[RECV] Actual RPM: ");
SerialBT.println(actual_rpm);
}
else if (!crc_ok)
{
data_valid = false;
SerialBT.println("[ERROR] CRC Check Failed!");
}

recv_index = 0; // 重置接收
}
}

// ----------------- 蓝牙指令处理 -----------------
while (SerialBT.available() > 0)
{
char cmd = SerialBT.read();

switch (toupper(cmd))
{
case 'S': // 切换速度环模式
current_mode = SPEED_LOOP;
Serial2.write(speed_loop_frame, sizeof(speed_loop_frame));
SerialBT.println("Mode switched to SpeedLoop");

break;

case 'O': // 切换开环模式
current_mode = OPEN_LOOP;
Serial2.write(open_loop_frame, sizeof(open_loop_frame));
SerialBT.println("Mode switched to OpenLoop");

break;

default:
SerialBT.print("Unknown command: ");
SerialBT.println(cmd);
break;
}
}
}

250307阶段完成

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#include <Arduino.h>
#include <BluetoothSerial.h>

BluetoothSerial SerialBT;

// ================= 预定义协议帧 =================
// 电机ID
#define ID 0x01

// 上电反馈数据
const uint8_t POWER_ON_ACK[3] = {0xAA, 0x55, 0xFF};

// 开环指令帧:ID A0 00 00 00 00 00 00 00 9E
const uint8_t open_loop_frame[10] = {
ID, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E};

// 速度环指令帧:ID A0 02 00 00 00 00 00 00 E4
const uint8_t speed_loop_frame[10] = {
ID, 0xA0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4};

// 减速指令帧:01 64 00 00 00 00 00 00 00 50
const uint8_t stop_frame[10] = {
ID, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50};

// ================= 控制模式定义 =================
enum ControlMode
{
SPEED_LOOP, // 速度环模式
OPEN_LOOP // 开环模式
};
ControlMode current_mode = OPEN_LOOP; // 初始模式

// ================= 发送端配置 =================
int target_rpm = -100; // 目标转速(速度环模式有效)
int open_loop_current = 10000; // 开环电流值(范围:-32767 ~ 32767)

// ================= 接收端配置 =================
int actual_rpm = 0; // 实际转速
int actual_current = 0; // 实际电流
unsigned char recv_buffer[10]; // 接收缓冲区
unsigned char bt_recv_buffer[10]; // 蓝牙接收缓冲区
bool data_valid = false; // 数据有效性标志

// ================= CRC函数 =================
unsigned char crc8_maxim(unsigned char *ptr, unsigned char len)
{
unsigned char crc = 0x00;
while (len--)
{
crc ^= *ptr++;
for (int i = 0; i < 8; i++)
{
crc = (crc & ID) ? (crc >> 1) ^ 0x8C : crc >> 1;
}
}
return crc;
}

// ================= 初始化 =================
void setup()
{
Serial.begin(115200); // USB调试
Serial2.begin(115200); // 主串口(全双工)
SerialBT.begin(); // 蓝牙名称
delay(1000); // 因为我人手不能同时给电机和单片机上电以及连接蓝牙,所以此处有一个延时
// 等待电机上电反馈
unsigned long start = millis();
bool ack_received = false;

// SerialBT.println("Waiting for motor initialization...");
// 清空接收缓冲区
while (Serial2.available() > 0)
{
// 读取并丢弃缓冲区中的字符
Serial2.read();
}

// while (!ack_received && (millis() - start < 5000))
// { // 5秒超时
// if (Serial2.available() >= sizeof(POWER_ON_ACK))
// {
// uint8_t tmp[3];
// Serial2.readBytes(tmp, 3);

// if (memcmp(tmp, POWER_ON_ACK, 3) == 0)
// {
// ack_received = true;
// SerialBT.println("Motor Ready!");

// // 发送电流环指令
// Serial2.write(open_loop_frame, 10);
// delay(50);
// Serial2.flush(); // 清空缓冲区
// }
// }
// }
Serial2.write(open_loop_frame, 10);
delay(50);
Serial2.flush(); // 清空缓冲区
}

// ================= 主循环 =================
void loop()
{
// ----------------- 发送逻辑 -----------------
static unsigned long last_send = 0;
if (millis() - last_send >= 2000) // millis()返回的是从 Arduino 开发板开始运行当前程序起,经过的毫秒数。
{ // 每秒发送一次
unsigned char data_frame[9];

// 根据模式构造数据帧
if (current_mode == SPEED_LOOP)
{
// 速度环模式:发送转速指令
int clamped_rpm = constrain(target_rpm, -150, 150); // 进行速度限幅
uint16_t speed_value = clamped_rpm * 10;
data_frame[0] = ID;
data_frame[1] = 0x64;
data_frame[2] = (speed_value >> 8) & 0xFF;
data_frame[3] = speed_value & 0xFF;
memset(&data_frame[4], 0x00, 5); // 填充后续字节

// 速度环模式发送
unsigned char crc = crc8_maxim(data_frame, 9);
Serial2.write(data_frame, 9);
Serial2.write(crc);

SerialBT.print("[SpeedLoop] Sent RPM: ");
SerialBT.println(clamped_rpm);
}
else if (current_mode == OPEN_LOOP)
{
// 构造开环指令帧:ID 64 [电流值高8位] [电流值低8位] 00 00 00 00 00 [CRC]
data_frame[0] = ID;
data_frame[1] = 0x64;

// 将电流值转为大端字节序
int16_t current_raw = constrain(open_loop_current, -32767, 32767); // 进行限幅
uint16_t unsigned_current = (uint16_t)current_raw; // 转为无符号类型
data_frame[2] = (unsigned_current >> 8) & 0xFF; // 高字节
data_frame[3] = unsigned_current & 0xFF; // 低字节
memset(&data_frame[4], 0x00, 5); // 后续5字节填充0

// 计算CRC(前9字节)
unsigned char crc = crc8_maxim(data_frame, 9);
Serial2.write(data_frame, 9);
Serial2.write(crc);

// 调试输出
SerialBT.print("[OpenLoop] Current: ");
SerialBT.print(current_raw);
}

delay(1);
// ----------------- 接收逻辑 -----------------

static uint8_t recv_index = 0;
while (Serial2.available() > 0)
{
recv_buffer[recv_index++] = Serial2.read();

// 完整帧处理
if (recv_index == 10)
{
// CRC校验
bool crc_ok = (crc8_maxim(recv_buffer, 9) == recv_buffer[9]);

// 速度数据解析(仅在速度环模式处理)
if (crc_ok && current_mode == SPEED_LOOP &&
recv_buffer[0] == ID && recv_buffer[1] == 0x64)
{
int16_t raw_value = (recv_buffer[2] << 8) | recv_buffer[3];
actual_rpm = raw_value / 10;
data_valid = true;
SerialBT.print("[RECV] Actual RPM: ");
SerialBT.println(actual_rpm);
}
// 开环速度接收
else if (crc_ok && current_mode == OPEN_LOOP && recv_buffer[0] == ID && recv_buffer[1] == 0x64)
{
int16_t raw_value = (recv_buffer[2] << 8) | recv_buffer[3];
actual_rpm = raw_value / 10;
data_valid = true;
SerialBT.print("[RECV] Actual RPM: ");
SerialBT.println(actual_rpm);
}
else if (!crc_ok)
{
data_valid = false;
SerialBT.println("[ERROR] CRC Check Failed!");
}

recv_index = 0; // 重置接收
}
}
delay(1);
last_send = millis();
}
// ----------------- 蓝牙指令处理 -----------------
while (SerialBT.available() > 0)
{
char cmd = SerialBT.read();

switch (toupper(cmd))
{
case 'S': // 切换速度环模式
current_mode = SPEED_LOOP;
Serial2.write(stop_frame, sizeof(stop_frame));
delay(500);
Serial2.write(speed_loop_frame, sizeof(speed_loop_frame));
SerialBT.println("Mode switched to SpeedLoop");

break;

case 'O': // 切换开环模式
current_mode = OPEN_LOOP;
Serial2.write(stop_frame, sizeof(stop_frame)); // 减速保护电机
delay(500);
Serial2.write(open_loop_frame, sizeof(open_loop_frame));
SerialBT.println("Mode switched to OpenLoop");

break;

default:
SerialBT.print("Unknown command: ");
SerialBT.println(cmd);
break;
}
}
delay(1);
}

阶段二

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
#include <Arduino.h>
#include <BluetoothSerial.h>

BluetoothSerial SerialBT;

// 定义 UART1 的自定义引脚
#define UART1_TX_PIN 25 // 自定义 TX 引脚(GPIO25)
#define UART1_RX_PIN 26 // 自定义 RX 引脚(GPIO26)

// ================= 预定义协议帧 =================
#pragma pack(push, 1)
typedef struct // 定义一个结构体接收反馈数据
{
uint8_t id; // 设备ID (0x01)
uint8_t cmd; // 指令类型 (0x64)
int16_t param; // 参数(速度或电流)
uint8_t reserved[5]; // 保留字节
uint8_t crc; // CRC校验码
} MotorFrame;
#pragma pack(pop)

// 电机ID
#define ID 0x01

// 上电反馈数据
const uint8_t POWER_ON_ACK[3] = {0xAA, 0x55, 0xFF};

// 开环指令帧:ID A0 00 00 00 00 00 00 00 9E
const uint8_t open_loop_frame[10] = {
ID, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E};

// 速度环指令帧:ID A0 02 00 00 00 00 00 00 E4
const uint8_t speed_loop_frame[10] = {
ID, 0xA0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4};

// 减速指令帧:01 64 00 00 00 00 00 00 00 50
const uint8_t stop_frame[10] = {
ID, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50};

// ================= 控制模式定义 =================
enum ControlMode
{
SPEED_LOOP, // 速度环模式
OPEN_LOOP // 开环模式
};
ControlMode current_mode = OPEN_LOOP; // 初始模式

MotorFrame tx_frame = {
.id = 0x01,
.cmd = 0x64,
.param = 0,
.reserved = {0},
.crc = 0};

// ================= 发送端配置 =================
int target_rpm = -100; // 目标转速(速度环模式有效)
int open_loop_current = 10000; // 开环电流值(范围:-32767 ~ 32767)

// ================= 接收端配置 =================
int actual_rpm = 0; // 实际转速
int actual_current = 0; // 实际电流
unsigned char recv_buffer[10]; // 接收缓冲区
unsigned char bt_recv_buffer[10]; // 蓝牙接收缓冲区
bool data_valid = false; // 数据有效性标志

uint8_t sync_state = 0; // 同步状态机
unsigned long last_rx = 0; // 最后接收时间戳
const uint32_t RX_TIMEOUT = 2000; // 接收超时(ms)

// ================= 函数声明 =================
unsigned char crc8_maxim(unsigned char *ptr, unsigned char len);
void process_Serial1_data();
void verify_frame();
void process_bluetooth();
void sendModeQuery();
// ================= 初始化 =================
void setup()
{
// 直接使用预定义的 Serial1,无需重新声明
Serial1.begin(115200, SERIAL_8N1, UART1_RX_PIN, UART1_TX_PIN);
SerialBT.begin(); // 蓝牙名称
delay(1000);

// 清空软件串口接收缓冲区
while (Serial1.available() > 0)
{
Serial1.read();
}

Serial1.write(open_loop_frame, 10); // 发送开环指令
delay(50);
Serial1.flush();
}

// ================= 主循环 =================
void loop()
{

// ----------------- 发送逻辑 -----------------
static unsigned long last_send = 0;
static unsigned long last_query = 0;
const unsigned long query_interval = 1000;

if (millis() - last_send >= 1000) // millis()返回的是从 Arduino 开发板开始运行当前程序起,经过的毫秒数。
{ // 每秒发送一次
unsigned char data_frame[9];

// 根据模式构造数据帧
if (current_mode == SPEED_LOOP)
{
// 速度环模式:发送转速指令
int clamped_rpm = constrain(target_rpm, -150, 150); // 进行速度限幅
uint16_t speed_value = clamped_rpm * 10;
data_frame[0] = ID;
data_frame[1] = 0x64;
data_frame[2] = (speed_value >> 8) & 0xFF;
data_frame[3] = speed_value & 0xFF;
memset(&data_frame[4], 0x00, 5); // 填充后续字节

// 速度环模式发送
unsigned char crc = crc8_maxim(data_frame, 9);
Serial1.write(data_frame, 9);
Serial1.write(crc);

SerialBT.print("[SpeedLoop] Sent RPM: ");
SerialBT.println(clamped_rpm);
}
else if (current_mode == OPEN_LOOP)
{
// 构造开环指令帧:ID 64 [电流值高8位] [电流值低8位] 00 00 00 00 00 [CRC]
data_frame[0] = ID;
data_frame[1] = 0x64;

// 将电流值转为大端字节序
int16_t current_raw = constrain(open_loop_current, -32767, 32767); // 进行限幅
uint16_t unsigned_current = (uint16_t)current_raw; // 转为无符号类型
data_frame[2] = (unsigned_current >> 8) & 0xFF; // 高字节
data_frame[3] = unsigned_current & 0xFF; // 低字节
memset(&data_frame[4], 0x00, 5); // 后续5字节填充0

// 计算CRC(前9字节)
unsigned char crc = crc8_maxim(data_frame, 9);
Serial1.write(data_frame, 9);
Serial1.write(crc);

// 调试输出
SerialBT.print("[OpenLoop] Current: ");
SerialBT.print(current_raw);
}

delay(1);
sendModeQuery();

// ----------------- 接收逻辑 -----------------
if (Serial1.available())
{
process_Serial1_data();
}

last_send = millis();

process_bluetooth();
}
}

unsigned char crc8_maxim(unsigned char *ptr, unsigned char len)
{
unsigned char crc = 0x00;
while (len--)
{
crc ^= *ptr++;
for (int i = 0; i < 8; i++)
{
crc = (crc & ID) ? (crc >> 1) ^ 0x8C : crc >> 1;
}
}
return crc;
}

// // ================= 数据接收状态机 =================
// void process_Serial1_data()
// {
// while (Serial1.available())
// {
// uint8_t c = Serial1.read();

// // 状态机处理
// switch (sync_state)
// {
// case 0: // 寻找0x01
// if (c == 0x01)
// sync_state = 1;
// break;

// case 1: // 检查后续是否为0x64
// if (c == 0x64)
// {
// recv_buffer[0] = 0x01;
// recv_buffer[1] = 0x64;
// sync_state = 2;
// last_rx = millis();
// }
// else
// {
// sync_state = 0; // 同步失败,重新开始
// }
// break;

// case 2: // 接收剩余数据
// static uint8_t idx = 2;
// recv_buffer[idx++] = c;
// last_rx = millis();

// // 检查是否收满
// if (idx >= 9)
// {
// verify_frame();
// idx = 0;
// sync_state = 0;
// }
// break;
// }

// // 超时重置
// if ((sync_state == 2) && (millis() - last_rx > RX_TIMEOUT))
// {
// sync_state = 0;
// memset(recv_buffer, 0, sizeof(recv_buffer));
// }
// }
// }

// // ================= 帧验证 =================
// void verify_frame()
// {
// MotorFrame *frame = (MotorFrame *)recv_buffer;

// // CRC校验
// if (crc8_maxim(recv_buffer, 9) != frame->crc)
// {
// SerialBT.println("[ERROR] CRC mismatch");
// return;
// }

// // 数据解析
// if (frame->cmd == 0x64)
// {
// int16_t raw_value = frame->param;
// actual_rpm = raw_value / 10;
// SerialBT.print("RPM: ");
// SerialBT.println(actual_rpm);
// }
// }

void sendModeQuery()
{
uint8_t query_frame[10] = {ID, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
query_frame[9] = crc8_maxim(query_frame, 9);
Serial1.write(query_frame, 10);
}

// ================= 改进的状态机接收 =================
void process_Serial1_data()
{
static uint8_t idx = 0; // 统一索引管理
static uint32_t last_rx = 0; // 最后接收时间戳

while (Serial1.available() > 0)
{
uint8_t c = Serial1.read(); // 串口1的接收数据

switch (sync_state)
{
case 0: // 同步头第一阶段
if (c == 0x01)
{
recv_buffer[0] = c;
sync_state = 1;
}
break;

case 1: // 同步头第二阶段
if (c == 0x64)
{
recv_buffer[1] = c;
idx = 2; // 重置索引
sync_state = 2;
last_rx = millis();
}
else
{
sync_state = 0; // 同步失败
}
break;

case 2: // 数据接收阶段
if (idx < sizeof(recv_buffer))
{
recv_buffer[idx++] = c;
last_rx = millis();
}

// 完整帧接收完成
if (idx >= sizeof(recv_buffer))
{
verify_frame();
sync_state = 0; // 重置状态机
idx = 0;
}
break;
}

// 超时处理(2秒无数据)
if (sync_state == 2 && (millis() - last_rx) > RX_TIMEOUT)
{
SerialBT.println("[ERROR] Reception timeout");
sync_state = 0;
idx = 0;
memset(recv_buffer, 0, sizeof(recv_buffer));
}
}
}

// ================= 增强的帧验证 =================
void verify_frame()
{ // 做开环检测
// CRC校验(前9字节)
uint8_t calculated_crc = crc8_maxim(recv_buffer, 9);
if (calculated_crc != recv_buffer[9])
{
SerialBT.printf("[ERROR] CRC Mismatch: 0x%02X vs 0x%02X\n", calculated_crc, recv_buffer[9]);
return;
}

if (recv_buffer[1] == 0x75)
{
uint8_t mode = recv_buffer[2];
if (mode == 0x00)
{
current_mode = OPEN_LOOP;
SerialBT.println("Mode: Open");
}
else if (mode == 0x02)
{
current_mode = SPEED_LOOP;
SerialBT.println("Mode: Speed");
}
}
else
{
// 速度值解析
// 协议解析(大端字节序处理)
if (recv_buffer[1] == 0x64)
{ // 速度环反馈
int16_t raw_value = (recv_buffer[2] << 8) | recv_buffer[3];
actual_rpm = raw_value / 10; // 实际转速

// 电流值解析
int16_t current = (recv_buffer[4] << 8) | recv_buffer[5];

SerialBT.printf("RPM: %d\tCurrent: %dmA\n", actual_rpm, current);
}
else
{
SerialBT.printf("[WARN] Unsupported command: 0x%02X\n", recv_buffer[1]);
}
}
}

void process_bluetooth()
{
// ----------------- 蓝牙指令处理 -----------------
// ...(原有蓝牙逻辑保持不变,但需修改模式切换时的串口操作)...
while (SerialBT.available() > 0)
{
char cmd = SerialBT.read();
switch (toupper(cmd))
{
case 'S':
current_mode = SPEED_LOOP;
Serial1.write(stop_frame, sizeof(stop_frame)); // 替换为 Serial1
delay(500);
Serial1.write(speed_loop_frame, sizeof(speed_loop_frame)); // 替换
break;
case 'O':
current_mode = OPEN_LOOP;
Serial1.write(stop_frame, sizeof(stop_frame)); // 替换
delay(500);
Serial1.write(open_loop_frame, sizeof(open_loop_frame)); // 替换
break;
}
}
}

单电机的最后一版

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
#include <Arduino.h>
#include <BluetoothSerial.h>

BluetoothSerial SerialBT;



// ================= 预定义协议帧 =================
#pragma pack(push, 1)
typedef struct // 定义一个结构体接收反馈数据
{
uint8_t id; // 设备ID (0x01)
uint8_t cmd; // 指令类型 (0x64)
int16_t param; // 参数(速度或电流)
uint8_t reserved[5]; // 保留字节
uint8_t crc; // CRC校验码
} MotorFrame;
#pragma pack(pop)

// 电机ID
#define ID 0x01

// 上电反馈数据
const uint8_t POWER_ON_ACK[3] = {0xAA, 0x55, 0xFF};

// 开环指令帧:ID A0 00 00 00 00 00 00 00 9E
const uint8_t open_loop_frame[10] = {
ID, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E};

// 速度环指令帧:ID A0 02 00 00 00 00 00 00 E4
const uint8_t speed_loop_frame[10] = {
ID, 0xA0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4};

// 减速指令帧:01 64 00 00 00 00 00 00 00 50
const uint8_t stop_frame[10] = {
ID, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50};

// ================= 控制模式定义 =================
enum ControlMode
{
SPEED_LOOP, // 速度环模式
OPEN_LOOP // 开环模式
};
ControlMode current_mode = OPEN_LOOP; // 初始模式

MotorFrame tx_frame = {
.id = 0x01,
.cmd = 0x64,
.param = 0,
.reserved = {0},
.crc = 0};

// ================= 发送端配置 =================
int target_rpm = -100; // 目标转速(速度环模式有效)
int open_loop_current = 10000; // 开环电流值(范围:-32767 ~ 32767)

// ================= 接收端配置 =================
int actual_rpm = 0; // 实际转速
int actual_current = 0; // 实际电流
unsigned char recv_buffer[10]; // 接收缓冲区
unsigned char bt_recv_buffer[10]; // 蓝牙接收缓冲区
bool data_valid = false; // 数据有效性标志

uint8_t sync_state = 0; // 同步状态机
unsigned long last_rx = 0; // 最后接收时间戳
const uint32_t RX_TIMEOUT = 2000; // 接收超时(ms)

// ================= 函数声明 =================
unsigned char crc8_maxim(unsigned char *ptr, unsigned char len);
void process_Serial2_data();
void verify_frame();
void process_bluetooth();
void sendModeQuery();
// ================= 初始化 =================
void setup()
{
// 直接使用预定义的 Serial2,无需重新声明
Serial2.begin(115200);
SerialBT.begin(); // 蓝牙名称
delay(1000);

// 清空软件串口接收缓冲区
while (Serial2.available() > 0)
{
Serial2.read();
}

Serial2.write(open_loop_frame, 10); // 发送开环指令
delay(50);
Serial2.flush();
}

// ================= 主循环 =================
void loop()
{

// ----------------- 发送逻辑 -----------------
static unsigned long last_send = 0;
static unsigned long last_query = 0;
const unsigned long query_interval = 1000;

if (millis() - last_send >= 1000) // millis()返回的是从 Arduino 开发板开始运行当前程序起,经过的毫秒数。
{ // 每秒发送一次
unsigned char data_frame[9];

// 根据模式构造数据帧
if (current_mode == SPEED_LOOP)
{
// 速度环模式:发送转速指令
int clamped_rpm = constrain(target_rpm, -150, 150); // 进行速度限幅
uint16_t speed_value = clamped_rpm * 10;
data_frame[0] = ID;
data_frame[1] = 0x64;
data_frame[2] = (speed_value >> 8) & 0xFF;
data_frame[3] = speed_value & 0xFF;
memset(&data_frame[4], 0x00, 5); // 填充后续字节

// 速度环模式发送
unsigned char crc = crc8_maxim(data_frame, 9);
Serial2.write(data_frame, 9);
Serial2.write(crc);

SerialBT.print("[SpeedLoop] Sent RPM: ");
SerialBT.println(clamped_rpm);
}
else if (current_mode == OPEN_LOOP)
{
// 构造开环指令帧:ID 64 [电流值高8位] [电流值低8位] 00 00 00 00 00 [CRC]
data_frame[0] = ID;
data_frame[1] = 0x64;

// 将电流值转为大端字节序
int16_t current_raw = constrain(open_loop_current, -32767, 32767); // 进行限幅
uint16_t unsigned_current = (uint16_t)current_raw; // 转为无符号类型
data_frame[2] = (unsigned_current >> 8) & 0xFF; // 高字节
data_frame[3] = unsigned_current & 0xFF; // 低字节
memset(&data_frame[4], 0x00, 5); // 后续5字节填充0

// 计算CRC(前9字节)
unsigned char crc = crc8_maxim(data_frame, 9);
Serial2.write(data_frame, 9);
Serial2.write(crc);

// 调试输出
SerialBT.print("[OpenLoop] Current: ");
SerialBT.print(current_raw);
}

delay(1);
sendModeQuery();

// ----------------- 接收逻辑 -----------------
if (Serial2.available())
{
process_Serial2_data();
}

last_send = millis();

process_bluetooth();
}
}

unsigned char crc8_maxim(unsigned char *ptr, unsigned char len)
{
unsigned char crc = 0x00;
while (len--)
{
crc ^= *ptr++;
for (int i = 0; i < 8; i++)
{
crc = (crc & ID) ? (crc >> 1) ^ 0x8C : crc >> 1;
}
}
return crc;
}

// // ================= 数据接收状态机 =================
// void process_Serial2_data()
// {
// while (Serial2.available())
// {
// uint8_t c = Serial2.read();

// // 状态机处理
// switch (sync_state)
// {
// case 0: // 寻找0x01
// if (c == 0x01)
// sync_state = 1;
// break;

// case 1: // 检查后续是否为0x64
// if (c == 0x64)
// {
// recv_buffer[0] = 0x01;
// recv_buffer[1] = 0x64;
// sync_state = 2;
// last_rx = millis();
// }
// else
// {
// sync_state = 0; // 同步失败,重新开始
// }
// break;

// case 2: // 接收剩余数据
// static uint8_t idx = 2;
// recv_buffer[idx++] = c;
// last_rx = millis();

// // 检查是否收满
// if (idx >= 9)
// {
// verify_frame();
// idx = 0;
// sync_state = 0;
// }
// break;
// }

// // 超时重置
// if ((sync_state == 2) && (millis() - last_rx > RX_TIMEOUT))
// {
// sync_state = 0;
// memset(recv_buffer, 0, sizeof(recv_buffer));
// }
// }
// }

// // ================= 帧验证 =================
// void verify_frame()
// {
// MotorFrame *frame = (MotorFrame *)recv_buffer;

// // CRC校验
// if (crc8_maxim(recv_buffer, 9) != frame->crc)
// {
// SerialBT.println("[ERROR] CRC mismatch");
// return;
// }

// // 数据解析
// if (frame->cmd == 0x64)
// {
// int16_t raw_value = frame->param;
// actual_rpm = raw_value / 10;
// SerialBT.print("RPM: ");
// SerialBT.println(actual_rpm);
// }
// }

void sendModeQuery()
{
uint8_t query_frame[10] = {ID, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
query_frame[9] = crc8_maxim(query_frame, 9);
Serial2.write(query_frame, 10);
}

// ================= 改进的状态机接收 =================
void process_Serial2_data()
{
static uint8_t idx = 0; // 统一索引管理
static uint32_t last_rx = 0; // 最后接收时间戳

while (Serial2.available() > 0)
{
uint8_t c = Serial2.read(); // 串口1的接收数据

switch (sync_state)
{
case 0: // 同步头第一阶段
if (c == 0x01)
{
recv_buffer[0] = c;
sync_state = 1;
}
break;

case 1: // 同步头第二阶段
if (c == 0x64)
{
recv_buffer[1] = c;
idx = 2; // 重置索引
sync_state = 2;
last_rx = millis();
}
else
{
sync_state = 0; // 同步失败
}
break;

case 2: // 数据接收阶段
if (idx < sizeof(recv_buffer))
{
recv_buffer[idx++] = c;
last_rx = millis();
}

// 完整帧接收完成
if (idx >= sizeof(recv_buffer))
{
verify_frame();
sync_state = 0; // 重置状态机
idx = 0;
}
break;
}

// 超时处理(2秒无数据)
if (sync_state == 2 && (millis() - last_rx) > RX_TIMEOUT)
{
SerialBT.println("[ERROR] Reception timeout");
sync_state = 0;
idx = 0;
memset(recv_buffer, 0, sizeof(recv_buffer));
}
}
}

// ================= 增强的帧验证 =================
void verify_frame()
{ // 做开环检测
// CRC校验(前9字节)
uint8_t calculated_crc = crc8_maxim(recv_buffer, 9);
if (calculated_crc != recv_buffer[9])
{
SerialBT.printf("[ERROR] CRC Mismatch: 0x%02X vs 0x%02X\n", calculated_crc, recv_buffer[9]);
return;
}

if (recv_buffer[1] == 0x75)
{
uint8_t mode = recv_buffer[2];
if (mode == 0x00)
{
current_mode = OPEN_LOOP;
SerialBT.println("Mode: Open");
}
else if (mode == 0x02)
{
current_mode = SPEED_LOOP;
SerialBT.println("Mode: Speed");
}
}
else
{
// 速度值解析
// 协议解析(大端字节序处理)
if (recv_buffer[1] == 0x64)
{ // 速度环反馈
int16_t raw_value = (recv_buffer[2] << 8) | recv_buffer[3];
actual_rpm = raw_value / 10; // 实际转速

// 电流值解析
int16_t current = (recv_buffer[4] << 8) | recv_buffer[5];

SerialBT.printf("RPM: %d\tCurrent: %dmA\n", actual_rpm, current);
}
else
{
SerialBT.printf("[WARN] Unsupported command: 0x%02X\n", recv_buffer[1]);
}
}
}

void process_bluetooth()
{
// ----------------- 蓝牙指令处理 -----------------
// ...(原有蓝牙逻辑保持不变,但需修改模式切换时的串口操作)...
while (SerialBT.available() > 0)
{
char cmd = SerialBT.read();
switch (toupper(cmd))
{
case 'S':
current_mode = SPEED_LOOP;
Serial2.write(stop_frame, sizeof(stop_frame)); // 替换为 Serial2
delay(500);
Serial2.write(speed_loop_frame, sizeof(speed_loop_frame)); // 替换
break;
case 'O':
current_mode = OPEN_LOOP;
Serial2.write(stop_frame, sizeof(stop_frame)); // 替换
delay(500);
Serial2.write(open_loop_frame, sizeof(open_loop_frame)); // 替换
break;
}
}
}

整理完代码

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#include <Arduino.h>
#include <BluetoothSerial.h>

BluetoothSerial SerialBT;

/* =============================================
数据结构定义
============================================= */
#pragma pack(push, 1)
/**
* @brief 电机反馈数据结构体
* @note 使用紧凑内存布局,总长度10字节
*/
typedef struct
{
uint8_t id; // 设备ID (0x01)
uint8_t cmd; // 指令类型 (0x64=速度环,0xA0=模式设置)
int16_t param; // 参数(速度或电流)
uint8_t reserved[5]; // 保留字节
uint8_t crc; // CRC校验码
} MotorFrame;
#pragma pack(pop)

/* =============================================
宏定义与常量
============================================= */
#define DEVICE_ID 0x01 // 本设备ID
#define RX_TIMEOUT_MS 2000 // 串口接收超时
#define CONTROL_INTERVAL_MS 1000 // 主控制周期

// 预定义指令帧(完整帧含CRC)
const uint8_t FRAME_OPEN_LOOP[10] = {DEVICE_ID, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E};
const uint8_t FRAME_SPEED_LOOP[10] = {DEVICE_ID, 0xA0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4};
const uint8_t FRAME_STOP[10] = {DEVICE_ID, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50};

/* =============================================
全局变量声明
============================================= */
enum ControlMode
{
SPEED_LOOP,
OPEN_LOOP
};
ControlMode current_mode = OPEN_LOOP; // 当前控制模式

// 控制参数
int target_rpm = -100; // 速度环目标转速(-150~150)
int open_loop_current = 10000; // 开环电流值(-32767~32767)

// 状态参数
int actual_rpm = 0; // 实际转速(通过反馈更新)
int actual_current = 0; // 实际电流(通过反馈更新)

/* =============================================
函数声明
============================================= */
uint8_t crc8_maxim(uint8_t *data, uint8_t len);
void send_control_frame();
void process_serial_data();
void process_bluetooth();
void validate_frame(uint8_t *frame);
void update_system_status();
void send_mode_query();

/* =============================================
初始化配置
============================================= */
void setup()
{
Serial2.begin(115200); // 电机通信串口
SerialBT.begin("MotorCtrl"); // 蓝牙设备名称

// 发送初始模式设置
Serial2.write(FRAME_OPEN_LOOP, sizeof(FRAME_OPEN_LOOP));
delay(50);
Serial2.flush(); // 等待发送完成

SerialBT.println("System Initialized");
}

/* =============================================
主循环程序
============================================= */
void loop()
{
static uint32_t last_control = 0;

// 定时发送控制指令
if (millis() - last_control >= CONTROL_INTERVAL_MS)
{
send_control_frame();
send_mode_query();
last_control = millis();
}

// 处理串口数据
if (Serial2.available())
{
process_serial_data();
}

// 处理蓝牙指令
process_bluetooth();
}

/* =============================================
功能函数实现
============================================= */

/**
* @brief 计算CRC8校验值(Maxim/Dallas算法)
* @param data 数据指针
* @param len 数据长度
* @return 计算得到的CRC值
*/
uint8_t crc8_maxim(uint8_t *data, uint8_t len)
{
uint8_t crc = 0x00;
while (len--)
{
crc ^= *data++;
for (uint8_t i = 0; i < 8; i++)
{
crc = (crc & 0x01) ? (crc >> 1) ^ 0x8C : crc >> 1;
}
}
return crc;
}

/**
* @brief 发送控制帧(根据当前模式)
*/
void send_control_frame()
{
uint8_t frame[10] = {DEVICE_ID, 0x64};
uint16_t value;

switch (current_mode)
{
case SPEED_LOOP:
// 构造速度环指令(大端序)
value = constrain(target_rpm, -150, 150) * 10;
frame[2] = (value >> 8) & 0xFF;
frame[3] = value & 0xFF;
break;

case OPEN_LOOP:
// 构造开环指令(有符号转无符号)
value = (uint16_t)constrain(open_loop_current, -32767, 32767);
frame[2] = (value >> 8) & 0xFF;
frame[3] = value & 0xFF;
break;
}

// 填充保留字节并计算CRC
memset(&frame[4], 0x00, 5);
frame[9] = crc8_maxim(frame, 9);

Serial2.write(frame, sizeof(frame));
}

/**
* @brief 处理串口接收数据(状态机实现)
*/
void process_serial_data()
{
static uint8_t sync_state = 0; // 同步状态机
static uint8_t buffer_index = 0; // 缓冲区索引
static uint8_t rx_buffer[10]; // 接收缓冲区
static uint32_t last_rx_time = 0; // 最后接收时间

while (Serial2.available())
{
uint8_t c = Serial2.read();

// 状态机处理
switch (sync_state)
{
case 0: // 等待帧头1
if (c == DEVICE_ID)
{
rx_buffer[0] = c;
sync_state = 1;
}
break;

case 1: // 等待帧头2
if (c == 0x64 || c == 0x75)
{
rx_buffer[1] = c;
buffer_index = 2;
sync_state = 2;
last_rx_time = millis();
}
else
{
sync_state = 0;
}
break;

case 2: // 接收数据体
rx_buffer[buffer_index++] = c;
last_rx_time = millis();

// 完成帧接收
if (buffer_index >= sizeof(rx_buffer))
{
validate_frame(rx_buffer);
sync_state = 0;
}
break;
}

// 超时处理
if (sync_state == 2 && (millis() - last_rx_time) > RX_TIMEOUT_MS)
{
sync_state = 0;
memset(rx_buffer, 0, sizeof(rx_buffer));
}
}
}

/**
* @brief 验证并处理接收到的数据帧
* @param frame 接收到的完整数据帧
*/
void validate_frame(uint8_t *frame)
{
// CRC校验
if (crc8_maxim(frame, 9) != frame[9])
{
SerialBT.println("CRC Error");
return;
}

// 模式查询响应处理
if (frame[1] == 0x75)
{

if (frame[2] == 0x02)
{
Serial2.write(FRAME_STOP, sizeof(FRAME_STOP));
delay(500);
current_mode = OPEN_LOOP;
Serial2.write(FRAME_OPEN_LOOP, sizeof(FRAME_OPEN_LOOP));
}
SerialBT.printf("Mode Updated: %s\n", current_mode);
return;
}

// 数据帧解析
if (frame[1] == 0x64)
{
actual_rpm = ((int16_t)(frame[2] << 8) | frame[3]) / 10;
actual_current = (int16_t)(frame[4] << 8) | frame[5];

SerialBT.printf("RPM: %d\tCurrent: %dmA\n", actual_rpm, actual_current);
}
}

/**
* @brief 处理蓝牙输入指令
*/
void process_bluetooth()
{
while (SerialBT.available())
{
char cmd = toupper(SerialBT.read());

switch (cmd)
{
case 'S': // 速度环模式
Serial2.write(FRAME_STOP, sizeof(FRAME_STOP));
delay(500);
current_mode = SPEED_LOOP;
Serial2.write(FRAME_SPEED_LOOP, sizeof(FRAME_SPEED_LOOP));
break;

case 'O': // 开环模式
Serial2.write(FRAME_STOP, sizeof(FRAME_STOP));
delay(500);
current_mode = OPEN_LOOP;
Serial2.write(FRAME_OPEN_LOOP, sizeof(FRAME_OPEN_LOOP));
break;
}
}
}

/**
* @brief 发送模式查询指令
*/
void send_mode_query()
{
uint8_t query_frame[10] = {DEVICE_ID, 0x75};
query_frame[9] = crc8_maxim(query_frame, 9);
Serial2.write(query_frame, sizeof(query_frame));
}