完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
NRF24无线通信最多可以发32个字节,因为它是无线的所以感觉很便捷。无线传参传输完rol、pit、yaw、thr、alt等值之后还有剩余的空间,可以利用起来。比如,无线调参,通过串口发送最先调的内环PID值给遥控器,然后遥控器通过NRF24传输给无人机进而改变无人机的PID值,方便调参。但是我在进行操作的时候写入flash里面,写一次经常不回成功,要写入2次或以上,有时还需要重启才能真正改变PID值,有bug,为了知道PID值是否改变你也可以在无人机那里将PID值参数给遥控器。说到PID调参,后期在出调参总结。调了半个月都调不好。
在调试的时候你也可以通过无线传输观察波形,如下: 接下来就讲一下我踩过的坑: 下面的TX_PAYLO_WIDTH RX_PAYLO_WIDTH 收发双方一定要相同 那发送地址和接收地址有是干啥的呢?o(´^`)o 所以发送端和接收端的地址要相同,那无人机和遥控器端的发送地址和接收地址是否需要相同? 经过我的测试,前4个一定要相同,最后一个最好相同,因为在小马哥的robofly的代码中,他可能是为了提高遥控器无人机对频的成功概率或者速度,对发送地址和接收地址的第五个进行了改变,并将其写入flash(后面后讲到) #define SI24R1AddrMax 50 //NRF最后一个字节地址最大为50 uint8_t SI24R1addr = 0xFF; //初始化NRF最后一字节地址 uint8_t SI24R1_TX_DATA[TX_PAYLO_WIDTH];//NRF发送缓冲区 uint8_t SI24R1_RX_DATA[RX_PAYLO_WIDTH];//NRF接收缓冲区 uint8_t TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x06}; //发送地址 uint8_t RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x06}; //接收地址 配置相关引脚,并检测nrf24是否在线路上,如果不在线就是焊接错误了(95%是焊接错误,不要怀疑就是95%,在焊接元器件的时候我有70%的时间在焊接NRF24,因为它是无线的,看不见摸不着最难搞)至于这个函数SI24R1_Check(); //检查SI24R1是否与MCU通信 ,往NRF24里面某个可读可写的寄存器里写入读出即可 /***************************************************************************** * 函 数:void SI24R1_Init(void) * 功 能:NRF引脚GPIO初始化 * 参 数:无 * 返回值:无 * 备 注:无 *****************************************************************************/ void SI24R1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOA,ENABLE); /* 配置CSN引脚 */ GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStruct); GPIO_ResetBits(GPIOB,GPIO_Pin_12); /* 配置CE引脚 */ GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStruct); GPIO_ResetBits(GPIOA,GPIO_Pin_8); SPI_GPIO_Init(); //SPI2初始化 SI24R1_Check(); //检查SI24R1是否与MCU通信 SI24R1_CSN_HIGH; //失能NRF SI24R1_CE_LOW; //待机模式 } 切换NRF24的工作模式: /***************************************************************************** * 函 数:void SI24R1set_Mode(uint8_t mode) * 功 能:切换SI24R1的工作模式模式 * 参 数:无 * 返回值:无 * 备 注:无 *****************************************************************************/ void SI24R1set_Mode(uint8_t mode) { if(mode == IT_TX) { SI24R1_CE_LOW; SI24R1_write_reg(W_REGISTER+CONFIG,IT_TX); SI24R1_write_reg(W_REGISTER+STATUS,0X7E); //清除所有中断,防止一进去发送模式就触发中断 SI24R1_CE_HIGH; // Delay_us(15); } else { SI24R1_CE_LOW; SI24R1_write_reg(W_REGISTER+CONFIG,IT_RX);//配置为接收模式 SI24R1_write_reg(W_REGISTER+STATUS,0X7E); //清除所有中断,防止一进去接收模式就触发中断 SI24R1_CE_HIGH; Delay_us(200); } } 下面的函数中值得注意的是或者说决你能否成功通信的是:通道是否自动应答、通道的有效数据宽度(因为只有接收达到这个宽度才会触发中断)和发射通道RF的频率 /***************************************************************************** * 函 数:void SI24R1_Config(void) * 功 能:SI24R1基本参数配置,并初始化为接收模式 * 参 数:无 * 返回值:无 * 备 注:无 *****************************************************************************/ void SI24R1_Config(void) { SI24R1_CE_LOW; SI24R1_write_reg(W_REGISTER+SETUP_AW, 0x03); //配置通信地址的长度,默认值时0x03,即地址长度为5字节 SI24R1_Write_Buf(W_REGISTER+TX_ADDR,(uint8_t*)TX_ADDRESS,TX_ADR_WIDTH); //写TX节点地址 SI24R1_Write_Buf(W_REGISTER+RX_ADDR_P0,(uint8_t*)TX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK SI24R1_write_reg(W_REGISTER+SETUP_RETR,0x1A); //设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次 0x1A SI24R1_write_reg(W_REGISTER+EN_RXADDR,0x01);//使能通道0的接收地址 SI24R1_write_reg(W_REGISTER+EN_AA,0x01); //使能通道0自动应答 SI24R1_write_reg(W_REGISTER+RX_PW_P0,RX_PAYLO_WIDTH);//选择通道0的有效数据宽度 SI24R1_Write_Buf(W_REGISTER+RX_ADDR_P0,(uint8_t*)RX_ADDRESS,RX_ADR_WIDTH); //写RX节点地址 SI24R1_write_reg(W_REGISTER+RF_CH,60); //设置RF通道为40hz(1-64Hz都可以) SI24R1_write_reg(W_REGISTER+RF_SETUP,0x27); //设置TX发射参数,0db增益,2Mbps,低噪声增益关闭 (注意:低噪声增益关闭/开启直接影响通信,要开启都开启,要关闭都关闭0x0f) SI24R1set_Mode(IT_RX); //默认为接收模式 2021-3-6 接收->发送 SI24R1_CE_HIGH; } NRF24发送一包数据: /***************************************************************************** * 函 数:uint8_t SI24R1_TxPacket(uint8_t *txbuf) * 功 能:SI24R1发送一包数据 * 参 数:txbuf:要发送数据地址 * 返回值:无 * 备 注:无 *****************************************************************************/ void SI24R1_TxPacket(uint8_t *txbuf) { SI24R1_CE_LOW; SI24R1_Write_Buf(W_REGISTER+TX_ADDR,(uint8_t*)TX_ADDRESS,TX_ADR_WIDTH); //写TX节点地址 SI24R1_Write_Buf(W_REGISTER+RX_ADDR_P0,(uint8_t*)TX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK SI24R1_Write_Buf(W_RX_PAYLOAD,txbuf,TX_PAYLO_WIDTH); //写数据到TX_BUFF SI24R1_write_reg(W_REGISTER+CONFIG,0x0e); //设置为发送模式,开启所有中断 SI24R1_write_reg(W_REGISTER+STATUS,0X7E); //清除所有中断,防止一进去发送模式就触发中断 SI24R1_CE_HIGH; Delay_us(10); //CE持续高电平10us } NRF24接收一包数据: /***************************************************************************** * 函 数:uint8_t SI24R1_RxPacket(uint8_t *rxbuf) * 功 能:SI24R1接收一包数据 * 参 数:rxbuf:接收数据存储地址 * 返回值:无 * 备 注:无 *****************************************************************************/ void SI24R1_RxPacket(uint8_t *rxbuf) { SI24R1_CE_LOW; SI24R1_Read_Buf(R_RX_PAYLOAD,rxbuf,TX_PAYLO_WIDTH);//读取RX的有效数据 SI24R1_write_reg(FLUSH_RX,0xff); //清除RX FIFO(注意:这句话很必要) SI24R1_CE_HIGH; } 注意robofly的代码中发送和接收地址的第五个就在这里面改变了,经过debug, SI24R1addr =0x16 给飞机获取上的SI24R1获取一个地址: /***************************************************************************** * 函 数:void SI24R1_GetAddr(void) * 功 能:给飞机获取上的SI24R1获取一个地址 * 参 数:无 * 返回值:无 * 备 注:此函数需要与遥控器的对频函数联合使用否者SI24R1通信不成功, 如果自己做的的遥控器可直接用固定地址 *****************************************************************************/ void SI24R1_GetAddr(void) { if(SI24R1addr > SI24R1AddrMax)//当 SI24R1addr大于10,就说明次时SI24R1还未初始化完成 { srand(SysTick->VAL);//给随机数种子 // printf("SysTick->VAL:%drn",SysTick->VAL); SI24R1addr = rand()%SI24R1AddrMax;//随机获取SI24R1最后一位地址(地址:0~50) PID_WriteFlash();//保存此地址Flash }else if(SI24R1addr != TX_ADDRESS[TX_ADR_WIDTH-1]) { TX_ADDRESS[TX_ADR_WIDTH-1] = SI24R1addr; RX_ADDRESS[TX_ADR_WIDTH-1] = SI24R1addr; SI24R1_Config(); // printf("SI24R1Addr:%drn",SI24R1addr); } } 上面是无人机的NRF24代码,下面是遥控器的相关代码: 配置引脚->NRF24配置->NRF24发送函数|NRF24接收函数->对频函数 u8 TX_ADDRESS[TX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0xFF}; //此地址用来识别接收端哪个RX通道可以接收发送出去的数据包 u8 RX_ADDRESS[RX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0xFF}; //此地址用来配置本机SI24R1的RX0通道的地址,同时为了能正常收到应答信号,此地址一般都和上面的地址配置相同 可以看到我的遥控器和无人机的发送接收地址不完全相同 /********************************************************************** 配置SI24R1为RX模式,准备开始接收数据 ***********************************************************************/ void RX_Mode(void) { CE_LOW; //拉低CE,进入待机模式,准备开始往SI24R1中的寄存器中写入数据 SPI_Write_Byte(WRITE_REG_CMD + CONFIG, 0x0f); //配置为接收模式 SPI_Write_Byte(WRITE_REG_CMD + STATUS, 0x7e); //写0111 xxxx 给STATUS,清除所有中断标志,防止一进入接收模式就触发中断 CE_HIGH; //拉高CE,准备接受从外部发送过来的数据 } /********************************************************************** 从SI24R1的RX的FIFO中读取一组数据包 输入参数rx_buf:FIFO中读取到的数据的保存区域首地址 ***********************************************************************/ void SI24R1_ReceivePacket(u8* rx_buf) { CE_LOW; SPI_Read_Buf(RD_RX_PLOAD,rx_buf,RX_PLOAD_WIDTH); //从RX端的FIFO中读取数据,并存入指定的区域,注意:读取完FIFO中的数据后,SI24R1会自动清除其中的数据 SPI_Write_Byte(FLUSH_RX,0xff); //清除接收FIFO(很必要) CE_HIGH; //重新拉高CE,让其重新处于接收模式,准备接收下一个数据 } /********************************************************************** 配置SI24R1为TX模式,并发送一个数据包 输入参数tfbuf:即将要发送出去的数据区首地址 ***********************************************************************/ void SI24R1_SendPacket(u8* tfbuf) { CE_LOW; //拉低CE,进入待机模式,准备开始往SI24R1中的寄存器中写入数据 // SPI_Write_Buf(WRITE_REG_CMD + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); //装载接收端地址,由于这里只有一个通道通讯,不用改变接收端的SI24R1的接收通道地址,所以,这句可以注释掉 SPI_Write_Buf(WR_TX_PLOAD, tfbuf, TX_PLOAD_WIDTH); //将数据写入TX端的FIFO中,写入的个数与TX_PLOAD_WIDTH设置值相同 SPI_Write_Byte(WRITE_REG_CMD + CONFIG, 0x0e); //将SI24R1配置成发射模式 SPI_Write_Byte(WRITE_REG_CMD + STATUS, 0x7e); //写0111 xxxx 给STATUS,清除所有中断标志,防止一进入发射模式就触发中断 CE_HIGH; //拉高CE,准备发射TX端FIFO中的数据 delay_ms(1); //CE拉高后,需要延迟至少130us } 在下面的飞机对频函数中同样是改变了发送接收地址中的第五个值。进入一个while循环直到遇到return才会跳出(也就是它一定进入那个条件判断里面) /* 遥控器飞机对频(其实是对地址) */ void WaitFlY_Connection(void) { static u8 cnt = 0,preaddr; ConnectingDisplay();//断线连接状态显示 while(1) { if(FLY_Connect_OK) { cnt = 0; if(preaddr != TX_ADDRESS[TX_ADR_WIDTH-1]) { PID_WriteFlash(); //保存上一次连接的飞机的SI24R1地址 // printf("Address save :%d preaddr:%drn",TX_ADDRESS[4],preaddr); } // printf("Fly connect OK!!!rn"); return; }else if(cnt++ < 10) { PID_ReadFlash(); //读取上一次保存的飞机的SI24R1地址 preaddr = TX_ADDRESS[TX_ADR_WIDTH-1]; SI24R1_Config(); delay_ms(50); // printf("Flash read SI24R1addr:%drn",TX_ADDRESS[4]); } else { TX_ADDRESS[TX_ADR_WIDTH-1]++ ; RX_ADDRESS[TX_ADR_WIDTH-1]++ ; if(TX_ADDRESS[TX_ADR_WIDTH-1]>AddrMax && RX_ADDRESS[TX_ADR_WIDTH-1]>AddrMax) { TX_ADDRESS[TX_ADR_WIDTH-1] = 0x00; RX_ADDRESS[TX_ADR_WIDTH-1] = 0x00; } SPI_Write_Buf(WRITE_REG_CMD + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH); SPI_Write_Buf(WRITE_REG_CMD + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH); delay_ms(100); } if(ADC_CALIBRATOR_OK) { OLED_ShowString(byte(2),line4,"Calibration",8); } else { OLED_ShowString(byte(2),line4,"Connecting...",8); } } } 下面来讲一下无人机发送数据给遥控器和正点原子开发板,然后开发板再传输数据到上位机上 其实我这个都是用的通道0,通道0的发送地址和通道0的接收地址,然后还能和2个或者2个以上的从机进行无线通信而互相之间没有干扰,那就要做到和每个从机的通信频率不一样,而且相差较大。其中关键的代码如下: 因为默认的发送频率是60HZ,所以第一次SendToRemote(); 是发送给遥控器的,第二次发送前先改变一下频率,改为10HZ,然后再发送,发送完成之后再改回来。 参考:https://blog.csdn.net/u012780337/article/details/90638629 那如果是同一个发送频率,不同的通道应该也能实现相同的效果 具体可以参考:一对多通信https://blog.csdn.net/m0_37968313/article/details/99453557 https://blog.csdn.net/m0_37968313/article/details/98219062 看下面可以看到相同频率干扰之大! |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1810 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1631 浏览 1 评论
1098 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
737 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1687 浏览 2 评论
1945浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
749浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
584浏览 3评论
604浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
567浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-29 09:50 , Processed in 0.804404 second(s), Total 78, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号