匿名四轴作为一个玩四轴的人必备入门级工具,匿名地面站简直好用的一批,这里有匿名科创对他们的地面站的介绍和下载方法,匿名视频。可谓功能相当强大,使用起来及其方便,大家如果想获得资源可以直接从网上寻找下载,本文主要对匿名地面站4.50版本进行协议解析,让大家理解相关的通讯协议,便于使用先进的资源进行小四轴的调试使用。
所谓的通讯协议,说白了就是一个暗号,你要和地面站进行通讯,就要按照地面站制定的规则进行通讯,首先要先向地面站汇报统一暗号(0xaaa)然后是你来的任务(编码位)有多少东西(数据位长度),你带来的东西(想要传输的数据),最终确认(校验值)。大家只需要按照这个已经约定好的暗号和地面站通讯,他就能理解我们带来了什么,同样地面站也会以另一种格式传输给我们相关的指令但是基本类似,只需要按照约定好的内容对传来的指令进行解析就能得到相应的数据。
/////////////////////////////////////////////////////////////////////////////////////
//Data_Exchange函数处理各种数据发送请求,比如想实现每5ms发送一次传感器数据至上位机,即在此函数内实现
void ANO_DT_Data_Exchange(void)
{
static u8 cnt = 0;
static u8 senser_cnt = 10;
static u8 status_cnt = 15;
static u8 rcdata_cnt = 20;
static u8 motopwm_cnt = 20;
static u8 power_cnt = 50;
if((cnt % senser_cnt) == (senser_cnt-1))
f.send_senser = 1;
if((cnt % status_cnt) == (status_cnt-1))
f.send_status = 1;
if((cnt % rcdata_cnt) == (rcdata_cnt-1))
f.send_rcdata = 1;
if((cnt % motopwm_cnt) == (motopwm_cnt-1))
f.send_motopwm = 1;
if((cnt % power_cnt) == (power_cnt-1))
f.send_power = 1;
cnt++;
/////////////////////////////////////////////////////////////////////////////////////
if(f.send_version)
{
f.send_version = 0;
ANO_DT_Send_Version(4,300,100,400,0);
}
/////////////////////////////////////////////////////////////////////////////////////
else if(f.send_status)
{
f.send_status = 0;
ANO_DT_Send_Status(Roll,Pitch,Yaw,baroAlt,0,fly_ready);
}
/////////////////////////////////////////////////////////////////////////////////////
else if(f.send_senser)
{
f.send_senser = 0;
ANO_DT_Send_Senser(mpu6050.Acc.x,mpu6050.Acc.y,mpu6050.Acc.z, mpu6050.Gyro.x,mpu6050.Gyro.y,mpu6050.Gyro.z,ak8975.Mag_Adc.x,ak8975.Mag_Adc.y,ak8975.Mag_Adc.z,0);
}
/////////////////////////////////////////////////////////////////////////////////////
else if(f.send_rcdata)
{
f.send_rcdata = 0;
ANO_DT_Send_RCData(Rc_Pwm_In[0],Rc_Pwm_In[1],Rc_Pwm_In[2],Rc_Pwm_In[3],Rc_Pwm_In[4],Rc_Pwm_In[5],Rc_Pwm_In[6],Rc_Pwm_In[7],0,0);
}
/////////////////////////////////////////////////////////////////////////////////////
else if(f.send_motopwm)
{
f.send_motopwm = 0;
ANO_DT_Send_MotoPWM(1,2,3,4,5,6,7,8);
}
/////////////////////////////////////////////////////////////////////////////////////
else if(f.send_power)
{
f.send_power = 0;
ANO_DT_Send_Power(123,456);
}
/////////////////////////////////////////////////////////////////////////////////////
else if(f.send_pid1)
{
f.send_pid1 = 0;
ANO_DT_Send_PID(1,ctrl_1.PID[PIDROLL].kp,ctrl_1.PID[PIDROLL].ki,ctrl_1.PID[PIDROLL].kd,ctrl_1.PID[PIDPITCH].kp,ctrl_1.PID[PIDPITCH].ki,ctrl_1.PID[PIDPITCH].kd,ctrl_1.PID[PIDYAW].kp,ctrl_1.PID[PIDYAW].ki,ctrl_1.PID[PIDYAW].kd);
}
/////////////////////////////////////////////////////////////////////////////////////
else if(f.send_pid2)
{
f.send_pid2 = 0;
ANO_DT_Send_PID(2,ctrl_1.PID[PID4].kp,ctrl_1.PID[PID4].ki,ctrl_1.PID[PID4].kd,ctrl_1.PID[PID5].kp,ctrl_1.PID[PID5].ki,ctrl_1.PID[PID5].kd, ctrl_1.PID[PID6].kp,ctrl_1.PID[PID6].ki,ctrl_1.PID[PID6].kd);
}
/////////////////////////////////////////////////////////////////////////////////////
else if(f.send_pid3)
{
f.send_pid3 = 0;
ANO_DT_Send_PID(3,ctrl_2.PID[PIDROLL].kp,ctrl_2.PID[PIDROLL].ki,ctrl_2.PID[PIDROLL].kd,ctrl_2.PID[PIDPITCH].kp,ctrl_2.PID[PIDPITCH].ki,ctrl_2.PID[PIDPITCH].kd,ctrl_2.PID[PIDYAW].kp,ctrl_2.PID[PIDYAW].ki,ctrl_2.PID[PIDYAW].kd);
}
/////////////////////////////////////////////////////////////////////////////////////
}
这个函数执行的功能其实就是判断一下我多久该发一次数据,有些数据比较重要(比如姿态信息)需要经常传输一下,有些相对而言更新慢一点没有太大问题(比如电压),而有一些只需要等待接收到发送指令以后再发送就OK(比如PID信息),然后得到相应的命令以后将数据按照约定的格式传输给上位机。
void ANO_DT_Send_Power(u16 votage, u16 current)
{
u8 _cnt=0;
u16 temp;
data_to_send[_cnt++]=0xAA;
data_to_send[_cnt++]=0xAA;
data_to_send[_cnt++]=0x05;
data_to_send[_cnt++]=0;
temp = votage;
data_to_send[_cnt++]=BYTE1(temp);
data_to_send[_cnt++]=BYTE0(temp);
temp = current;
data_to_send[_cnt++]=BYTE1(temp);
data_to_send[_cnt++]=BYTE0(temp);
data_to_send[3] = _cnt-4;
u8 sum = 0;
for(u8 i=0;i<_cnt;i++)
sum += data_to_send[i];
data_to_send[_cnt++]=sum;
ANO_DT_Send_Data(data_to_send, _cnt);
}
这个是传输电压和电源的传输协议,协议内容也是按照前面已经规定的格式进行传输的,发送帧头,功能码,数据内容,校验值等相关的内容。函数任务就是将这些信息都整合到一个数组里面,然后将相关的内容通过使用的数据传输方式进行数据传输。传输其他的内容乃至高级数据都是通过类似的方式完成的。在此不多做介绍。
void ANO_DT_Send_Data(u8 *dataToSend , u8 length)
{
#ifdef ANO_DT_USE_USB_HID
Usb_Hid_Adddata(data_to_send,length);
#endif
#ifdef ANO_DT_USE_USART2
Usart2_Send(data_to_send, length);
#endif
}
这个其实就是一个传输函数,前面两个函数已经确定了数据格式,这里只需要根据相关的东西进行数据传输,传输格式可以根据自己方便进行选择,比如USB,或者串口模块等等。
void ANO_DT_Data_Receive_Prepare(u8 data)
{
static u8 RxBuffer[50];
static u8 _data_len = 0,_data_cnt = 0;
static u8 state = 0;
if(state==0&&data==0xAA)
{
state=1;
RxBuffer[0]=data;
}
else if(state==1&&data==0xAF)
{
state=2;
RxBuffer[1]=data;
}
else if(state==2&&data<0XF1)
{
state=3;
RxBuffer[2]=data;
}
else if(state==3&&data<50)
{
state = 4;
RxBuffer[3]=data;
_data_len = data;
_data_cnt = 0;
}
else if(state==4&&_data_len>0)
{
_data_len--;
RxBuffer[4+_data_cnt++]=data;
if(_data_len==0)
state = 5;
}
else if(state==5)
{
state = 0;
RxBuffer[4+_data_cnt]=data;
ANO_DT_Data_Receive_Anl(RxBuffer,_data_cnt+5);
}
else
state = 0;
}
首先将接收到的数据进行解析,解析主要是一位一位进行解析,层层递进,将解析得到的数据传过来,和相应的协议进行对比,如果正确就将数据传输到下一个模块
void ANO_DT_Data_Receive_Anl(u8 *data_buf,u8 num)
{
u8 sum = 0;
for(u8 i=0;i<(num-1);i++)
sum += *(data_buf+i);
if(!(sum==*(data_buf+num-1))) return; //判断sum
if(!(*(data_buf)==0xAA && *(data_buf+1)==0xAF)) return; //判断帧头
if(*(data_buf+2)==0X01)
{
if(*(data_buf+4)==0X01)
mpu6050.Acc_CALIBRATE = 1;
if(*(data_buf+4)==0X02)
mpu6050.Gyro_CALIBRATE = 1;
if(*(data_buf+4)==0X03)
{
mpu6050.Acc_CALIBRATE = 1;
mpu6050.Gyro_CALIBRATE = 1;
}
}
if(*(data_buf+2)==0X02)
{
if(*(data_buf+4)==0X01)
{
f.send_pid1 = 1;
f.send_pid2 = 1;
f.send_pid3 = 1;
f.send_pid4 = 1;
f.send_pid5 = 1;
f.send_pid6 = 1;
}
if(*(data_buf+4)==0X02)
{
}
if(*(data_buf+4)==0XA0) //读取版本信息
{
f.send_version = 1;
}
if(*(data_buf+4)==0XA1) //恢复默认参数
{
Para_ResetToFactorySetup();
}
}
if(*(data_buf+2)==0X10) //PID1
{
ctrl_1.PID[PIDROLL].kp = 0.001*( (vs16)(*(data_buf+4)<<8)|*(data_buf+5) );
ctrl_1.PID[PIDROLL].ki = 0.001*( (vs16)(*(data_buf+6)<<8)|*(data_buf+7) );
ctrl_1.PID[PIDROLL].kd = 0.001*( (vs16)(*(data_buf+8)<<8)|*(data_buf+9) );
ctrl_1.PID[PIDPITCH].kp = 0.001*( (vs16)(*(data_buf+10)<<8)|*(data_buf+11) );
ctrl_1.PID[PIDPITCH].ki = 0.001*( (vs16)(*(data_buf+12)<<8)|*(data_buf+13) );
ctrl_1.PID[PIDPITCH].kd = 0.001*( (vs16)(*(data_buf+14)<<8)|*(data_buf+15) );
ctrl_1.PID[PIDYAW].kp = 0.001*( (vs16)(*(data_buf+16)<<8)|*(data_buf+17) );
ctrl_1.PID[PIDYAW].ki = 0.001*( (vs16)(*(data_buf+18)<<8)|*(data_buf+19) );
ctrl_1.PID[PIDYAW].kd = 0.001*( (vs16)(*(data_buf+20)<<8)|*(data_buf+21) );
ANO_DT_Send_Check(*(data_buf+2),sum);
Param_SavePID();
}
if(*(data_buf+2)==0X11) //PID2
{
ctrl_1.PID[PID4].kp = 0.001*( (vs16)(*(data_buf+4)<<8)|*(data_buf+5) );
ctrl_1.PID[PID4].ki = 0.001*( (vs16)(*(data_buf+6)<<8)|*(data_buf+7) );
ctrl_1.PID[PID4].kd = 0.001*( (vs16)(*(data_buf+8)<<8)|*(data_buf+9) );
ctrl_1.PID[PID5].kp = 0.001*( (vs16)(*(data_buf+10)<<8)|*(data_buf+11) );
ctrl_1.PID[PID5].ki = 0.001*( (vs16)(*(data_buf+12)<<8)|*(data_buf+13) );
ctrl_1.PID[PID5].kd = 0.001*( (vs16)(*(data_buf+14)<<8)|*(data_buf+15) );
ctrl_1.PID[PID6].kp = 0.001*( (vs16)(*(data_buf+16)<<8)|*(data_buf+17) );
ctrl_1.PID[PID6].ki = 0.001*( (vs16)(*(data_buf+18)<<8)|*(data_buf+19) );
ctrl_1.PID[PID6].kd = 0.001*( (vs16)(*(data_buf+20)<<8)|*(data_buf+21) );
ANO_DT_Send_Check(*(data_buf+2),sum);
Param_SavePID();
}
if(*(data_buf+2)==0X12) //PID3
{
ctrl_2.PID[PIDROLL].kp = 0.001*( (vs16)(*(data_buf+4)<<8)|*(data_buf+5) );
ctrl_2.PID[PIDROLL].ki = 0.001*( (vs16)(*(data_buf+6)<<8)|*(data_buf+7) );
ctrl_2.PID[PIDROLL].kd = 0.001*( (vs16)(*(data_buf+8)<<8)|*(data_buf+9) );
ctrl_2.PID[PIDPITCH].kp = 0.001*( (vs16)(*(data_buf+10)<<8)|*(data_buf+11) );
ctrl_2.PID[PIDPITCH].ki = 0.001*( (vs16)(*(data_buf+12)<<8)|*(data_buf+13) );
ctrl_2.PID[PIDPITCH].kd = 0.001*( (vs16)(*(data_buf+14)<<8)|*(data_buf+15) );
ctrl_2.PID[PIDYAW].kp = 0.001*( (vs16)(*(data_buf+16)<<8)|*(data_buf+17) );
ctrl_2.PID[PIDYAW].ki = 0.001*( (vs16)(*(data_buf+18)<<8)|*(data_buf+19) );
ctrl_2.PID[PIDYAW].kd = 0.001*( (vs16)(*(data_buf+20)<<8)|*(data_buf+21) );
ANO_DT_Send_Check(*(data_buf+2),sum);
Param_SavePID();
}
if(*(data_buf+2)==0X13) //PID4
{
ANO_DT_Send_Check(*(data_buf+2),sum);
}
if(*(data_buf+2)==0X14) //PID5
{
ANO_DT_Send_Check(*(data_buf+2),sum);
}
if(*(data_buf+2)==0X15) //PID6
{
ANO_DT_Send_Check(*(data_buf+2),sum);
}
}
本模块代码虽多,但执行的功能相对比较简单,主要是将传过来的八位数据转换成对应格式的数据然后更新到相对应的变量里面。