一些变量我没写出来,找来找去太麻烦了,后期可以看我丢出来的工程文件对照;也可以自己写一下。

上面我们做好了NF03模块发送接收数据的代码
然后要发送接收什么样的数据,就从这边来控制

image-1678886214034
image-1678886167644

这两个协议我是抄的小马哥的,基本没改
解释下无人机的

是否解锁(我这没用)
一个八位的数据 最低位是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);

image-1678939592153
首先自己定义一个浮点型(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值取出,覆盖之前的值,以表示信号链接正常
}

遥控发来的数据就放到我们刚接收完那块,外部中断函数里面(我们之前不是空着那一行吗)
image-1679211219716

遥控器的发送接收函数

这是遥控器的,我在这放一下
发送

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值取出,覆盖之前的值,以表示信号链接正常
}