一些变量我没写出来,找来找去太麻烦了,后期可以看我丢出来的工程文件对照;也可以自己写一下。
上面我们做好了NF03模块发送接收数据的代码
然后要发送接收什么样的数据,就从这边来控制
这两个协议我是抄的小马哥的,基本没改
解释下无人机的
是否解锁(我这没用)
一个八位的数据 最低位是0就锁定(0000 0000),1就解锁(0000 0001)
。。好像其他也没啥能说的算了不说了
注意下spi发送的数据都是8位8位的往外发,所以16位数据拆成2个
还有我们发送的固定长度是32字节,不过也不用太注意,创建的数组是32个字节的就行(uint8_t a[32])
我们发的是uint8_t,但是像航向角之类的float,要变成uint16,再拆成uint8再发
手柄的数据包标识,是每一次发送,都加一,让两次标识不同,如果收到相同的,就说明连接有问题了。
代码
有两个协议,我们要写四个函数,发送的和接收的
不过两个再四轴上两个在遥控上;这里我们先只写四轴的
四轴:【遥控发给四轴的数据的解析】【四轴给遥控的数据的准备】
遥控:【四轴发给遥控的数据的解析】【遥控给四轴的数据的准备】
一些数据格式上的操作
发送的数据是uint8_t,但我们有的是float
接收到的数据是uint8_t,但我们需要的是float
那么就要变化数据
发送:float->int->uint8_t
接收:uint8_t->int->float
我们知道,浮点float变整型int会把小数搞丢(3.321123变成3),然后再int变float小数也回不来(3变成3.000000)
但我们可以这样,让浮点的小数部分变到整数部分,比如乘100;那么float变int的变化就是(3321.123000变成3321),然后再int变float(3321变成3321.000000),最后再除以100,浮点的小数部分就回来一点(3321.000000 / 100=3.321000)
数据总会丢失一点(3.321012变成3.321),但不太碍事
【测试】关于数据转换的测试
我们有很多float数据要变成uint8_t发出去,然后在另一个设备上再从uint8_t转换回float
但是应该很多人还不懂怎么变
打印二进制数据的代码
首先我们先准备一个可以看一个数字的二进制是什么样的代码(比如输入1,输出0000 0001)
下面两个代码我是在这里复制来的这里
pbin1可以看16位的二进制,(1111 1111 1111 1111)
void pbin1(int i)
{
int j;
int mask = 32768;// 1000 0000 0000 0000
fprintf(stdout, "二进制%d:\t", i);
for(j = 0; j < 16; j++)
{
fprintf(stdout, "%c", ((i & (mask >> j)) >> (15 - j)) + '0');
switch(j){
case 3:
case 7:
case 11:
fprintf(stdout, " ");
break;
}
}
fprintf(stdout, "\n");
}
pbin2可以看8位的二进制 (1111 1111)
void pbin2(unsigned char dat)
{
int8_t index;
printf("二进制%d:\t", dat);
for(index = 7; index >= 0; index--)
{
printf("%d", (dat >> index) & 1);
}
printf("\n");
}
原理的话,是把每一位都拉到最低位,与1相与(&1),看看这一位是1是0;
以pbin2来举例,比如我们发送 1001 0011,先把最高位的1右移7位到最低位,和0000 0001相与,把结果1打印出来;在把次高位的0右移6位到最低位,和0000 0001想与,打印结果0;循环全部打印出来。
测试
有了能看二进制的代码,我们就可以看一看数据的操作了
float p=5.2461*100;
int x=p,y;
uint8_t z,e,f;
printf("浮点:%f\r\n",p);
printf("int:%d\r\n",x);
pbin1(x);
e=(*((uint8_t *)(&x)));
f=(*(((uint8_t *)(&x))+1));
printf("uint8:%d,%d\r\n",e,f);
pbin2(e);
pbin2(f);
y=(((int16_t)f)<<8)|(int16_t)e;
printf("变回int:%d\r\n",y);
printf("变回浮点:%f\r\n",(float)y/100);
首先自己定义一个浮点型(4个字节)。为什么浮点后面要乘100我们在上面说了
不过为什么不让精度更高一点呢,因为如果我们浮点是300,那么乘1000变成3000000,就超出16位了。
然后浮点变成int(2字节16位)。
把int的16位拆成两个8位;【打比方,int a变成uint8_t a[2],把一个int变成一个长度为2的uint8_t数组】
e=(*((uint8_t )(&x)));
f=((((uint8_t *)(&x))+1));【看不懂这句的往下看,最下面我有解释】
对应上面的图片,我们能看到,数组的低位(a[0]),是之前int的低8位;数组的高位(a[1]),是之前int的高8位
再用uint8_t变回int;让数组的高位左移8位,低位保持,或一下就行(做加法也没问题)
int再变回float,让float再除100变回之前的数字。
【有人会有疑问:为什么要中间隔一个int,直接让float变uint8_t,而且变回来直接float进行各种移位操作不行吗,,,,,我不太懂,我试了,办不了,另外float是不能位移操作的】【可能是uint8_t;uint32_t之类的强转float也不是只是显示不一样,在各个位上也有变回吧】
(*(((uint8_t )(&x))+1))这句我知道有萌新看不懂,我建议重学c语言但我知道大家不会重学的。我们一句一句分析
首先我们知道x是int类型
&x是拿到x的地址——就像是一个数组a[3],我们只写a一样;有的时候我们函数传参也写&b,然后进参数里面想拿到b里的数据,就会写b
((uint8_t *)(&x))这是通过x的地址把x变成数组类型,比如本来是int x,变成uint8_t x[2];
((uint8_t *)(&x))+1是获取x这个地址的下一个地址——就像是数组a[3],我们写了a[1]一样,是数组a[3]里的第二个数据的地址
*(((uint8_t *)(&x))+1)这是把这个地址里面的数据读出来
四轴的数据
int->uint8_t的宏
之前测试那边,我们能看到int转uint8_t的时候特别麻烦
所以我们用宏来简化他
#define Byte0(data) ( *( (uint8_t *)(&data) ) )
#define Byte1(data) ( *( (uint8_t *)(&data) + 1) )
#define Byte2(data) ( *( (uint8_t *)(&data) + 2) )
#define Byte3(data) ( *( (uint8_t *)(&data) + 3) )
上面我们测试过,但这么些的话,是高位在前面,低位在后面(很怪但测试出来是这样)所以下面我们接收数据的话是高位右移,低位不动。
四轴给遥控的数据的准备
void SendToRemote(void)
{
int16_t temp;
NF_TX_DATA[0] = 0xFF;//帧头
NF_TX_DATA[1] = 0x01; //是否解锁;这里没有过锁住的代码,直接随便写了
temp = (uint16_t)thr; //油门
NF_TX_DATA[2] = Byte1(temp);
NF_TX_DATA[3] = Byte0(temp);
temp = (int)(yaw*100); //航向
NF_TX_DATA[4] = Byte1(temp);
NF_TX_DATA[5] = Byte0(temp);
temp = (int)(pitch*100); //俯仰
NF_TX_DATA[6] = Byte1(temp);
NF_TX_DATA[7] = Byte0(temp);
temp = (int)(roll*100); //横滚
NF_TX_DATA[8] = Byte1(temp);
NF_TX_DATA[9] = Byte0(temp);
temp = (int)(high*100); //高度留待
NF_TX_DATA[10] = Byte1(temp);
NF_TX_DATA[11] = Byte0(temp);
temp = (int)(vbat*100); //飞机电池电压
NF_TX_DATA[12] = Byte1(temp);
NF_TX_DATA[13] = Byte0(temp);
NF_TX_DATA[14] = 0xA5;//帧尾
nRF24L01P_TxPacket(NF_TX_DATA); //SI24R1发送函数
}
没啥说的,对着上面协议表格一个个的写就行;注意一下在函数里面定义的数据,用extern是找不到的,之前我们写的变量,记得挪到函数外面定义。
像thr,high,vbat,这个还没写过,就随便搞吧
遥控发给四轴的数据的解析
void ReadFromRemote(void)
{
//SI24R1_Controlflag = 1;
if(NF_RX_DATA[11]!=0xa5) //验证校验码是否为0xa5
{
return;
}
if(NF_RX_DATA[0] & 0x01) //当数据包是由遥控器的ADC采样完成时触发发送时
{
yk_yaw = (int16_t)NF_RX_DATA[2]<<8|(int16_t)NF_RX_DATA[3]; //ADC2
yk_thr = (int16_t)NF_RX_DATA[4]<<8|(int16_t)NF_RX_DATA[5]; //ADC1
yk_roll = (int16_t)NF_RX_DATA[6]<<8|(int16_t)NF_RX_DATA[7]; //ADC4
yk_pitch = (int16_t)NF_RX_DATA[8]<<8|(int16_t)NF_RX_DATA[9]; //ADC3
//printf("遥控发来的数据:yk_yaw:%d,yk_thr:%d,yk_roll:%d,yk_pitch:%d",yk_yaw,yk_thr,yk_roll,yk_pitch);
}
// else if(NF_RX_DATA[0] & 0x08) //当数据包是由遥控器的按键触发发送时
// {
// Button_command(NF_RX_DATA[1]); //ButtonMask按键命令解析
// }
DataID = NF_RX_DATA[10];//将数据包识别PID值取出,覆盖之前的值,以表示信号链接正常
}
遥控发来的数据就放到我们刚接收完那块,外部中断函数里面(我们之前不是空着那一行吗)
遥控器的发送接收函数
这是遥控器的,我在这放一下
发送
void SendToAxis(void)
{
int16_t temp;
NF_TX_DATA[0] = 0x01;//帧头
NF_TX_DATA[1] = 0x00; //按键没用到,随便写一个
temp = ADC_Value[0]; //油门
NF_TX_DATA[2] = Byte1(temp);
NF_TX_DATA[3] = Byte0(temp);
temp = ADC_Value[1]; //航向
NF_TX_DATA[4] = Byte1(temp);
NF_TX_DATA[5] = Byte0(temp);
temp = ADC_Value[2]; //俯仰
NF_TX_DATA[6] = Byte1(temp);
NF_TX_DATA[7] = Byte0(temp);
temp = ADC_Value[3]; //横滚
NF_TX_DATA[8] = Byte1(temp);
NF_TX_DATA[9] = Byte0(temp);
if(DataID>=200)DataID=0;
DataID++;
NF_TX_DATA[11] = 0xA5;//帧尾
NF03_TxPacket(NF_TX_DATA); //SI24R1发送函数
}
接收
void ReadFromAxis(void)
{
//SI24R1_Controlflag = 1;
if(NF_RX_DATA[14]!=0xa5) //验证校验码是否为0xa5
{
return;
}
if(NF_RX_DATA[0] & 0xFF) //当数据包是无人机发来的0xFF开头的
{
axis_yaw = (uint16_t)NF_RX_DATA[4]<<8|(uint16_t)NF_RX_DATA[5]; //ADC2
yaw = (float)axis_yaw/100;
axis_thr = (uint16_t)NF_RX_DATA[2]<<8|(uint16_t)NF_RX_DATA[3]; //ADC1
thr = (float)axis_thr;
axis_roll = (uint16_t)NF_RX_DATA[8]<<8|(uint16_t)NF_RX_DATA[9]; //ADC4
roll = (float)axis_roll/100;
axis_pitch = (uint16_t)NF_RX_DATA[6]<<8|(uint16_t)NF_RX_DATA[7]; //ADC3
pitch = (float)axis_pitch/100;
axis_high =(uint16_t)NF_RX_DATA[11]<<8|(uint16_t)NF_RX_DATA[10];
high = (float)axis_high/100;
axis_vbat = (uint16_t)NF_RX_DATA[12]<<8|(uint16_t)NF_RX_DATA[13];
vbat = (float)axis_vbat/100;
printf("thr:%f\r\nyaw:%f\r\nroll:%f\r\npitch:%f\r\nhigh:%f\r\nvbat:%f\r\n",thr,yaw,roll,pitch,high,vbat);
}
RX_ID = NF_RX_DATA[10];//将数据包识别PID值取出,覆盖之前的值,以表示信号链接正常
}