当前位置: 首页 > news >正文

NRF24L01模块通信实验

NRF24L01简要介绍

在这里插入图片描述

  这里主要介绍模块的最重要的参数,废话就不多介绍了。
  该模块是一款无线通信模块,一个模块即可同时具备发射和接收数据的功能,但是要想实现通信必须使用两个模块之间才能进行通信。NRF24L01模块使用的总线控制方式为SPI总线。SPI的通信方式本文就不介绍了。

预备知识

  首先需要知道NRF24L10的通信过程是怎样的,这里主要是指具体的通信方式,假设一个模块A设置为发送方,另一个模块B设置为接收方,那么A和B之间通信需要几个基本设置,只有配置好这些参数后,双方才能进行通信。

通道

  通道是发送方与接收方的通信具体的一条路,可以这么理解,比如小时候我们玩的两个人用纸杯子连一条线来进行传话,那么这里这条线就可以理解为通道,因为双方的声音是通过这条线进行传输的。对于NRF24L01模块,其最多有6个通道,也就是说,一个设备,最多可以与6个设备同时进行通信。

发送地址与接收地址

  模块通信中有TX_ADDR和RX_ADDR两个地址。
  所谓发送地址就是对于当前模块,你希望将数据在数据通道中发往哪个具体的地址,这个地址就是发送地址。对于发送模式下,需要对该地址进行配置。接收地址其实有些同学可能会越看越觉得难以理解,其实我们换个说法就很容易理解了,直接把接收地址理解为本地地址,也就是你用这个地址来接收别人发送的数据的地址,只有当别人发送过来的数据的发送地址和你的地址相同时,你才将这个数据进行接收。
  对于接收地址,接收模式下需要进行配置,这是理所当然的,但是注意!接收地址在发送模式中也要进行配置(当工作在自动应答模式下),为什么呢?这是因为作为发送方,我们发送完数据后,还需要通过接收应答信号来确定接收方是否接收到了我们发送的数据,那么这个接收方的应答信号来了的话,发送方就需要去接收,因此这也是我们配置接收地址的原因了。
  在NRF24L01模块中,发送地址TX_ADDR只有一个,而接收地址最多可以配置6个。

NRF24L01引脚介绍

  数据手册上有很多引脚的介绍,但是我们实际使用的话主要用到其中6个引脚,如下图。
在这里插入图片描述
  其中SPI的四个引脚就不过多介绍了。主要介绍另外两个引脚CE和IRQ。在下一节可以看到,CE的主要功能是用来配置NRF24L01的工作模式的。而另外一个引脚IRQ其实是中断引脚,这里具体解释一下这个引脚的作用是什么。
  当下面的情况发生时,IRQ引脚会被硬件自动拉低用于提醒我们中断的产生:
1、接收到数据时,IRQ引脚会被硬件拉低;
2、发送数据成功时,IRQ引脚会被硬件拉低;
3、达到最大重发次数时,IRQ引脚会被硬件拉低;
  当上述三种情况发生时,通过IRQ引脚的检测,我们可以知道事件的发生,比如在发射模式下,我们可以通过检测IRQ引脚是否被拉低来得知发送是否完成或者是发送次数达到最大重发次数。当IRQ引脚发起中断后,必须进行手动清除中断,否则IRQ引脚将一直保持低电平状态,在后面讲到的STATUS寄存器中,我们可以通过对STATUS寄存器的RX_DR、TX_DS、MAX_RT三个位置1分别清除三种情况产生的中断。

NRF24L01工作模式

  在数据手册中,模块有6种工作模式,如果只是进行简单的通信实验我们不需要了解那么多,只需要知道三种模式:掉电模式、发射模式、接收模式。掉电模式顾名思义,就是让模块处于关机状态为了省电,此时PWR_UP位为0。发射模式和接收模式的引脚具体配置如下图所示,这里需要额外说明的有两点第一个是,在配置为发射模式时,需要将CE引脚拉低至少10us;第二个是不管是配置为发射模式还是接收模式,首先需要让模块处于掉电模式(不过在实际操作过程中发现好像没有进行这一步操作也没什么问题,但最好还是按手册规定来)。
在这里插入图片描述

NRF24L01相关操作码

  这里主要介绍需要用到的操作码。一般不会用到的操作码就不介绍了,具体如下图所示。
在这里插入图片描述
  R_REGISTER指令用于对寄存器进行读操作,0x000A AAAA,这里的A表示任意的数据,也就是你寄存器的地址,读操作时,前面三位必须是0;同理,写寄存器操作时,前面三位必须是001。
  R_RX_PAYLOAD是读取接收数据寄存器中数据的操作码,最多读32字节。
