协议规范

端到端

分为PC端和设备端

  • PC端运行调试程序
  • 设备端实现自己的业务

设备端向PC端发送数据,称之为PUSH
设备端获取PC端数据,称之为PULL

PUSH 协议规范

帧头 命令位 数据长度 数据位 校验位 帧尾
字节数 1 1 1 n 1 1
默认值 0x7a 待定 待定 待定 待定 0x7b

命令位: 表示命令类型
数据位的n值,由数据长度位的值决定
校验位:(命令位 + 数据长度位 + 数据位)的结果取高位

PULL 协议规范

帧头 命令位 数据长度 数据位 校验位 帧尾
字节数 1 1 1 n 1 1
默认值 0x7b 待定 待定 待定 待定 0x7a

命令位: 表示命令类型
数据位的n值,由数据长度位的值决定
校验位:(命令位 + 数据长度位 + 数据位)的结果取高位

已实现协议

命令 方向 说明 示例
0x00 接收 速度 0x7a 0x00 11 2 2 2 1 0x7b
0x01 接收 直立环PID 0x7a 0x01 17 4 4 4 1 0x7b
0x02 接收 速度环PID 0x7a 0x02 17 4 4 4 1 0x7b
0x03 接收 转向环PID 0x7a 0x03 17 4 4 4 1 0x7b

PUSH协议

通道消息

帧头 命令位 数据长度 数据位 校验位 帧尾
字节数 1 1 1 4 …… 1 1
默认值 0x7a 0x01 待定 计算获得 0x7b
  • 数据位长度为4的倍数
  • 每4位表示一个通道
  • 每4位表示一个float类型数据

速度消息

帧头 命令位 数据长度 数据位 校验位 帧尾
x y z ax ay az gx gy gz v
字节数 1 1 1 4 4 4 4 4 4 4 4 4 4 1 1
默认值 0x7a 0x01 待定 待定 0x7b
  • x:4个字节,float类型。x轴速度
  • y: 4个字节,float类型。y轴速度
  • z: 4个字节,float类型。z轴速度
  • ax:4个字节,float类型。x轴加速度
  • ay: 4个字节,float类型。y轴加速度
  • az: 4个字节,float类型。z轴加速度
  • gx:4个字节,float类型。x轴角速度
  • gy: 4个字节,float类型。y轴角速度
  • gz: 4个字节,float类型。z轴角速度
  • v: 4个字节,float类型。电压值

PULL协议

配置PID

帧头 命令位 数据长度 数据位 校验位 帧尾
P i d
字节数 1 1 1 1 4 4 4 1 1
默认值 0x7b 0x01 待定 待定 0x7a

数据位包含了

  • id: 第几组pid
  • p:4个字节,float类型
  • i: 4个字节,float类型
  • d: 4个字节,float类型

设置速度

帧头 命令位 数据长度 数据位 校验位 帧尾
x y z
字节数 1 1 1 4 4 4 1 1
默认值 0x7b 0x02 待定 待定 0x7a

数据位包含了

  • x:4个字节,float类型。x轴速度
  • y: 4个字节,float类型。y轴速度
  • z: 4个字节,float类型。z轴速度

库使用说明

