NRF24L01+自组网(1对多)实现


本算法基于软件分配和判断从机地址的方式实现,模块不使用自动应答模式和自动重发模式,并且关闭发送中断和最大重发次数中断。故将NRF24L01+模块的代码部分发出来分享一下,算法算不上完美,大神轻喷:

算法注意点:

*
主机和从机共同使用同一个通道,所有从机的地址相同,所以主机发送指令时,要带上软件分配的从机地址

*
关闭发送中断和最大重发次数中断,防止发送完成或者超过重发次数引起中断,但不会影响读取标志位的值

*

从机采用中断接收模式,主机采用定时器定时查询NRF24L01+模块的STATU寄存器进行判断,也可用中断方式进行接收,但是要考虑到中断是下降沿触发,但是要考虑到单片机有没有可能在处理别的中断的时候,由于关闭了全局中断而此时有触发了接收中断而不能及时处理,从而错过了下降沿触发的时间,需根据具体情况具体分析。

*
由于屏蔽了发送中断,在发送时需要对CE管脚的高电平时间进行足够的延时,不然会引起发送乱码或者发送失败

*
由于采用的是非自动应答模式,所以需要切换发送和接收模式

*
为了保证通讯的质量,需要对发送的数据进行CRC校验

代码块

使用了PIC16F1876系列,发送和接收设置代码:
//发送模式配置字 void TX_Mode(void) { Clr_NRF24L01_CE; //写TX节点地址
NRF24L01_Write_Buf(NRF24L01_WRITE_REG+TX_ADDR,(unsigned char
*)TX_ADDRESS,TX_ADR_WIDTH);//设置TX节点地址,主要为了使能ACK
NRF24L01_Write_Buf(NRF24L01_WRITE_REG+RX_ADDR_P0,(unsigned char
*)RX_ADDRESS,RX_ADR_WIDTH);//失能自动应答功能(广播模式)
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+EN_AA,0x00); //使能通道0的接收地址
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+EN_RXADDR,0x01); //失能自动重发功能
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+SETUP_RETR,0x00); //设置RF通道为CHANNEL
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RF_CH,CHANNEL);
//设置TX发射参数,0db增益,250kbps,低噪声增益开启 NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RF_SETUP,
0x27); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,关闭发送和重发中断
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+CONFIG,0x3e); //CE为高,10us后启动发送
Set_NRF24L01_CE; }//接收模式配置字 void RX_Mode(void) { Clr_NRF24L01_CE; //写RX节点地址
NRF24L01_Write_Buf(NRF24L01_WRITE_REG+RX_ADDR_P0,(unsigned char
*)RX_ADDRESS,RX_ADR_WIDTH);//失能自动应答功能(广播模式)
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+EN_AA,0x00); //使能通道0的接收地址
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+EN_RXADDR,0x01); //设置RF通信频率
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RF_CH,CHANNEL);//选择通道0的有效数据宽度
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);
//设置TX发射参数,0db增益,250kbps,低噪声增益开启 NRF24L01_Write_Reg(NRF24L01_WRITE_REG+RF_SETUP,
0x27); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,关闭发送和重发中断
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+CONFIG,0x3f); //CE为高,进入接收模式
Set_NRF24L01_CE; }//发送一次数据 unsigned char NRF24L01_TxPacket(unsigned char
*txbuf) {unsigned char sta; uint32_t count = 0; //拉低CE脚才能进行写数据 Clr_NRF24L01_CE;
//写数据到TXBUF NRF24L01_Write_Buf(NRF24L01_WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);
Set_NRF24L01_CE;//根据主频而定,足够的延时,等待发送完成 while(count<300) count++;
sta=NRF24L01_Read_Reg(STATUSET);//清除TX_DS或MAX_RT中断标志
NRF24L01_Write_Reg(NRF24L01_WRITE_REG+STATUSET,sta);//清除TX FIFO寄存器
NRF24L01_Write_Reg(NRF24L01_FLUSH_TX,0xff); //达到最大重发次数,要不要都无所谓 if(sta&MAX_TX) {
return MAX_TX; } else if(sta&TX_OK) { NRF24L01_SEND_COUNT++; //单次发送计数,用于发送计数统计
return TX_OK; } else { return 0xff; //其他原因发送失败 } } //接收一次数据 unsigned char
NRF24L01_RxPacket(unsigned char *rxbuf) { unsigned char sta; Set_NRF24L01_CE;
sta = NRF24L01_Read_Reg(STATUSET);//清除TX_DS或MAX_RT中断标志
NRF24L01_Write_Reg(NRF24L01_WRITE_REG + STATUSET, sta);//接收到数据 if (sta & RX_OK)
{//读取数据 NRF24L01_Read_Buf(NRF24L01_RD_RX_PLOAD, rxbuf, RX_PLOAD_WIDTH); //清除RX
FIFO寄存器 NRF24L01_Write_Reg(NRF24L01_FLUSH_RX, 0xff); return RX_OK; } else {
return 0; } } //接收中断处理数据 void interrupt INTERRUPT_InterruptManager(void) { if
(PIE1bits.TMR2IE == 1 && PIR1bits.TMR2IF == 1) { TMR2_ISR(); } else if((IOCIF==1
)&&(IOCIE==1)) { //关闭电平变化中断 IOCIE=0; //清除PB4电平变化标志 IOCBF4=0;
//对接收数据的返回值进行判断,是否是接收到数据了 if(NRF24L01_RxPacket(NRF_RX_BUF) == RX_OK) {
LED2_SetLow();//DS2灯 __delay_ms(5); LED2_SetHigh(); NRF_RX_FLAG = 1; //接收标志位 }
IOCIE =1; } else if (PIE1bits.RCIE == 1 && PIR1bits.RCIF == 1) {
EUSART_Receive_ISR(); }else if (PIE1bits.TXIE == 1 && PIR1bits.TXIF == 1) {
EUSART_Transmit_ISR(); } }
主从机的发送和接收的代码差不多的,但是从机在处理接收数据时需要与软件分配的地址进行对比:
void interrupt INTERRUPT_InterruptManager(void) { if((IOCIF==1)&&(IOCIE==1)) {
//关闭电平变化中断 IOCIE=0; //清除PB4电平变化标志 IOCBF4=0; //把上次收到的数据进行清零 for (int i = 0; i <
10; i++) { RX_BUF[i] = 0; } //接受无线数据进行处理 if(NRF24L01_RxPacket(RX_BUF)==RX_OK) {
BCC_BACK_VALUE = BCC(RX_BUF,9); //CRC校验 //比对地址信息,0x00为主机地址,0x01~0xFE为从机的地址 if
(RX_BUF[0] == TUREDATA.Data.Addr || RX_BUF[0] == 0x00) { for (int i = 0; i < 10
; i++) { TX_BUF[i] =0; } if (BCC_BACK_VALUE != RX_BUF[9]) //校验码错误直接返回数据 { if
(RX_BUF[0] != 0x00) { TX_BUF[0] = RX_BUF[0]; TX_BUF[1] = RX_BUF[1]; TX_BUF[2] =
0x84; TX_BUF[9] = BCC(TX_BUF, 9); NRF24L01_SendMSG(TX_BUF); } IOCIE = 1;
//打开电平外部中断 return; } else { //校验正确 TUREDATA.Data.CMD = RX_BUF[1]; switch
(TUREDATA.Data.CMD) {case 0xB1: NRF24L01_SendMSG((uint8_t *)
TUREDATA.DataBuffer);break; default: break; } } } } IOCIE=1;//打开电平外部中断 } }
基本上就是以上的思路了,还请大家多多指教!

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信