NRF24L01双向传输(一对一) |
您所在的位置:网站首页 › 麻雀的说明 › NRF24L01双向传输(一对一) |
NRF24L01双向传输(一对一)
简介
本文章记录两个NRF24L01无线模块实现双向传输的软件设计~ 为什么可以双向传输呢?这要归功于它具有Enhanced ShockBurst,可以工作在主接收和主发送模式,在主接收方可以将我们自己的数据附在ACK Packet实现双向传输,所以此次双向传输会将两个NRF分别设置成主接收和主发送。 · 实物(模块非单独芯片): 上面的AAAAA就是后面即将看到的功能寄存器的映射地址。 功能寄存器地址映射表: 地址助记符位复位值类型描述00CONFIG配置寄存器Reserved70R/w只能为0MASK_RX_DR60R/w接收中断标志。0:使能MASK_TX_D50R/w发送中断标志。0:使能MASK_MAX_RT40R/w最大重发次数中断。0:使能EN_CRC31R/w使能CRCCRCO20R/wCRC编码方案选择,1~2bytesPWR_UP10R/w1:上电,0:下电PRIM_RX00R/w收发控制。1:PRX;0:PTX01EN_AA使能自动应答Reserved7:600R/w只能为0ENAA_P551R/w使能通道5自动应答机制ENAA_P441R/w使能通道4自动应答机制ENAA_P331R/w使能通道3自动应答机制ENAA_P221R/w使能通道2自动应答机制ENAA_P111R/w使能通道1自动应答机制ENAA_P001R/w使能通道0自动应答机制02EN_RXADDR使能 RX 地址Reserved7:600R/w只能为0ERX_P550R/w使能通道5ERX_P440R/w使能通道4ERX_P330R/w使能通道3ERX_P220R/w使能通道2ERX_P110R/w使能通道1ERX_P000R/w使能通道003SETUP_AW设置地址宽度(所有通道的地址)Reserved7:2000000R/w只能为0AW1:011R/w收发地址宽度,’11’-5bytes04SETUP_RETR设置自动重发射机制ARD7:40000R/w自动重发延时’0001’-等待500usARC3:00011R/w自动重发计数05RF_CH设置RF频宽Reserved70R/w只能为0RF_CH6:00000010R/w设置RF的工作频宽06RF_SETUPRF设置寄存器CONT_WAVE70R/w为1使能连续传送载波Reserved60R/w只能为0RF_DR_LOW50R/w设置RF数据250kbpsPLL_LOCK40R/w仅使用于测试?RF_DR_HIGH31R/w‘00’-1M,’01’-2M,’10’-250kbpsRF_PWR2:111R/w设置RF输出增益 ’00’-18dbm,’11’-0dbmObsolete0无意义07STATUS状态寄存器Reserved70R/w只能为0RX_DR60R/w数据就绪RXFIFO中断,写1清除TX_DS50R/w数据发送RXFIFO中断,写1清除MAX_RT40R/w最大字节重发中断,写1清除RX_P_NO3:1111R数据通道号TX_FULL00RTX FIFO满标志(满为1)08OBSERVE_TX发射监测寄存器PLOS_CNT7:40R丢包计数ARC_CNT3:00R重发射数据包次数09RPD接收电源检测Reserved7:1000000R只能为0RPD00R接收电源检测0ARX_ADDR_P039:0E7E7E7E7E7R/W通道0接收地址(5个字节)0BRX_ADDR_P139:0C2C2C2C2C2R/W通道1接收地址(5个字节)0CRX_ADDR_P27:0C3R/W通道2接收地址(1个字节(低))0DRX_ADDR_P37:0C4R/W通道3接收地址(1个字节(低))0ERX_ADDR_P47:0C5R/W通道4接收地址(1个字节(低))0FRX_ADDR_P57:0C6R/W通道5接收地址(1个字节(低))10TX_ADDR39:0E7E7E7E7E7R/W发射地址,仅适用于PTX11RX_PW_P0在通道0中RX_Payload的字节个数Reserved7:600R/W只能为0RX_PW_P05:00R/W接收数据通道0字节数12RX_PW_P1………在通道1中RX_Payload的字节个数13RX_PW_P2………在通道2中RX_Payload的字节个数14RX_PW_P3………在通道3中RX_Payload的字节个数15RX_PW_P4………在通道4中RX_Payload的字节个数16RX_PW_P5………在通道5中RX_Payload的字节个数17FIFO_STATUSFIFIO状态寄存器Reserved70R/W只能为0TX_REUSE60R重用TX PayloadTX_FULL50RTX FIFO满标志TX_EMPTY41RTX FIFO空标志Reserved3:200R/W只能为0RX_FULL10RRX FIFO满标志RX_EMPTY01RRX FIFO空标志1CDYNPD使能动态数据包长度Reserved7:60R/W只能为0DPL_P550R/W使能pipe 5动态数据包长度DPL_P440R/W使能pipe 4动态数据包长度DPL_P330R/W使能pipe 3动态数据包长度DPL_P220R/W使能pipe 2动态数据包长度DPL_P110R/W使能pipe 1动态数据包长度DPL_P000R/W使能pipe 0动态数据包长度1DFEATURER/W特征寄存器Reserved7:30R/W只能为0EN_DPL20R/W使能动态数据包长度EN_ACK_PAY10R/W使能数据包应答EN_DYN_ACK00R/W使能写发送数据包非应答命令如果NRF24L01用作具有Enhanced ShockedBurst 的PTX设备,那么将TX_ADDR寄存器里边的数值(地址)设置成通道0接收地址,使两个相同。 接下来看看该芯片的时序图: (1) 写时序 至此,SPI的初始化配置和通行API函数已经写好,接下来define下片选以及CE使能的IO口: #define NRF24L01_CE PGout(8) //24L01片选信号 #define NRF24L01_CSN PGout(7) //SPI片选信号 /******下面的中断引脚程序上不用,调试的时候用过而已******/ #define NRF24L01_IRQ PGin(6) //IRQ主机数据输入接下来就进一步根据时序图来编写读写函数了: /*************************************************************** * 写寄存器 ****************************************************************/ uint8_t Write_Reg(uint8_t reg, uint8_t value) { uint8_t status; NRF24L01_CSN = 0; /* 选通器件 */ status = SPI2_ReadWriteByte(reg); /* 写寄存器地址 */ SPI2_ReadWriteByte(value); /* 写数据 */ NRF24L01_CSN = 1; /* 禁止该器件 */ return status; } /*************************************************************** * 读寄存器 ****************************************************************/ uint8_t Read_Reg(uint8_t reg) { uint8_t reg_val; NRF24L01_CSN = 0; /* 选通器件 */ SPI2_ReadWriteByte(reg); /* 写寄存器地址 */ reg_val = SPI2_ReadWriteByte(0); /* 读取该寄存器返回数据 */ NRF24L01_CSN = 1; /* 禁止该器件 */ return reg_val; }扩展至连续读连续写,那么还有下面函数: /**************************************************************** * 写缓冲区---------------------------------- *****************************************************************/ uint8_t Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars) { uint8_t i; uint8_t status; NRF24L01_CSN = 0; /* 选通器件 */ status = SPI2_ReadWriteByte(reg); /* 写寄存器地址 */ for(i=0; i uint8_t i; uint8_t status; NRF24L01_CSN = 0; /* 选通器件 */ status = SPI2_ReadWriteByte(reg); /* 写寄存器地址 */ for(i=0; i MySPI2_Init(); NRF24L01_CE = 0; Write_Reg(NRF_WRITE_REG + SETUP_AW, 0x03); Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(uint8_t *)RX_ADDRESS,5); Write_Buf(NRF_WRITE_REG+TX_ADDR,(uint8_t *)TX_ADDRESS,5); Write_Reg(NRF_WRITE_REG+EN_AA,0x01); //使能通道0的自动应答 Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址 Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a); //设置自动重发间隔时间:500us;最大自动重发次数:10次 2M波特率下 Write_Reg(NRF_WRITE_REG+RF_CH,ch); //设置RF通道为CHANAL Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启 if(mode==1) //PRX { Write_Reg(NRF_WRITE_REG + CONFIG, 0x0f); // IRQ收发完成中断开启,16位CRC,主接收 Write_Reg(FLUSH_TX,0xff); Write_Reg(FLUSH_RX,0xff); Write_Reg(NRF_WRITE_REG+0x1c,0x01); Write_Reg(NRF_WRITE_REG+0x1d,0x06); } else if(mode==2) //TX2 { Write_Reg(NRF_WRITE_REG + CONFIG, 0x0e); // IRQ收发完成中断开启,16位CRC,主发送 Write_Reg(FLUSH_TX,0xff); Write_Reg(FLUSH_RX,0xff); Write_Reg(NRF_WRITE_REG+0x1c,0x01); Write_Reg(NRF_WRITE_REG+0x1d,0x06); } NRF24L01_CE = 1; }数据传输涉及到发射数据包和接收数据包,另外,有必要检测NRF是否初始化正常,还需要检测环节。 //就是试着通过SPI写一串数据,然后再读出来,对比下,没问题返回1 u8 NRF_Check(void)//检查NRF模块是否正常工作 { u8 buf1[5]; u8 i; /*写入5个字节的地址. */ Write_Buf(NRF_WRITE_REG+TX_ADDR,(uint8_t *)TX_ADDRESS,5); /*读出写入的地址 */ Read_Buf(TX_ADDR,buf1,5); /*比较*/ for(i=0;i NRF24L01_CE = 0; //可向别的模块(地址)发射数据 Write_Buf(NRF_WRITE_REG + RX_ADDR_P0, TX_ADDRESS, 5); // 装载接收端地址 Write_Buf(WR_TX_PLOAD, tx_buf, len); // 装载数据 NRF24L01_CE = 1; //置高CE,激发数据发送 } void NRF_Send_Data(u8 *data , u8 length) { TxPacket(data,length); //while(NRF24L01_IRQ!=0); } void Receive_Data(void)//检查是否有通信事件 { u8 sta = Read_Reg(NRF_READ_REG + NRFRegSTATUS); //while(NRF24L01_IRQ!=0);//等待发送完成 //接收到数据包后,再对数据进行辨别 if(sta & (1 if(sta & 0x01) //TX FIFO FULL { Write_Reg(FLUSH_TX,0xff); } } //很多单片机或者是其他芯片,清除状态为都是往相应位写1 Write_Reg(NRF_WRITE_REG + NRFRegSTATUS, sta);//写1清除状态寄存器 sta = Read_Reg(NRF_READ_REG + NRFRegSTATUS); } void Data_Receive_PRO(void) { if((NRF24L01RXDATA[0] == 0x01) && (NRF24L01RXDATA[1]==0x02))//帧头 { Rec_ADC_Raw_Val = ((uint16_t)NRF24L01RXDATA[2] u8 blink_flag2=0; //简单点,省去配置通用定时器的麻烦,直接用滴答定时器 //注意这里开启滴答定时器中断就不要用正点原子的delay.c了 //一个完美的程序尽量不去用延时,占用CPU资源 //不精确延时可以自己写 SysTick_Config(SystemCoreClock / 1000);//开启滴答定时器中断(72000000/1000)/72MHz = 1ms,即定时1ms中断一次 uart_init(115200); //串口初始化为115200 LED_Init(); //初始化与LED连接的硬件接口 //下面的函数第一个入口参数为PTX,表示设置为主发送 //其实,作为两个nRF实现双向通讯,另一方只需要设置为PRX即可。 NRF_Init(PTX,80); //初始化NRF24L01 while(NRF_Check()==0) printf("NRF24L01 disconnected!/n"); while(1) { if(ms_10>100)//每100ms发射一次数据包,闪一次灯,表明正常执行 { ms_10 = 0; /*调试用****/ if(blink_flag2 == 0) { PEout(5) = 0; blink_flag2 = 1; } else if(blink_flag2 == 1) { PEout(5) = 1; blink_flag2 = 0; } /*****只自加这个元素*******/ Txdata_buffer1[2]++; NRF_Send_Data(Txdata_buffer1,7); } if(ms_2>2)//每2ms检查NRF是否有通信事件 { Receive_Data(); ms_2 =0; } if(ms_500>500)//每0.5秒led灯闪一次,串口打印接收到的ADC原始值。 { if(blink_flag == 0){ PBout(5) = 0; blink_flag = 1; } else{ PBout(5) = 1; blink_flag = 0; } printf("the Raw ADC val is %d.\n",Rec_ADC_Raw_Val); ms_500 = 0; } } }上面的ms_2,ms_10,ms_500在文件stm32f10x_it.c中的滴答定时器中断更新。 void SysTick_Handler(void) { ms_10++; ms_500++; ms_2++; }(2)主接收主程序demo: //平台用的是潘多拉开发板stm32L475 int main(void) { HAL_Init(); SystemClock_Config(); //初始化系统时钟为80M delay_init(80); //初始化延时函数 80M系统时钟 Usart1_Init(115200); ADC1IN3_Init(); LED_Init(); //初始化LED NRF_Init(PRX,80); while(NRF_Check()==0); LCD_Init();//IIC接口屏 LCD_Clear(WHITE); TIM2_Init(100 - 1, 8000 - 1);//10ms //Iwdg_Init(); while(1) { LED_Function();//每0.5秒LED闪一次 Schedule_100ms();//每100ms在显示屏上面,这里只显示了在主发送方那边自加的Txdata_buffer1[2] Schedule_Rec_data();//每10ms检查有没有接收到数据 Schedule_Send_data();//每50ms发送数据包 } } void Schedule_Send_data(void) { uint8_t temp[10]; if(count_Send_data>5) { temp[0] = 0x01; temp[1] = 0x02; temp[2] = adc_test/256; temp[3] = adc_test%256; NRF_Send_Data(temp, 4); count_Send_data = 0; } } void Schedule_Rec_data(void) { if(count_Rec_data>1) { Receive_Data(); count_Rec_data = 0; } } void Schedule_100ms(void) { if(counter_100ms > 10) { Get_ADC(); Get_AHT10_Data(); LCD_ShowNum(100, 200, Rec_Test_Buf[0] , 5, 16); counter_100ms = 0; } } 总结 两个NRF24L01双向通讯,初始化程序除了配置寄存器的最低位不一样之外,其他都一样。(最低位切换PTX和PRX)一开始没设置收发地址宽度,导致两个模块通讯不上,查找了挺久,最后看了寄存器表试着设置了就OK了。上面的主接收主程序没写全,不过相信大家应该知道怎么写,跟主发送的差不多。本程序在两个开发板上已经调试正常运行 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |