NRF24L012.4G无线收发通信模块

datasheet

NRF24L01模块资料:https://download.csdn.net/download/acktomas/11135884

相关案例:

使用Arduinon开发板连接RF24L01实现控制伺服电机:
https://www.yiboard.com/thread-970-1-1.html
如何使用Arduino UNO、操纵杆模块和NRF24L01模块控制伺服电机:
https://www.yiboard.com/thread-1095-1-1.html
如何使用多个NRF24L01模块搭建一个Arduino无线网络
https://www.yiboard.com/thread-985-1-1.html

1. Arduino开发板使用NRF24L01进行无线通信

原文地址:https://www.yiboard.com/thread-994-1-1.html

如何使用NRF24L01收发器模块在两个Arduino开发板之间进行无线通信。为了说明无线通信,我们将举两个例子,第一个是从一个Arduino开发板向另一个发送简单的“Hello World”消息,在第二个例子中,我们将在Arduino开发板之间进行双向通信,其中我们使用在第一个Arduino开发板的操纵杆,控制在第二个Arduino开发板的伺服电机,反之亦然,使用第二个Arduino开发板的按钮,我们将控制第一个Arduino开发板的LED灯。

在这里插入图片描述

1.1 NRF24L01收发器模块

该模块的功耗非常低。它在传输过程中消耗大约12mA的功率,甚至低于LED。

该模块工作在3.3V,因此不要将其直接连接到5V的Arduino,因为它可能会损坏。 NRF24L01模块的其他引脚具有5V容限,因此您可以将它们直接连接到Arduino。
在这里插入图片描述
在这里插入图片描述

  • CE: 模块控制线,CSN为低时,CE协同CONFIG寄存器共同决定NRF24L01状态
  • CSN: SPI片选线
  • SCK: SPI时钟线
  • MOSI:SPI数据线(主机输出从机输入)
  • MISO:SPI数据线(主机输入从机输出)
  • IRQ: 中断信号线。中断时变为低电平,在以下三种情况变低:Tx FIFO发完并且收到ACK(使能ACK情况下)、Rx FIFO收到数据、达到最大重发次数

其中三个引脚用于SPI通信,它们需要连接到Arduino的SPI引脚,但请注意每个Arduino开发板都有不同的SPI引脚。引脚CSN和CE可以连接到Arduino板的任何数字引脚,它们用于将模块设置为待机或活动模式,以及用于在发送或命令模式之间切换。最后一个引脚是一个不必使用的中断引脚。

1.2 NRF24L01使用说明

NRF24L01收发器模块使用2.4 GHz频段,可以在250 kbps到2 Mbps的波特率下运行。如果在开放空间中使用且波特率较低,其范围可达100米。

该模块可以使用125个不同的通道,可以在一个地方拥有125个独立工作的调制解调器网络。每个通道最多可以有6个地址,或者每个单元可以同时与多达6个其他单元通信。
在这里插入图片描述

1.3 连接示意图

将NRF24L01模块连接到Arduino开发板,我们就可以为发射器和接收器制作代码了在这里插入图片描述

1.4 Arduino代码

首先,我们需要下载并安装RF24库:https://github.com/nRF24/RF24,这使得编程变得简单。

以下是无线通信的两个代码,在代码下面是对它们的解释。

1.4.1 发射机代码

/*
* Arduino Wireless Communication Tutorial
*     Example 1 - Transmitter Code
*                
* by Dejan Nedelkovski, www.HowToMechatronics.com
* 
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(7, 8); // CE, CSN
const byte address[6] = "00001";
void setup() {
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
}
void loop() {
  const char text[] = "Hello World";
  radio.write(&text, sizeof(text));
  delay(1000);
}

1.4.2 接收机代码

/*
* Arduino Wireless Communication Tutorial
*       Example 1 - Receiver Code
*                
* by Dejan Nedelkovski, www.HowToMechatronics.com
* 
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(7, 8); // CE, CSN
const byte address[6] = "00001";
void setup() {
  Serial.begin(9600);
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();
}
void loop() {
  if (radio.available()) {
    char text[32] = "";
    radio.read(&text, sizeof(text));
    Serial.println(text);
  }
}

1.4.3 代码描述:

我们需要包含基本SPI和新安装的RF24库,并创建一个RF24对象。这里的两个参数是CSN和CE引脚。

RF24 radio(7, 8); // CE, CSN

接下来,我们需要创建一个字节数组,它将代表地址,或两个模块通过的所谓管道。

const byte address[6] = "00001";

我们可以将此地址的值更改为任意5个字母的字符串,这样就可以选择我们要说话的接收器,因此在本例中,我们将在接收器和发送器上具有相同的地址。

在设置部分,我们需要初始化无线电对象,并使用radio.openWritingPipe()函数设置我们将发送数据的接收器的地址,即我们之前设置的5个字母的字符串。

radio.openWritingPipe(address);

另一方面,在接收器处,使用radio.setReadingPipe()函数,我们设置相同的地址,并以此方式启用两个模块之间的通信。

radio.openReadingPipe(0, address);

然后使用radio.setPALevel()函数设置功率放大器电平,在我们的例子中,我将其设置为最小值,因为我的模块彼此非常接近。

radio.setPALevel(RF24_PA_MIN);

请注意,如果使用更高的电平,建议在GND和3.3V模块之间使用旁路电容,以便在工作时具有更稳定的电压。

接下来我们有了radio.stopListening()函数,它将模块设置为发送器,另一方面,我们有radio.startListening()函数,它将模块设置为接收器。

// at the Transmitter
radio.stopListening();
// at the Receiver
radio.startListening();

在发送器的loop函数中,我们创建了一个字符数组,并为其分配消息“Hello World”。使用radio.write()函数,我们将该消息发送给接收器。这里的第一个参数是想要发送的变量。

void loop() {
const char text[] = "Hello World";
radio.write(&text, sizeof(text));
delay(1000);
}

通过在变量名之前使用“&”,我们实际上设置了一个指示存储我们想要发送的数据的变量,并使用第二个参数设置了我们想要从该变量中获取的字节数。在这种情况下,sizeof()函数获取字符串“text”的所有字节。在程序结束时,我们将增加1秒的延迟。

另一方面,在接收器,在loop()函数部分中,通过使用radio.available()函数,我们检查是否有要接收的数据。如果有数据,首先我们创建一个由32个元素组成的数组,每个元素称为“text”,我们将在其中保存传入的数据。

void loop() {
  if (radio.available()) {
    char text[32] = "";
    radio.read(&text, sizeof(text));
    Serial.println(text);
  }
}

通过使用radio.read()函数,我们读取数据并将其存储到变量“text”中。最后,我们只在串口监视器上打印文本。因此,在我们上传了两个程序之后,就可以在接收器上运行串口监视器,我们会注意到每秒打印一条消息“Hello World”。

1.5 补充spi一点知识

1. SPI读写时序

空闲状态SCK为0,CPOL=0;数据在时钟第一个跳变沿采集,CPHA=0;

  • Cn:SPI命令位
  • Sn:STATUS寄存器位
  • Dn:数据位(MSB,多字节传输时,低字节在前)
    在这里插入图片描述

2. 工作模式

NRF24L01工作模式,由CE和CONFIG寄存器(0x00)的PWR_UP(第1位)和PRIM_RX(第0位)共同控制:

NRF24L01工作模式 PWR_UP位状态 PRIM_RX位状态 CE引脚电平 FIFO寄存器状态
接收模式 1 1 1 -
发送模式 1 0 1 数据在TX FIFO寄存器中
发送模式 1 0 1 ----> 0 停留在发送模式,直至发送完
待机模式II 1 0 1 TX FIFO为空
待机模式I 1 - 0 无数据传输
掉电模式 0 - - -

2.1 收发模式

收发模式有Enhanced ShockBurstTM收发模式、ShockBurstTM收发模式和直接模式三种,收发模式由器件配置字决定。

Enhanced ShockBurstTM收发模式

发送数据时,自动加上字头和CRC校验码,在发送模式下,置CE为高,至少10us,将使能发送过程;接收数据时,自动把字头和CRC校验码移去。

在接收端,确认收到数据后记录地址,并以此地址为目标地址发送应答信号。在发送端,通道0被用作接收应答信号,故通道0的接收地址与发送地址端地址相等,以确保接收到正确应答信号。

Enhanced ShockBurstTM发送流程:

A. 把地址和要发送的数据按时序送入NRF24L01;
B. 配置CONFIG寄存器,使之进入发送模式;
C. MCU把CE置高(至少10us),激发Enhanced ShockBurstTM发射;
D. Enhanced ShockBurstTM发射:a)给射频前端供电;
b)射频数据打包(加字头、CRC校验码);
c)高速发射数据包;
d)发射完成,NRF24L01进入空闲状态

Enhanced ShockBurstTM接收流程:

A. 配置接收地址和要接收的数据包大小;
B. 配置CONFIG寄存器,使之进入接收模式,把CE置高;
C. 130us后,NRF24L01进入监视状态,等待数据包的到来;
D. 当接收到正确的数据包(正确的地址和CRC校验码),NRF24L01自动移去字头、地址和CRC校验位
E. NRF24L01通过把STATUS寄存器的RX_DR置位(STATUS一般引起MCU中断)通知MCU;
F. MCU把数据从FIFO读出(0x61指令);
G. 所有数据读取完毕后,可清除STATUS寄存器;NRF24L01可以进入四中主要的模式之一。

3. SPI指令

常用SPI指令

指令名称 指令格式 操作
R_REGISTER 000A AAAA 读寄存器。AAAAA为要读取的寄存器地址
W_REGISTER 001A AAAA 写寄存器。AAAAA为要写入的寄存器地址
R__RX_PAYLOAD 0110 0001 读 RX 有效数据,1-32字节。 读操作从字节0开始,当读RX有效数据完成后,FIFO寄存器中的使有效数据将被清除。接收模式下用。
W_TX_PAYLOAD 1010 0000 写TX有效数据,1-32字节。写操作从0字节开始,发送模式下用。
FLUSH_TX 1110 0001 清除TX FIFO寄存器。发送模式下用
FLUSH_RX 1110 0010 清除RX FIFO寄存器。接收模式下用

在这里插入图片描述

NRF24L01一对多通信方法

原文地址:
如果你还搞不通2个NRF24L01之间的通信,请看瑞生写的这两篇文章《新手如何快速搞通NRF24L01通信》《NRF24L01中文资料_原理_程序详解》

搞通了2个NRF24L01的互相通信,一对多实际上就非常容易了。你要记住,一对多通信,实际上同一时刻也是一对一通信。一次通信只需几个毫秒,如果一个和一个通信完以后,再和另外一个通信,然后再和另外一个通信……这中间如果没有延时程序的话,对于人的反应来说,就是同时进行的。这个原理就和操作系统一样,同一时刻,既可以打字聊天,又可以看电影。

下面视频是瑞生做的一个项目,是一对五,共用到6个NRF24L01模块,一个盒子里有一个。

视频地址:http://v.youku.com/v_show/id_XOTUwMDM4NTg4.html

上面这个视频的实例,如果只看NRF24L01的话,可以总结为下面的图示:
在这里插入图片描述
我们知道,2个NRF24L01通信需要具备4个条件设置相同。

  • 发射接收数据宽度相同(最大32个字节)
  • 发射接收地址相同(5个8位地址)
  • 发射接收频道相同(0~125)
  • 发射接收速率相同(2M 1M 250K)
    现在假设,主机和5个从机的这四个条件全部设置相同,如果主机发送数据的话,理论上5个从机会收到数据,实际上由于干扰,不会都收到。而且,这时候,如果从机1给主机发送数据的话,从机2~5也会收到数据。所以这样做是不行的!

一对多,有两种方式,一种是修改为不同的频道,一种是修改为不同的地址,也可以修改为不同的频道+不同的地址。

上面视频中用到的,是修改为不同的频道,例如,从机1的频道是10,从机2的频道是30,从机3的频道是50,从机4的频道是70,从机5的频道是90(注意,频道的值可以是0~125,共126个频道),频道接近的话,有可能出问题,例如从机1的频道是20,如果设置从机2的频道是21的话,主机给从机1发送数据时,从机2也有可能受到数据。所以要向上面一样,频道距离拉开一些。这时候,有人会产生疑问,如果我有几十个从机,频道岂不是很接近了,恭喜你,答对了。如果有几个从机的话,建议不要用不同的频道,而同一频道不同的地址,由于地址是5字节的,所以理论上可以有上亿个从机。

从机的频道定义好了,那么主机的程序该怎么写呢?这个很容易。例如,主机要和从机1通信,自己就把频道改为从机1一样的频道10,然后就可以发送数据,这时候,从机2~5由于频道不同,是收不到数据的。同样,主机要和从机2通信,就把自己的频道改为和从机2一样的频道30,然后就可以发送数据了。到了这个时候了,还会有童鞋问,我怎么就知道我要和谁通信?该怎么改呢?这样的童鞋还不少,那我就给你解释下吧。我都不好意思说了,太简单了。假设我的主机有5个按键,按下按键1,给从机1发送数据;按下按键2,给从机2发送数据……以此类推。那么,主机程序就是下面这个样子的:

if(KEY==0)//按下KEY1
{
  NRF_CE=0;	//拉低CE引脚
  NRF24L01_Write_Reg(WRITE_REG+RF_CH,10);//修改为从机1的频道
  NRF_CE=1;	//拉高CE引脚
  NRF24L01_TxPacket(rece_buf);//发送数据
}
else if(KEY2==0)//按下KEY2
{
  NRF_CE=0;	//拉低CE引脚
  NRF24L01_Write_Reg(WRITE_REG+RF_CH,30);//修改为从机2的频道
  NRF_CE=1;	//拉高CE引脚
  NRF24L01_TxPacket(rece_buf);//发送数据
}
else if(KEY2==0)//按下KEY3
{
  NRF_CE=0;	//拉低CE引脚
  NRF24L01_Write_Reg(WRITE_REG+RF_CH,50);//修改为从机3的频道
  NRF_CE=1;	//拉高CE引脚
  NRF24L01_TxPacket(rece_buf);//发送数据
}
else if(KEY2==0)//按下KEY4
{
  NRF_CE=0;	//拉低CE引脚
  NRF24L01_Write_Reg(WRITE_REG+RF_CH,70);//修改为从机4的频道
  NRF_CE=1;	//拉高CE引脚
  NRF24L01_TxPacket(rece_buf);//发送数据
}
else if(KEY2==0)//按下KEY5
{
  NRF_CE=0;	//拉低CE引脚
  NRF24L01_Write_Reg(WRITE_REG+RF_CH,90);//修改为从机5的频道
  NRF_CE=1;	//拉高CE引脚
  NRF24L01_TxPacket(rece_buf);//发送数据
}

话说到这份上,有的童鞋还会有疑问:“那如果我的5个从机要给主机发送数据怎么办?按照你上面的程序,主机的频道是不定的,一会儿这个,一会那个的,从机要想和主机通信的时候,我怎么判断现在主机的频道和现在从机的频道相同,不相同不能通信啊!”。真有童鞋这么问,还不是少数,这个问题,稍微动一下脑筋就想出来了。请看下面解决方案:

假设有5个大棚,1个机房,机房要无线采集5个大棚的温度。每个大棚里,都有一个电路板,板子上有温度传感器+单片机+NRF24L01,单片机通过温度传感器采集到温度以后,就通过NRF24L01发送到机房。这个例子,正好是上面童鞋问到的,大棚要给机房发送温度。

解决思路是:主机修改为从机1的频道以后,发送数据命令,从机收到命令后,发送温度数据给主机,主机再回应从机收到数据;然后主机再把频道修改为从机2的频道,发送数据命令,从机收到命令后,发送温度数据给主机,主机再回应从机收到数据….以此类推。

有的人听了以后,感觉好麻烦,实际上机房按照上面的描述收集5个机房的温度,也就是一眨眼的功夫。

假设机房每隔5分钟采集一次大棚温度,示例程序如下:

main()
{
  //各种初始化配置
  while(1)
  {
    delay()//延时5分钟(每5分钟采集一次数据)

    NRF_CE=0; //拉低CE引脚
    NRF24L01_Write_Reg(WRITE_REG+RF_CH,10);//修改为从机1的频道
    NRF_CE=1; //拉高CE引脚
    rece_buf[0]=0x66;//这是我规定的从机发送温度命令
    NRF24L01_TxPacket(rece_buf);//告诉从机我要你给我发温度数据
    NRF24L01_RX_Mode();// 配置为接收模式
    while(IRQ==1);//等待发来数据
    NRF24L01_RxPacket(rece_buf);//接收温度数据
    wendu[0]=rece_buf[1];//把采集到的温度数据给了wendu[0]变量
    NRF24L01_TX_Mode();//配置为发送模式
    rece_buf[0]=0x88;//这是我规定的主机接收到温度的回应数据
    NRF24L01_TxPacket(rece_buf);//告诉从机1我收到数据了

    NRF_CE=0; //拉低CE引脚
    NRF24L01_Write_Reg(WRITE_REG+RF_CH,30);//修改为从机2的频道
    NRF_CE=1; //拉高CE引脚
    rece_buf[0]=0x66;//这是我规定的从机发送温度命令
    NRF24L01_TxPacket(rece_buf);//告诉从机我要你给我发温度数据
    NRF24L01_RX_Mode();// 配置为接收模式
    while(IRQ==1);//等待发来数据
    NRF24L01_RxPacket(rece_buf);//接收温度数据
    wendu[1]=rece_buf[1];//把采集到的温度数据给了wendu[1]变量
    NRF24L01_TX_Mode();//配置为发送模式
    rece_buf[0]=0x88;//这是我规定的主机接收到温度的回应数据
    NRF24L01_TxPacket(rece_buf);//告诉从机1我收到数据了

    NRF_CE=0; //拉低CE引脚
    NRF24L01_Write_Reg(WRITE_REG+RF_CH,50);//修改为从机3的频道
    NRF_CE=1; //拉高CE引脚
    rece_buf[0]=0x66;//这是我规定的从机发送温度命令
    NRF24L01_TxPacket(rece_buf);//告诉从机我要你给我发温度数据
    NRF24L01_RX_Mode();// 配置为接收模式
    while(IRQ==1);//等待发来数据
    NRF24L01_RxPacket(rece_buf);//接收温度数据
    wendu[2]=rece_buf[1];//把采集到的温度数据给了wendu[2]变量
    NRF24L01_TX_Mode();//配置为发送模式
    rece_buf[0]=0x88;//这是我规定的主机接收到温度的回应数据
    NRF24L01_TxPacket(rece_buf);//告诉从机1我收到数据了

    NRF_CE=0; //拉低CE引脚
    NRF24L01_Write_Reg(WRITE_REG+RF_CH,70);//修改为从机4的频道
    NRF_CE=1; //拉高CE引脚
    rece_buf[0]=0x66;//这是我规定的从机发送温度命令
    NRF24L01_TxPacket(rece_buf);//告诉从机我要你给我发温度数据
    NRF24L01_RX_Mode();// 配置为接收模式
    while(IRQ==1);//等待发来数据
    NRF24L01_RxPacket(rece_buf);//接收温度数据
    wendu[3]=rece_buf[1];//把采集到的温度数据给了wendu[3]变量
    NRF24L01_TX_Mode();//配置为发送模式
    rece_buf[0]=0x88;//这是我规定的主机接收到温度的回应数据
    NRF24L01_TxPacket(rece_buf);//告诉从机1我收到数据了

    NRF_CE=0; //拉低CE引脚
    NRF24L01_Write_Reg(WRITE_REG+RF_CH,90);//修改为从机5的频道
    NRF_CE=1; //拉高CE引脚
    rece_buf[0]=0x66;//这是我规定的从机发送温度命令
    NRF24L01_TxPacket(rece_buf);//告诉从机我要你给我发温度数据
    NRF24L01_RX_Mode();// 配置为接收模式
    while(IRQ==1);//等待发来数据
    NRF24L01_RxPacket(rece_buf);//接收温度数据
    wendu[4]=rece_buf[1];//把采集到的温度数据给了wendu[4]变量
    NRF24L01_TX_Mode();//配置为发送模式
    rece_buf[0]=0x88;//这是我规定的主机接收到温度的回应数据
    NRF24L01_TxPacket(rece_buf);//告诉从机1我收到数据了
  }
}

nRF24L01通信的常识

  1. 用5根线的SPI接口向2401发送数据或指令。
  2. 芯片在每次上电的时候都需要进行一番配置。这些配置数据,就是所谓的指令了。
  3. 配置完成后知道芯片处在哪个模式。并且知道它将要转向哪个模式。
  4. 通讯协议。
  5. 观察现象。

好了,小牛们一定急切想知道自己的程序问题出在哪里了,再小白一点的一定想急切的知道如何配置才能让它工作。更小白一点的一定想知道这个芯片的各个模式之间是怎么联系的。又是如何进行相互转化的。别急,一个一个说。

1. 发送模式。

发送模式要经历几个变化。

  1. 上电,上电完了芯片其实还是在powerDown模式,因为芯片为了省电,它需要通过软件来控制开机或关机。powerDown模式就是所谓的待机模式了。这个模式理所当然是最省电的。它除了省电以外还能干吗呢?配置!对最重要的配置。就是可以通过SPI口向它发送数据,它是可以接收到的,并且可以正确写入到指定的寄存器中的。一般我们在初始化一开始就可以配置了,而此时芯片正是处在这个模式。

  2. 我们的目标是要把它配置的能发送数据,而且能被目标设备正确接收。所以这中间涉及的寄存器(当然是2401里自带的寄存器区了)有:

    1. 发送到的目标地址。 说地址其实是虚的,它就相当于一个钥匙和一把锁一样。当你在接收设备里边规定了一个地址以后,那么接收到的数据只有带了这个地址的包才会被正确接收。所以说这里的发送目标地址就是接收设备里的本机地址。只要设计时两个地址相同,就不会接收不到。
    2. @接收到的地址。 这个名字起的不是很好哈,有点误导人。说白了就是一个本机地址了。在纯发射机里,它并不是必要的。因为它从来不接收数据~~~这里写上,是因为:发射模式还有个东西,就是自动应答功能,(当然也是通过配置才能启用的,不配置则不会自动应答,也就不需要知道本机地址了)。
    3. 说到自动应答,那就先说它吧。一般我们会想,发送出一帧数据后是不是应该等待另一方回应个数据呀?想法是好的,但我说了,有通讯协议在控制。因为我们这里只想设计成一直发送而不管对方是否收到。所以自动应答可以不去配置,让它开机默认即可。但实际上我们的通讯协议可能很复杂,肯定不会是一直在发送。并且,它发送完一帧后,确实应该转为接收模式来等待对方发送应答数据过来。我们人为的当然可以自己来个模式跳转,但是有自动应答了,也就是说,当你设定开启自动应答了,那么在它发送完一包数据后,芯片会立即转为接收模式了。 《在这里我也仍有一点没理解,就是,当它收到应答后会变成什么模式呢,是自动再回到发送模式还是停留在接收模式。》
    4. 有自动应答了,那么就不能少了自动重发功能了。为什么呢。首先理解为什么会自动重发,就因为它处在自动应答模式时,通过接收数据来判定上次的数据是否有接收者成功接收了,就像我送出了一封信,如果你没有回信我是不是会想你可能没收到信,我需要再发一次。就是这个道理 。自动重发寄存器8位被分成两个4位的小区域,高4位存的是重发间隔时间,也就是说总共可以设定为16种不同的间隔,在这个间隔时间过后仍没有回应才再次重发。低4位好说,就是存一个最大重发次数。则最大可以设为15,即重发15次后仍然没回应那就不理你了,不再重发了。并且还会产生一个中断呢。最后再讲中断部分。 这里应该注意的是,当自动应答功能禁止时,就没有自动重发了,不管你设了重发多少次都不管用了。所以是互相影响的。还有就是重发次数设为0次,则相当于禁自动重发了,这不是显然的么。嘿嘿。
    5. 最基本的配置,,频率。这个芯片不光能以2.4Ghz的载波发射呢,它的带宽为2.4G----2.512G呢。这中间有一百多M的频带,可以划分成2M一个的信道。这个寄存器中的值就标明你想工作在哪个信道上啦,只有设置为同频的设备才能接收的到很容易理解吧。比如你设为10信道,则10*2=20M即发送时所用的载波频率就是2400+20=2420MHz。当然也只有设为2420M的接收设备才能接收的到了。
    6. 功率,数据速率。这是什么呢?原来2401里边也有自动增益控制部分,我们可以设定一个合适的功率来发射数据,第一可以在能耗上有利,另一方面对通信距离的控制也是很重要的。比如我只想在2米内能够通信,但2401最大的通信距离可以达到上百米,显然需要降发射功率来降低通信距离。可选的功率值有 0Dbm,-6dBm, -12dBm,-18dBm, 显然0在这里是最大的发射功率了。开发时尽可能设为0.如果设为0时,接收者都接收不到数据,那肯定是芯片坏了,就不用考虑降低功耗来通信了。这四档功率,通信距离从远到近,从几十米到几cm.并且在通信过程中还可以动态更改这个功率值,以达到最佳匹配的效果。数据速率是什么呢,刚开始我跟SPI接口的速率搞混了,想着,一个4M的单片机跟芯片通信速率怎么可能到这么高的。其实不是,这个速率就是加在载波上的数据的码率了。有两个可选值,1Mb/s,2Mb/s.也就是说,1秒钟能发送出去多少个高低电平。

以上这些配置命令之后就可以通过简单的指令转向发送模式了。下面就看一下,配置的伪指令吧:

  1. sendCommand(TxAddress_5) .因为地址是3到5字节可选的,这里选择了5字节的地址
  2. sendCommand(LocalAddress_5),本机地址如果想要开启自动应答则必需与发射地址相同,即LocalAddress==TxAddress…不开启则不需要相等。
  3. sendCommand(EnableAutoAck_0),因为芯片自带了6个通道,可以同时工作同时接收不同的数据,所以这里的自动应答也有相应的6个bit位来分别控制。这里只允许通道0自动应答。
  4. sendCommand(EnableAutoTransmit) .只要上边一句配置开了自动应答,这句就应答生效。即自动重发.
  5. sendCommand(RF_CH). 这一句设置发射频率。即设定信道。一般默认的就是0信道了,也即2.4G的载波。
  6. sendCommand(RF_Power). 设定发射速率,还有发射功率就设成0dBM吧。

以上这些是发射模式的必要配置了。如果不涉及接收,那么现在就可以转向发射模式。
7. sendCommand(Config); 通过 config寄存器中的开机控制位 powerup=1,转向空闲模式,config^0=0.转向发射模式。
8. sendCommand(TxBuffer);  填充发送数据到缓冲区里边。
9. CE=1;  这是一个引脚,用来控制从空闲模式向发送或接收模式跳转的.仅将相应位设为发送或接收模式不行,芯片现在只欠东风了,就是CE=1并保持最少10us时间后。就会开始发送数据了。

一个帧数据发送完成后会产生中断。这些中断是可以屏蔽的,就像单片机的中断允许控制一样。控制位在config寄存器中。

这里还可能产生另一个中断,就是重发次数达到上限了,你设了重发3次,那么重发3次以后还没收到应答就产生中断了。同样也是可屏蔽的。

如何使用多个NRF24L01模块搭建一个Arduino无线网络

原文地址:https://www.yiboard.com/thread-985-1-1.html
https://www.yiboard.com/thread-985-1-1.html
在本篇文章中,我们将学习如何搭建由多个NR24L01收发器模块组成的Arduino无线网络。在本示例中,我们搭建了一个由5个节点组成的网络,每个节点都可以与网络中的任何节点通信,同时它们既可以作为发送器,也可以作为接收器。实际上,该示例采用这种方式设置是解释如何搭建更大网络,或者准确地说,我们可以在单个RF信道上共有3125个模块相互通信。现在让我们来看看它是如何工作的。
在这里插入图片描述

在我之前的文章中,我们已经学习了如何使用NRF24L01模块和RF24库在两个Arduino开发板之间进行无线通信。现在除了这个库之外,我们还将使用RF24Network库,它可以轻松地构建一个无线网络,其中许多Arduino开发板相互通信。以下是网络拓扑的工作原理。

单个NRF24L01模块可以同时主动监听多达6个其他模块。
在这里插入图片描述
RF24网络库利用此能力生成以树形拓扑排列的网络,其中一个节点是基础,所有其他节点是该节点或另一个节点的子节点。每个节点最多可以有5个子节点,这可以达到5级深度,这意味着我们可以创建总共3125个节点的网络。每个节点必须使用15位地址来定义,该地址精确地描述树中节点的位置。
在这里插入图片描述

我们实际上可以用八进制格式定义节点的地址。因此,master或base的地址为00,base子地址为0105,01节点子地址为011051,依此类推。

注意,如果节点011想要与节点02通信,则通信将必须通过节点01和基节点00,因此这两个节点必须始终是活动的,以便通信成功。

在这里插入图片描述

使用RF24Network库的Arduino无线伺服电机控制

在我们开始本文的的主要示例之前,为了更好地理解库的工作方式,让我们举两个Arduinos相互通信的简单示例。这是此示例的电路图。

在这里插入图片描述

我们在使用第一个Arduino开发板上的电位计来控制第二个Arduino上的的伺服电机。现在来看看源代码。

以下是电位器侧的代码:

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
== Example 01 - Servo Control / Node 00 - Potentiometer ==
  by Dejan, www.HowToMechatronics.com
  Libraries:
  nRF24/RF24, https://github.com/nRF24/RF24
  nRF24/RF24Network, https://github.com/nRF24/RF24Network
*/
#include <RF24.h>
#include <RF24Network.h>
#include <SPI.h>
RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 00;   // Address of this node in Octal format ( 04,031, etc)
const uint16_t node01 = 01;      
void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node);  //(channel, node address)
}
void loop() {
  network.update();
  unsigned long potValue = analogRead(A0);  // Read the potentiometer value
  unsigned long angleValue = map(potValue, 0, 1023, 0, 180); // Convert the value to 0-180
  RF24NetworkHeader header(node01);     // (Address where the data is going)
  bool ok = network.write(header, &angleValue, sizeof(angleValue)); // Send the data
}

首先,我们需要包括RF24和RF24Network库以及SPI库。然后我们需要创建RF24对象,并将其包含在RF24Network对象中。这里我们需要以八进制格式定义节点的地址,也就是,为此节点定义00,为伺服侧的另一个节点定义01。

在setup()函数中,我们需要通过设置此节点的通道和地址来初始化网络。

在loop()函数中,我们需要经常调用update()函数,通过该函数发生网络中的所有操作。然后我们读取电位计的值并将其转换为0到180的值,这适用于伺服控制。然后我们创建一个网络数据头,我们在其中分配数据所在节点的地址。最后,使用write()函数将数据发送到另一个节点。这里第一个参数包含地址信息,第二个参数包含将被发送的数据,第三个参数是数据的大小。

以下是伺服电机端的代码:

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
  == Example 01 - Servo Control / Node 01 - Servo motor ==
*/
#include <RF24.h>
#include <RF24Network.h>
#include <SPI.h>
#include <Servo.h>
RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 01;   // Address of our node in Octal format ( 04,031, etc)
Servo myservo;  // create servo object to control a servo
void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node); //(channel, node address)
  myservo.attach(3);   // (servo pin)
}
void loop() {
  network.update();
  while ( network.available() ) {     // Is there any incoming data?
    RF24NetworkHeader header;
    unsigned long incomingData;
    network.read(header, &incomingData, sizeof(incomingData)); // Read the incoming data
    myservo.write(incomingData);  // tell servo to go to a particular angle
  }
}

在另一侧伺服电机上,我们需要以与前面所述相同的方式定义库和对象。 这里八进制格式的节点地址为01。定义伺服电机地址后,在loop()函数中,使用while()循环和available()函数,我们不断检查是否有数据输入。 如果为true,我们将创建一个网络数据头,通过该数据头接收数据,以及创建一个存储数据的变量。 然后使用read()函数读取数据,并将其存储到incomingData变量中。 最后,我们使用这些数据根据另一个节点的电位器移动伺服电机。

使用多个NRF24L01模块的Arduino无线网络

在理解了上个示例之后,我们可以继续学习本篇文章的主要示例,然后搭建一个由5个Arduino开发板相互通信的无线网络。 以下是该示例的框图。
在这里插入图片描述
因此,从基本节点开始,我们将使用电位器控制节点01处的伺服电机,使用第二个电位器,我们将控制节点022处的LED,使用按钮我们将控制节点012处的LED,基本节点的LED将使用节点02处的电位器进行控制。同样使用节点012处的红外传感器,我们将控制节点01处的LED。因此我们可以注意到该示例解释了如何同时发送和接收数据, 以及如何与来自不同分支的节点通信。 我们现在来看看Arduino代码。

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
          == Base/ Master Node 00==
  by Dejan, www.HowToMechatronics.com
  Libraries:
  nRF24/RF24, https://github.com/nRF24/RF24
  nRF24/RF24Network, https://github.com/nRF24/RF24Network
*/
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#define button 2
#define led 3
RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 00;   // Address of this node in Octal format ( 04,031, etc)
const uint16_t node01 = 01;      // Address of the other node in Octal format
const uint16_t node012 = 012;
const uint16_t node022 = 022;
void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node);  //(channel, node address)
  radio.setDataRate(RF24_2MBPS);
  pinMode(button, INPUT_PULLUP);
  pinMode(led, OUTPUT);
}
void loop() {
  network.update();
  //===== Receiving =====//
  while ( network.available() ) {     // Is there any incoming data?
    RF24NetworkHeader header;
    unsigned long incomingData;
    network.read(header, &incomingData, sizeof(incomingData)); // Read the incoming data
    analogWrite(led, incomingData);    // PWM output to LED 01 (dimming)
  }
  //===== Sending =====//
  // Servo control at Node 01
  unsigned long potValue = analogRead(A0);
  unsigned long angleValue = map(potValue, 0, 1023, 0, 180); // Suitable for servo control
  RF24NetworkHeader header2(node01);     // (Address where the data is going)
  bool ok = network.write(header2, &angleValue, sizeof(angleValue)); // Send the data
  // LED Control at Node 012
  unsigned long buttonState = digitalRead(button);
  RF24NetworkHeader header4(node012);    // (Address where the data is going)
  bool ok3 = network.write(header4, &buttonState, sizeof(buttonState)); // Send the data
  // LEDs control at Node 022
  unsigned long pot2Value = analogRead(A1);
  RF24NetworkHeader header3(node022);    // (Address where the data is going)
  bool ok2 = network.write(header3, &pot2Value, sizeof(pot2Value)); // Send the data
}

因此,在基本节点或主节点,我们需要如前所述定义库和对象,并定义主节点将向其发送数据的所有其他节点。 在loop()函数部分,我们首先不断检查是否有数据传入。 如果有,我们读取数据,将其存储到incomingData变量中,然后使用它来控制LED亮度。 这些数据实际上来自节点02的电位器。如果我们看看它的代码,我们可以注意到设置几乎是一样的。 重要的是将正确的地址分配给我们想要发送数据的位置。 本例中是主地址00。所以在读取电位器值并将其转换为0到255的合适PWM值后,我们将这些数据发送给主机。 我们在这里可以注意到我使用millis()函数以10毫秒的间隔发送数据。

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
        == Node 02 (Child of Master node 00) ==    
*/
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 02;   // Address of our node in Octal format ( 04,031, etc)
const uint16_t master00 = 00;    // Address of the other node in Octal format
const unsigned long interval = 10;  //ms  // How often to send data to the other unit
unsigned long last_sent;            // When did we last send?
void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node);  //(channel, node address)
  radio.setDataRate(RF24_2MBPS);
}
void loop() {
  network.update();
  //===== Sending =====//
  unsigned long now = millis();
  if (now - last_sent >= interval) {   // If it's time to send a data, send it!
    last_sent = now;
    unsigned long potValue = analogRead(A0);
    unsigned long ledBrightness = map(potValue, 0, 1023, 0, 255);
    RF24NetworkHeader header(master00);   // (Address where the data is going)
    bool ok = network.write(header, &ledBrightness, sizeof(ledBrightness)); // Send the data
  }
}

接下来,我们从主机发送电位器数据到节点01,以控制伺服电机。

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
        == Node 02 (Child of Master node 00) ==
*/
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#include <Servo.h>
#define led 2
RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 01;   // Address of our node in Octal format ( 04,031, etc)
const uint16_t master00 = 00;    // Address of the other node in Octal format
Servo myservo;  // create servo object to control a servo
void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node); //(channel, node address)
  radio.setDataRate(RF24_2MBPS);
  myservo.attach(3);   // (servo pin)
  pinMode(led, OUTPUT);
}
void loop() {
  network.update();
  //===== Receiving =====//
  while ( network.available() ) {     // Is there any incoming data?
    RF24NetworkHeader header;
    unsigned long incomingData;
    network.read(header, &incomingData, sizeof(incomingData)); // Read the incoming data
    if (header.from_node == 0) {    // If data comes from Node 02
      myservo.write(incomingData);  // tell servo to go to a particular angle
    }
    if (header.from_node == 10) {    // If data comes from Node 012
      digitalWrite(led, !incomingData);  // Turn on or off the LED 02
    }
  }
}

节点01实际上是从两个不同的节点接收数据,一个用于伺服控制,另一个用于来自节点012的红外传感器的LED控制。

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
            == Node 012 (child of Node 02)==    
*/
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#define led 2
#define IR 3
RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 012;  // Address of our node in Octal format ( 04,031, etc)
const uint16_t node01 = 01;    // Address of the other node in Octal format
void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node);  //(channel, node address)
  radio.setDataRate(RF24_2MBPS);
  pinMode(led, OUTPUT);
  pinMode(IR, INPUT);
}
void loop() {
  network.update();
  //===== Receiving =====//
  while ( network.available() ) {     // Is there any incoming data?
    RF24NetworkHeader header;
    unsigned long buttonState;
    network.read(header, &buttonState, sizeof(buttonState)); // Read the incoming data
    digitalWrite(led, !buttonState); // Turn on or off the LED
  }
  //===== Sending =====//
  unsigned long irV = digitalRead(IR); // Read IR sensor
  RF24NetworkHeader header8(node01);
  bool ok = network.write(header8, &irV, sizeof(irV)); // Send the data
}

在本段代码中,我们使用header.from_node属性来获取数据来自哪个节点的信息。 如果输入数据来自主设备,我们用它来控制伺服,如果输入数据来自节点012,我们用它来控制LED。

在节点012,我们有发送和接收。 红外传感器控制节点01处的前面提到的LED,这里的LED是来自主机上的按钮的控制。