W_RX_PAYLOAD是写数据到发送数据寄存器的操作,同样最多32字节。其实这里我不太理解为什么名称不是W_TX_PAYLOAD,在实际代码中我还是比较喜欢用W_TX_PAYLOAD来表示这个操作码的。
  FLUSH_RX和FLUSH_TX就是用于清空接收数据寄存器和发送数据寄存器的操作码,因为有些情况寄存器中发送或者接收的数据会满了,如果没有及时清空里面的内容,会导致下一次的数据读取或接收产生错误。

寄存器地址

  这里我们同样只介绍一般情况下需要用到的寄存器。

CONFIG寄存器

  CONFIG寄存器主要用于对工作模式进行设置;
  CONFIG寄存器地址:0x00
  发射模式下设置参数:0x0E
  接收模式设置参数:0x0F
在这里插入图片描述

EN_AA寄存器

  EN_AA寄存器主要用于使能通道自动应答;
  EN_AA寄存器地址:0x01
  用到第几个通道就将对应的位置1,例如用到通道0则EN_AA:0x01;
在这里插入图片描述

EN_RXADDR寄存器

  EN_RXADDR寄存器主要用于使能数据通道接收允许;
  EN_RXADDR地址:0x02
  用到第几个通道就将对应的位置1;
在这里插入图片描述
在这里插入图片描述

SETUP_AW寄存器

  SETUP_AW寄存器地址:0x03
  SETUP_AW寄存器主要用于对地址长度进行配置(第0位和第1位);
  具体配置规则见下表。注意这里的地址长度是发射地址和接收地址,而不是单独某一个,也就是说发射地址和接收地址的长度必须一样。
在这里插入图片描述

SETUP_RETR寄存器

  SETUP_RETR寄存器地址:0x04
  SETUP_RETR寄存器主要用于重发相关的参数配置,主要包括重发时间间隔和重发次数;
在这里插入图片描述

RF_CH寄存器

  RF_CH寄存器地址:0x05;
  RF_CH寄存器主要用于设置通道的工作频率(第0位到第6位);
在这里插入图片描述

RF_SETUP寄存器

  RF_SETUP寄存器地址:0x06;
  RF_SETUP寄存器主要用于配置传输速率(第3位)和发射功率(第2位和第1位);
在这里插入图片描述

STATUS寄存器

  STATUS寄存器地址:0x07:
  STATUS寄存器主要用来获取当前模块状态的,通过该寄存器可以得知模块是否发送数据成功(发射模式下)、接收到数据(接收模式下)、是否达到最大重发次数(发射模式下)及其他相关信息。通过检查STATUS寄存器的RX_DR位可以判断在接收状态下是否收到数据,并对该位写1清除中断,实现对该位的清零操作。对于TX_DS和MAX_RT位同理。

在这里插入图片描述在这里插入图片描述

RX_ADDR_P0寄存器

  RX_ADDR_P0寄存器地址:0x0A;
  RX_ADDR_P0寄存器主要用于设置接收通道0的接收地址,最大长度为5;
  还有通道12345也是相同的,就不一一列出了;
在这里插入图片描述

TX_ADDR寄存器

  TX_ADDR寄存器地址:0x10;
  TX_ADDR寄存器主要用于配置发射地址,一个设备只有一个发射地址,最大长度5;
在这里插入图片描述

RX_PW_P0寄存器

  RX_PW_P0寄存器地址:0x11;
  RX_PW_P0寄存器主要用于设置接收通道0的接收地址宽度,范围在1-32,不能设置为0;通道12345也是相同的,就不一一列出了;
在这里插入图片描述
  至此,所有需要用到的寄存器就全部介绍完了,接下来我们进入代码部分。

各功能代码

相关定义

#ifndef __NRF_H__
#define __NRF_H__
#include <STC8.H>
#include"UART.h"sbit CE=P6^0;
sbit CS=P6^1;
sbit CLK=P6^2;
sbit MOSI=P6^3;
sbit MISO=P6^4;
sbit IRQ=P6^5;#define TX_ADDR_WIDTH 5
#define RX_ADDR_WIDTH 5
#define TX_PLOAD_WIDTH 1
#define RX_PLOAD_WIDTH 1#define READ_REG		0x00
#define WRITE_REG		0x20
#define R_RX_PAYLOAD	0x61
#define W_TX_PAYLOAD	0xA0
#define FLUSH_TX		0xE1
#define FLUSH_RX		0xE2#define CONFIG			0x00
#define EN_AA			0x01
#define EN_RXADDR		0x02
#define SETUP_AW		0x03
#define SETUP_RETR		0x04
#define RF_CH			0x05
#define RF_SETUP		0x06
#define STATUS			0x07
#define RX_ADDR_P0		0x0A
#define TX_ADDR			0x10
#define RX_PW_P0		0x11#define RX_FLAG			0x40
#define TX_FLAG			0x20
#define MAX_FLAG		0x10void NRF_Init();
void NRF_Tx_Mode();
uchar NRF_Tx_Data(uchar*buffer);
void NRF_Rx_Mode();
uchar NRF_Rx_Data(uchar*buffer);
#endif

注意,引脚对应需要按照你自己的接线方式来修改。

NRF24L01初始化

void NRF_Init()
{CE=1;CS=1;CLK=0;
}

SPI读写

uchar SPI_RW(uchar Byte)
{uchar i;for(i=0;i<8;i++){if(Byte&0x80)MOSI=1;elseMOSI=0;CLK=1;Byte<<=1;if(MISO)Byte|=0x01;CLK=0;}return Byte;
}

NRF24L01写寄存器一个字节

uchar NRF_Write(uchar addr,Byte)
{uchar status;CS=0;status=SPI_RW(addr);SPI_RW(Byte);CS=1;return status;
}

NRF24L01读寄存器一个字节

uchar NRF_Read(uchar addr)
{uchar Byte;CS=0;SPI_RW(addr);Byte=SPI_RW(0xFF);CS=1;return Byte;
}

NRF24L01写多个字节

uchar NRF_Write_Buffer(uchar addr,uchar*buffer,uchar len)
{uchar status;CS=0;status=SPI_RW(addr);while(len--){SPI_RW(*buffer);buffer++;}CS=1;return status;
}

NRF24L01读多个字节

uchar NRF_Read_Buffer(uchar addr,uchar*buffer,uchar len)
{uchar status;CS=0;status=SPI_RW(addr);while(len--){*buffer=SPI_RW(0xFF);buffer++;}CS=1;return status;
}

NRF24L01配置为发射模式

void NRF_Tx_Mode()
{CE=0;NRF_Write_Buffer(WRITE_REG+TX_ADDR,TX_ADDRESS,TX_ADDR_WIDTH);NRF_Write_Buffer(WRITE_REG+RX_ADDR_P0,RX_ADDRESS,RX_ADDR_WIDTH);NRF_Write(WRITE_REG+EN_AA,0x01);NRF_Write(WRITE_REG+EN_RXADDR,0x01);NRF_Write(WRITE_REG+SETUP_AW,0x03);NRF_Write(WRITE_REG+SETUP_RETR,0x0F);NRF_Write(WRITE_REG+RF_CH,27);NRF_Write(WRITE_REG+RF_SETUP,0x0F);NRF_Write(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);NRF_Write(WRITE_REG+CONFIG,0x0E);CE=1;
}

NRF24L01配置为接收模式

void NRF_Rx_Mode()
{CE=0;NRF_Write_Buffer(WRITE_REG+RX_ADDR_P0,RX_ADDRESS,RX_ADDR_WIDTH);NRF_Write(WRITE_REG+EN_AA,0x01);NRF_Write(WRITE_REG+EN_RXADDR,0x01);NRF_Write(WRITE_REG+SETUP_AW,0x03);NRF_Write(WRITE_REG+SETUP_RETR,0x0F);NRF_Write(WRITE_REG+RF_CH,27);NRF_Write(WRITE_REG+RF_SETUP,0x0F);NRF_Write(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);NRF_Write(WRITE_REG+CONFIG,0x0F);CE=1;
}

NRF24L01发送数据

// 这里的返回值只是我自己调试代码的过程中自己设置的 你可以不要返回值或者按照你的需要进行修改返回值
uchar NRF_Tx_Data(uchar*buffer)
{uchar state;CE=0;NRF_Write_Buffer(W_TX_PAYLOAD,buffer,TX_PLOAD_WIDTH);CE=1;while(IRQ);state=NRF_Read(STATUS);NRF_Write(WRITE_REG+STATUS,state);if(state&TX_FLAG)return 0x01;else if(state&MAX_FLAG){NRF_Write(FLUSH_TX,0xFF);return 0xFF;}return 0x00;
}

NRF24L01接收数据

// 这里的返回值只是我自己调试代码的过程中自己设置的 你可以不要返回值或者按照你的需要进行修改返回值
uchar NRF_Rx_Data(uchar*buffer)
{uchar state;state=NRF_Read(STATUS);NRF_Write(WRITE_REG+STATUS,state);if(state&RX_FLAG){NRF_Read_Buffer(R_RX_PAYLOAD,buffer,RX_PLOAD_WIDTH);NRF_Write(FLUSH_RX,0xFF);return 0x01;}return 0x00;
}



