协议规范
端到端
分为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轴速度
库使用说明
库文件
#ifndef __PID_H__
#define __PID_H__
void PID_init();
void PID_push(float *chns, int chn_cnt);
void PID_pull(unsigned char dat);
void PID_parse();
extern void PID_on_config_pid(unsigned char id, float kp, float ki, float kd);
#endif
#include "PID.h"
#include "Bluetooth.h"
//////////// PUSH ///////////////
#define TX_FRAME_HEAD 0x7a
#define TX_FRAME_END 0x7b
#define TX_CMD_CHN 0x01
//////////// PULL //////////////
#define RX_FRAME_HEAD 0x7b
#define RX_FRAME_END 0x7a
#define RX_CMD_PID 0x01
#define MAX_BUF 64
static unsigned char buf[MAX_BUF];
static unsigned char buf_idx = 0;
static unsigned char buf_len = 0;
//////////////////////////// 根据实际情况修改以下代码调用方式////////////////
static void PID_send(unsigned char dat) {
BT_send(dat);
}
////////////////////////////////////////////////////////////////////////////
//////////////////////////// 数据转换:不同平台可能存在大小端问题 /////////
////////////////////// 当前是大端,修改顺序即可改为小端 ///////////////////
typedef union {
float f;
unsigned char b[4];
} FloatBytes;
static void floatToBytes(float value, unsigned char *bytes) {
FloatBytes fb;
fb.f = value;
bytes[0] = fb.b[3];
bytes[1] = fb.b[2];
bytes[2] = fb.b[1];
bytes[3] = fb.b[0];
}
static float bytesToFloat(unsigned char *bytes) {
FloatBytes fb;
fb.b[0] = bytes[3];
fb.b[1] = bytes[2];
fb.b[2] = bytes[1];
fb.b[3] = bytes[0];
return fb.f;
}
///////////////////////////////////////////////////////////////
void PID_init() {
}
void PID_push(float *chns, int chn_cnt) {
//帧头 | 命令 | 数据长度 | 数据 | 校验码 | 帧尾
//1字节 | 1字节 | 1字节 | n字节 | 1字节 | 1字节
//命令: 请求类型的标识
//数据长度: 表示后面 数据 的字节个数
//校验码: 命令 + 数据长度 + 数据, 取高位
unsigned char i;
float chn;
unsigned char tmp[4];
unsigned short sum = 0;
PID_send(TX_FRAME_HEAD);
PID_send(TX_CMD_CHN);
PID_send(chn_cnt * 4);
sum += TX_CMD_CHN;
sum += chn_cnt * 4;
for(i = 0; i < chn_cnt; i++) {
floatToBytes(chns[i], tmp);
PID_send(tmp[0]);
sum += tmp[0];
PID_send(tmp[1]);
sum += tmp[1];
PID_send(tmp[2]);
sum += tmp[2];
PID_send(tmp[3]);
sum += tmp[3];
}
PID_send((sum >> 8) & 0xFF);
PID_send(TX_FRAME_END);
}
static void parse_config_pid() {
float kp, ki, kd;
kp = bytesToFloat(&buf[(buf_idx + 4) % MAX_BUF]);
ki = bytesToFloat(&buf[(buf_idx + 8) % MAX_BUF]);
kd = bytesToFloat(&buf[(buf_idx + 12) % MAX_BUF]);
PID_on_config_pid(buf[buf[(buf_idx + 3) % MAX_BUF], kp, ki, kd);
}
//////////////////// 如果要去增加其他命令的解析,可以修改这个函数 ////////////
static void on_cmd() {
unsigned char cmd;
cmd = buf[(buf_idx + 1) % MAX_BUF];
if(cmd == RX_CMD_PID) {
parse_config_pid();
}
}
//////////////////////////////////////////////////////////////////////////////
void PID_parse() {
unsigned char dat_len, i;
unsigned short sum = 0;
//////////////// check cmd
if(buf_len < 3) return;
// 消息组成
// 帧头 | 命令 | 数据长度 | 数据 | 校验码 | 帧尾
// 1字节 | 1字节 | 1字节 | n字节 | 1字节 | 1字节
// 命令: 请求类型的标识
// 数据长度: 表示后面 数据 的字节个数
// 校验码: 命令 + 数据长度 + 数据, 取高位
// check frame head
if(buf[buf_idx] != RX_FRAME_HEAD) {
// drop one
buf_idx++;
buf_idx %= MAX_BUF;
if(buf_len != 0) buf_len--;
PID_parse();
return;
}
// check dat len
dat_len = buf[(buf_idx + 2) % MAX_BUF];
// check cmd total len
if(buf_len < dat_len + 5) return;
// check sum
sum += buf[(buf_idx + 1) % MAX_BUF];
sum += buf[(buf_idx + 2) % MAX_BUF];
for(i = 0; i < dat_len; i++) {
sum += buf[(buf_idx + 3 + i) % MAX_BUF];
}
if(((sum >> 8) & 0xFF) != buf[(buf_idx + dat_len + 3) % MAX_BUF]) {
// drop
buf_idx++;
buf_idx %= MAX_BUF;
if(buf_len != 0) buf_len--;
PID_parse();
return;
}
// check frame end
if(buf[(buf_idx + dat_len + 4) % MAX_BUF] != RX_FRAME_END) {
// drop
buf_idx++;
buf_idx %= MAX_BUF;
if(buf_len != 0) buf_len--;
PID_parse();
return;
}
// parse success
/**
if(buf[(buf_idx + 1) % MAX_BUF] == RX_CMD_PID) {
// config pid
parse_config_pid();
}
**/
on_cmd();
// drop
buf_idx += dat_len + 5;
buf_idx %= MAX_BUF;
if(buf_len >= dat_len + 5) {
buf_len -= dat_len + 5;
} else {
buf_len = 0;
}
PID_parse();
}
void PID_pull(unsigned char dat) {
unsigned char idx = buf_idx + buf_len;
idx %= MAX_BUF;
// config dat
buf[idx] = dat;
buf_len ++;
if(buf_len > MAX_BUF) {
buf_idx = 0;
buf_len = 0;
}
}
使用步骤
- 拷贝都文件和c文件到项目种
修改c文件中PID__send函数实现
#include "Bluetooth.h"
....
static void PID_send(unsigned char dat) {
BT_send(dat);
}
修改为自己的实现,以上是蓝牙的实现,发送的是单个byte
调用
PID_init()
初始化在需要的地方调用
PID_push
void PID_push(float *chns, int chn_cnt);
命令 | 方向 | 说明 | 示例 |
---|---|---|---|
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 |
协议文档
/*
接收的数据:
帧头 标志位 长度 X速度 Y速度 Z速度 校验位 帧尾
0x7a 0x00 11 2 2 2 1 0x7b 11位
帧头 标志位 长度 类别 kp ki kd 校验位1 帧尾 直立环
0x7a 0x01 18 0x00 4 4 4 1 0x7b
帧头 标志位 长度 类别 kp ki kd 校验位1 帧尾 速度环
0x7a 0x01 18 0x01 4 4 4 1 0x7b
帧头 标志位 长度 类别 kp ki kd 校验位1 帧尾 转向换
0x7a 0x01 18 0x02 4 4 4 1 0x7b
浮点数据,放大100倍,转为整数发送
0x7a 0x00 0x00 0x11 0x11 0x11 0x11 0x12 0x13 0x12 0x7b
发出去的数据:
帧头 停止位 X速度 Y速度 Z速度 X加速度 Y加速度 Z加速度 X角速度 Y角速度 Z角速度 电压 校验位 帧尾
0x7b 1 2 2 2 2 2 2 2 2 2 2 1 0x7a
short 是2个字节,范围-32768~32767
*/