/*
  Arduino Wireless Network - Multiple NRF24L01 Tutorial
            == Node 022 (child of Node 02)==    
*/
#include <RF24Network.h>
#include <RF24.h>
#include <SPI.h>
#define led1 2
#define led2 3
#define led3 4
#define led4 5
RF24 radio(10, 9);               // nRF24L01 (CE,CSN)
RF24Network network(radio);      // Include the radio in the network
const uint16_t this_node = 022;  // Address of our node in Octal format ( 04,031, etc)
const uint16_t master00 = 00;    // Address of the other node in Octal format
void setup() {
  SPI.begin();
  radio.begin();
  network.begin(90, this_node);  //(channel, node address)
  radio.setDataRate(RF24_2MBPS);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
  pinMode(led4, OUTPUT);
}
void loop() {
  network.update();
  //===== Receiving =====//
  while ( network.available() ) {     // Is there any incoming data?
    RF24NetworkHeader header;
    unsigned long potValue;
    network.read(header, &potValue, sizeof(potValue)); // Read the incoming data
    // Turn on the LEDs as depending on the incoming value from the potentiometer
    if (potValue > 240) {
      digitalWrite(led1, HIGH);
    } else {
      digitalWrite(led1, LOW);
    }
    if (potValue > 480) {
      digitalWrite(led2, HIGH);
    } else {
      digitalWrite(led2, LOW);
    }
    if (potValue > 720) {
      digitalWrite(led3, HIGH);
    } else {
      digitalWrite(led3, LOW);
    }
    if (potValue > 960) {
      digitalWrite(led4, HIGH);
    } else {
      digitalWrite(led4, LOW);
    }
  }
}

最后,使用来自主机上的另一个电位器的数据来控制节点022处的LED。

因此,总而言之,如果一切正常连接,并且所有节点始终处于活动状态,通过精确寻址节点就可以完成我们的工作,所有繁重的工作都由强大的RF24Network库执行。

以上就是本篇文章的全部内容,希望您能喜欢这篇Arduino文章,并能学到一些新东西。 如果遇到任何问题,请随时在下面进行回复。

如何使用Arduino UNO、操纵杆模块和NRF24L01模块控制伺服电机

本文转自:https://www.yiboard.com/thread-1095-1-1.html
基于Arduino和NRF24L01模块制作发射器和接收器,实现使用操纵杆控制伺服电机!
在这里插入图片描述

在本篇文章中,我们主要介绍如何使用NRF24L01和Arduino控制伺服电机。我们在发送端移动操纵杆,使用NRF24L01,将操纵杆移动的值发送到接收侧,然后在接收侧接收该值,使用此值移动伺服电机。

1. 必需的组件

  • Arduino开发板
  • NRF24L01模块
  • 操纵杆模块
  • 伺服电机
  • 连接导线

2. 伺服电机控制如何工作

在发送侧,我们有一个操纵杆模块、Arduino和NRF24L01,而在接收侧,我们有一个NRF24L01、Arduino和一个伺服电机。

当我们在水平方向上移动操纵杆时,操纵杆模块将向Arduino发送模拟值。我们已将NRF24L01模块设置为发送模式,并将操纵杆移动值发送到特定地址。

在接收侧,将NRF24L01模块设置为接收模式。我们在接收端给出了相同的地址,其他NRF24L01模块正在传输数据。因此,只要模块接收到数据,Arduino就会读取数据并根据它移动伺服电机。 在这里插入图片描述

3. NRF24L01引脚说明

该模块的功耗非常低。它在传输过程中消耗大约12mA的功率,甚至低于LED。

该模块工作在3.3V,因此不要将其直接连接到5V的Arduino,因为它可能会损坏。 NRF24L01模块的其他引脚具有5V容限,因此您可以将它们直接连接到Arduino。

SCK、MOSI和MISO引脚用于SPI通信,CSN和CE引脚用于设置待机或活动模式以及设置发送或命令模式。 在这里插入图片描述

4. 电路原理图

连接有点冗长,因此我将分别介绍发送器和接收器的连接。

在发送器侧,NRF24L01与Arduino的连接如下:

  • 将NRF24L01的3.3V引脚与Arduino的3.3V连接
  • 将NRF24L01的GND引脚与Arduino的GND连接
  • 将NRF24L01的CSN引脚与Arduino的引脚8连接
  • 将NRF24L01的CE引脚与Arduino的引脚7连接
  • 将NRF24L01的SCK引脚与Arduino的引脚13连接
  • 将NRF24L01的MOSI引脚与Arduino的引脚11连接
  • 将NRF24L01的MISO引脚与Arduino的引脚12连接

然后将操纵杆模块与Arduino连接如下:

  • 操纵杆模块的VCC到Arduino的5V
  • 操纵杆模块的GND到Arduino的GND
  • 操纵杆模块的VER到Arduino的A1
  • 操纵杆模块的HOR到Arduino的A0

在接收器侧,NRF24L01与Arduino的连接与发送器侧的连接相同。使用Arduino连接伺服电机如下:

  • 红线到Arduino的5V
  • 棕色线到Arduino的GND
  • 黄色线到Arduino的引脚6 在这里插入图片描述

5. 代码介绍

首先,包括NRF24L01和伺服电机的库

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Servo.h>

然后,定义我们连接NRF24L01的CSN和CE引脚的引脚。之后,我们初始化将发送和接收数据的地址。该地址在发射机和接收机侧应该相同。该地址可以是任何五个字母的字符串。

RF24 radio(7, 8); // CSN, CE
const byte address[6] = "00001";

在发送器的setup()函数中,我们设置了发送数据的地址。然后将功率放大范围设置为最小,因为模块彼此很接近。

radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);

在接收器侧,我们使用以下命令并设置模块以从该地址接收数据。

radio.openReadingPipe(0, address);

在发送器的loop()函数中,我们从操纵杆模块读取并在我们之前设置的地址发送值。

radio.write(&x_pos, sizeof(x_pos));

接收器侧的以下命令将从发送器获取数据,并且在将数据映射到0-180之后,我们将移动伺服电机。

radio.read(&x_pos, sizeof(x_pos));

发送器和接收器的完整代码如下所示: https://download.csdn.net/download/acktomas/11114570

猜你喜欢

转载自blog.csdn.net/acktomas/article/details/89296317
今日推荐