总体代码

NRF.h

#ifndef __NRF_H__
#define __NRF_H__
#include <STC8.H>
#include"UART.h"sbit CE=P6^0;
sbit CS=P6^1;
sbit CLK=P6^2;
sbit MOSI=P6^3;
sbit MISO=P6^4;
sbit IRQ=P6^5;#define TX_ADDR_WIDTH 5
#define RX_ADDR_WIDTH 5
#define TX_PLOAD_WIDTH 1
#define RX_PLOAD_WIDTH 1#define READ_REG		0x00
#define WRITE_REG		0x20
#define R_RX_PAYLOAD	0x61
#define W_TX_PAYLOAD	0xA0
#define FLUSH_TX		0xE1
#define FLUSH_RX		0xE2#define CONFIG			0x00
#define EN_AA			0x01
#define EN_RXADDR		0x02
#define SETUP_AW		0x03
#define SETUP_RETR		0x04
#define RF_CH			0x05
#define RF_SETUP		0x06
#define STATUS			0x07
#define RX_ADDR_P0		0x0A
#define TX_ADDR			0x10
#define RX_PW_P0		0x11#define RX_FLAG			0x40
#define TX_FLAG			0x20
#define MAX_FLAG		0x10void NRF_Init();
void NRF_Tx_Mode();
uchar NRF_Tx_Data(uchar*buffer);
void NRF_Rx_Mode();
uchar NRF_Rx_Data(uchar*buffer);
#endif

NRF.h

#include"NRF.h"uchar TX_ADDRESS[]={0x00,0x11,0x22,0x33,0x44};
uchar RX_ADDRESS[]={0x00,0x11,0x22,0x33,0x44};void NRF_Init()
{CE=1;CS=1;CLK=0;
}
uchar SPI_RW(uchar Byte)
{uchar i;for(i=0;i<8;i++){if(Byte&0x80)MOSI=1;elseMOSI=0;CLK=1;Byte<<=1;if(MISO)Byte|=0x01;CLK=0;}return Byte;
}uchar NRF_Write(uchar addr,Byte)
{uchar status;CS=0;status=SPI_RW(addr);SPI_RW(Byte);CS=1;return status;
}
uchar NRF_Read(uchar addr)
{uchar Byte;CS=0;SPI_RW(addr);Byte=SPI_RW(0xFF);CS=1;return Byte;
}
uchar NRF_Write_Buffer(uchar addr,uchar*buffer,uchar len)
{uchar status;CS=0;status=SPI_RW(addr);while(len--){SPI_RW(*buffer);buffer++;}CS=1;return status;
}
uchar NRF_Read_Buffer(uchar addr,uchar*buffer,uchar len)
{uchar status;CS=0;status=SPI_RW(addr);while(len--){*buffer=SPI_RW(0xFF);buffer++;}CS=1;return status;
}
void NRF_Tx_Mode()
{CE=0;NRF_Write_Buffer(WRITE_REG+TX_ADDR,TX_ADDRESS,TX_ADDR_WIDTH);NRF_Write_Buffer(WRITE_REG+RX_ADDR_P0,RX_ADDRESS,RX_ADDR_WIDTH);NRF_Write(WRITE_REG+EN_AA,0x01);NRF_Write(WRITE_REG+EN_RXADDR,0x01);NRF_Write(WRITE_REG+SETUP_AW,0x03);NRF_Write(WRITE_REG+SETUP_RETR,0x0F);NRF_Write(WRITE_REG+RF_CH,27);NRF_Write(WRITE_REG+RF_SETUP,0x0F);NRF_Write(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);NRF_Write(WRITE_REG+CONFIG,0x0E);CE=1;
}
uchar NRF_Tx_Data(uchar*buffer)
{uchar state;CE=0;NRF_Write_Buffer(W_TX_PAYLOAD,buffer,TX_PLOAD_WIDTH);CE=1;while(IRQ);state=NRF_Read(STATUS);NRF_Write(WRITE_REG+STATUS,state);if(state&TX_FLAG)return 0x01;else if(state&MAX_FLAG){NRF_Write(FLUSH_TX,0xFF);return 0xFF;}return 0x00;
}
void NRF_Rx_Mode()
{CE=0;NRF_Write_Buffer(WRITE_REG+RX_ADDR_P0,RX_ADDRESS,RX_ADDR_WIDTH);NRF_Write(WRITE_REG+EN_AA,0x01);NRF_Write(WRITE_REG+EN_RXADDR,0x01);NRF_Write(WRITE_REG+SETUP_AW,0x03);NRF_Write(WRITE_REG+SETUP_RETR,0x0F);NRF_Write(WRITE_REG+RF_CH,27);NRF_Write(WRITE_REG+RF_SETUP,0x0F);NRF_Write(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);NRF_Write(WRITE_REG+CONFIG,0x0F);CE=1;
}
uchar NRF_Rx_Data(uchar*buffer)
{uchar state;state=NRF_Read(STATUS);NRF_Write(WRITE_REG+STATUS,state);if(state&RX_FLAG){NRF_Read_Buffer(R_RX_PAYLOAD,buffer,RX_PLOAD_WIDTH);NRF_Write(FLUSH_RX,0xFF);return 0x01;}return 0x00;
}