库文件

  1. #ifndef __PID_H__
  2. #define __PID_H__
  3. void PID_init();
  4. void PID_push(float *chns, int chn_cnt);
  5. void PID_pull(unsigned char dat);
  6. void PID_parse();
  7. extern void PID_on_config_pid(unsigned char id, float kp, float ki, float kd);
  8. #endif
  1. #include "PID.h"
  2. #include "Bluetooth.h"
  3. //////////// PUSH ///////////////
  4. #define TX_FRAME_HEAD 0x7a
  5. #define TX_FRAME_END 0x7b
  6. #define TX_CMD_CHN 0x01
  7. //////////// PULL //////////////
  8. #define RX_FRAME_HEAD 0x7b
  9. #define RX_FRAME_END 0x7a
  10. #define RX_CMD_PID 0x01
  11. #define MAX_BUF 64
  12. static unsigned char buf[MAX_BUF];
  13. static unsigned char buf_idx = 0;
  14. static unsigned char buf_len = 0;
  15. //////////////////////////// 根据实际情况修改以下代码调用方式////////////////
  16. static void PID_send(unsigned char dat) {
  17. BT_send(dat);
  18. }
  19. ////////////////////////////////////////////////////////////////////////////
  20. //////////////////////////// 数据转换:不同平台可能存在大小端问题 /////////
  21. ////////////////////// 当前是大端,修改顺序即可改为小端 ///////////////////
  22. typedef union {
  23. float f;
  24. unsigned char b[4];
  25. } FloatBytes;
  26. static void floatToBytes(float value, unsigned char *bytes) {
  27. FloatBytes fb;
  28. fb.f = value;
  29. bytes[0] = fb.b[3];
  30. bytes[1] = fb.b[2];
  31. bytes[2] = fb.b[1];
  32. bytes[3] = fb.b[0];
  33. }
  34. static float bytesToFloat(unsigned char *bytes) {
  35. FloatBytes fb;
  36. fb.b[0] = bytes[3];
  37. fb.b[1] = bytes[2];
  38. fb.b[2] = bytes[1];
  39. fb.b[3] = bytes[0];
  40. return fb.f;
  41. }
  42. ///////////////////////////////////////////////////////////////
  43. void PID_init() {
  44. }
  45. void PID_push(float *chns, int chn_cnt) {
  46. //帧头 | 命令 | 数据长度 | 数据 | 校验码 | 帧尾
  47. //1字节 | 1字节 | 1字节 | n字节 | 1字节 | 1字节
  48. //命令: 请求类型的标识
  49. //数据长度: 表示后面 数据 的字节个数
  50. //校验码: 命令 + 数据长度 + 数据, 取高位
  51. unsigned char i;
  52. float chn;
  53. unsigned char tmp[4];
  54. unsigned short sum = 0;
  55. PID_send(TX_FRAME_HEAD);
  56. PID_send(TX_CMD_CHN);
  57. PID_send(chn_cnt * 4);
  58. sum += TX_CMD_CHN;
  59. sum += chn_cnt * 4;
  60. for(i = 0; i < chn_cnt; i++) {
  61. floatToBytes(chns[i], tmp);
  62. PID_send(tmp[0]);
  63. sum += tmp[0];
  64. PID_send(tmp[1]);
  65. sum += tmp[1];
  66. PID_send(tmp[2]);
  67. sum += tmp[2];
  68. PID_send(tmp[3]);
  69. sum += tmp[3];
  70. }
  71. PID_send((sum >> 8) & 0xFF);
  72. PID_send(TX_FRAME_END);
  73. }
  74. static void parse_config_pid() {
  75. float kp, ki, kd;
  76. kp = bytesToFloat(&buf[(buf_idx + 4) % MAX_BUF]);
  77. ki = bytesToFloat(&buf[(buf_idx + 8) % MAX_BUF]);
  78. kd = bytesToFloat(&buf[(buf_idx + 12) % MAX_BUF]);
  79. PID_on_config_pid(buf[buf[(buf_idx + 3) % MAX_BUF], kp, ki, kd);
  80. }
  81. //////////////////// 如果要去增加其他命令的解析,可以修改这个函数 ////////////
  82. static void on_cmd() {
  83. unsigned char cmd;
  84. cmd = buf[(buf_idx + 1) % MAX_BUF];
  85. if(cmd == RX_CMD_PID) {
  86. parse_config_pid();
  87. }
  88. }
  89. //////////////////////////////////////////////////////////////////////////////
  90. void PID_parse() {
  91. unsigned char dat_len, i;
  92. unsigned short sum = 0;
  93. //////////////// check cmd
  94. if(buf_len < 3) return;
  95. // 消息组成
  96. // 帧头 | 命令 | 数据长度 | 数据 | 校验码 | 帧尾
  97. // 1字节 | 1字节 | 1字节 | n字节 | 1字节 | 1字节
  98. // 命令: 请求类型的标识
  99. // 数据长度: 表示后面 数据 的字节个数
  100. // 校验码: 命令 + 数据长度 + 数据, 取高位
  101. // check frame head
  102. if(buf[buf_idx] != RX_FRAME_HEAD) {
  103. // drop one
  104. buf_idx++;
  105. buf_idx %= MAX_BUF;
  106. if(buf_len != 0) buf_len--;
  107. PID_parse();
  108. return;
  109. }
  110. // check dat len
  111. dat_len = buf[(buf_idx + 2) % MAX_BUF];
  112. // check cmd total len
  113. if(buf_len < dat_len + 5) return;
  114. // check sum
  115. sum += buf[(buf_idx + 1) % MAX_BUF];
  116. sum += buf[(buf_idx + 2) % MAX_BUF];
  117. for(i = 0; i < dat_len; i++) {
  118. sum += buf[(buf_idx + 3 + i) % MAX_BUF];
  119. }
  120. if(((sum >> 8) & 0xFF) != buf[(buf_idx + dat_len + 3) % MAX_BUF]) {
  121. // drop
  122. buf_idx++;
  123. buf_idx %= MAX_BUF;
  124. if(buf_len != 0) buf_len--;
  125. PID_parse();
  126. return;
  127. }
  128. // check frame end
  129. if(buf[(buf_idx + dat_len + 4) % MAX_BUF] != RX_FRAME_END) {
  130. // drop
  131. buf_idx++;
  132. buf_idx %= MAX_BUF;
  133. if(buf_len != 0) buf_len--;
  134. PID_parse();
  135. return;
  136. }
  137. // parse success
  138. /**
  139. if(buf[(buf_idx + 1) % MAX_BUF] == RX_CMD_PID) {
  140. // config pid
  141. parse_config_pid();
  142. }
  143. **/
  144. on_cmd();
  145. // drop
  146. buf_idx += dat_len + 5;
  147. buf_idx %= MAX_BUF;
  148. if(buf_len >= dat_len + 5) {
  149. buf_len -= dat_len + 5;
  150. } else {
  151. buf_len = 0;
  152. }
  153. PID_parse();
  154. }
  155. void PID_pull(unsigned char dat) {
  156. unsigned char idx = buf_idx + buf_len;
  157. idx %= MAX_BUF;
  158. // config dat
  159. buf[idx] = dat;
  160. buf_len ++;
  161. if(buf_len > MAX_BUF) {
  162. buf_idx = 0;
  163. buf_len = 0;
  164. }
  165. }

使用步骤

  1. 拷贝都文件和c文件到项目种
  2. 修改c文件中PID__send函数实现

    1. #include "Bluetooth.h"
    2. ....
    3. static void PID_send(unsigned char dat) {
    4. BT_send(dat);
    5. }

    修改为自己的实现,以上是蓝牙的实现,发送的是单个byte

  3. 调用PID_init()初始化

  4. 在需要的地方调用PID_push

    1. void PID_push(float *chns, int chn_cnt);
    • 第一个参数是float数组
    • 第二个是数组长度
    • 一个元素对应一个GUI显示通道

      高级配置

命令 方向 说明 示例
0x00 接收 速度 0x7a 0x00 11 2 2 2 1 0x7b
0x01 接收 直立环PID 0x7a 0x01 0x00 18 4 4 4 1 0x7b
0x01 接收 速度环PID 0x7a 0x01 0x01 18 4 4 4 1 0x7b
0x01 接收 转向换 0x7a 0x01 0x02 18 4 4 4 1 0x7b

协议文档

  1. /*
  2. 接收的数据:
  3. 帧头 标志位 长度 X速度 Y速度 Z速度 校验位 帧尾
  4. 0x7a 0x00 11 2 2 2 1 0x7b 11位
  5. 帧头 标志位 长度 类别 kp ki kd 校验位1 帧尾 直立环
  6. 0x7a 0x01 18 0x00 4 4 4 1 0x7b
  7. 帧头 标志位 长度 类别 kp ki kd 校验位1 帧尾 速度环
  8. 0x7a 0x01 18 0x01 4 4 4 1 0x7b
  9. 帧头 标志位 长度 类别 kp ki kd 校验位1 帧尾 转向换
  10. 0x7a 0x01 18 0x02 4 4 4 1 0x7b
  11. 浮点数据,放大100倍,转为整数发送
  12. 0x7a 0x00 0x00 0x11 0x11 0x11 0x11 0x12 0x13 0x12 0x7b
  13. 发出去的数据:
  14. 帧头 停止位 X速度 Y速度 Z速度 X加速度 Y加速度 Z加速度 X角速度 Y角速度 Z角速度 电压 校验位 帧尾
  15. 0x7b 1 2 2 2 2 2 2 2 2 2 2 1 0x7a
  16. short 是2个字节,范围-32768~32767
  17. */