UART.h

#ifndef __UART_H__
#define __UART_H__#include <STC8.H>#define uchar unsigned char
void Uart1_Init();
void UartSendByte(uchar Byte);#endif

UART.c

#include"UART.h"void Uart1_Init(void)	//9600bps@11.0592MHz
{SCON = 0x50;		//8位数据,可变波特率AUXR |= 0x40;		//定时器时钟1T模式AUXR &= 0xFE;		//串口1选择定时器1为波特率发生器TMOD &= 0x0F;		//设置定时器模式TL1 = 0xE0;			//设置定时初始值TH1 = 0xFE;			//设置定时初始值ET1 = 0;			//禁止定时器中断TR1 = 1;			//定时器1开始计时
}void UartSendByte(uchar Byte)
{SBUF=Byte;while(TI==0);TI=0;
}

main.c

#include <STC8.H>
#include"UART.h"
#include"NRF.h"
// 0:接收 1:发射
#define MODE 0
void Delay500ms(void)	//@11.0592MHz
{unsigned char data i, j, k;i = 29;j = 14;k = 54;do{do{while (--k);} while (--j);} while (--i);
}
uchar tx_buffer[1];
uchar rx_buffer[1];
/*
程序说明
该程序实现了独立接线的NRF24L01模块的通信实验
成功实现发送与接收 可通过MODE的值设置为发射还是接收模式
发射和接收地址:{0x00,0x11,0x22,0x33,0x44} 通道27
*/
void main()
{uchar state;Uart1_Init();NRF_Init();if(MODE)NRF_Tx_Mode();elseNRF_Rx_Mode();while(1){if(MODE){tx_buffer[0]=(tx_buffer[0]+1)%10;state=NRF_Tx_Data(tx_buffer);UartSendByte(state);}else{state=NRF_Rx_Data(rx_buffer);if(state==0x01)UartSendByte(rx_buffer[0]);}Delay500ms();}
}

再次提醒!!!模块必须使用两个才能进行通信!!!需要用两块板子两个模块搭配才能进行通信!!!如果看完这篇文章对你有帮助的话不妨点个赞吧~~~


http://www.mrgr.cn/news/82200.html

相关文章:

  • overleaf写学术论文常用语法+注意事项+审阅修订
  • Scala_【5】函数式编程
  • vue字符串的数字比较大小有问题
  • 家谱管理系统|Java|SSM|VUE| 前后端分离
  • 《 C++ 点滴漫谈: 十七 》编译器优化与 C++ volatile:看似简单却不容小觑
  • Fedora安装docker
  • 日期时间选择(设置禁用状态)
  • linux系统安装搭建chrony(ntp)时间同步服务器
  • git使用指南-实践-搭建git私服
  • 数据仓库中的指标体系模型介绍
  • Frontend - 分页(针对 python / Django )
  • 用Python操作字节流中的Excel工作簿
  • SpringCloud源码分析-Ribbon与LoadBalancer
  • python实现自动登录12306抢票 -- selenium
  • Yolo11改进策略:注意力改进|Neck层改进|SCSA,探索空间与通道注意力之间的协同效应|即插即用
  • 【Rust自学】9.2. Result枚举与可恢复的错误 Pt.1:match、expect和unwrap处理错误
  • 【Rust自学】9.1. 不可恢复的错误以及panic!
  • 【Rust自学】8.6. HashMap Pt.2:更新HashMap
  • 深入探讨服务器虚拟化:架构、技术与应用
  • 我们能否使用 ANSYS SPEOS 测量水质?
  • Unity3D仿星露谷物语开发14之Custom Property Attribute
  • 偏移类窗口函数—— LAG()、LEAD()用法详解
  • 【Kafka 消息队列深度解析与应用】
  • 【Rust自学】8.5. HashMap Pt.1:HashMap的定义、创建、合并与访问
  • 【分布式缓存中间件Memcached原理与应用】
  • 面试题汇总