全部的使用Arduino 的朋友大多都会知道大名鼎鼎的XBee 这个土豪级的ZigBee 的通讯模块。咱们是作产品开发的,对于XBee这个产品可谓是又爱又恨,不得不认可他确实是一个好货,从作工到功能都无须质疑,让人最感到遗憾的是他并不太适合于作平民化的产品,¥150~¥300 多的集价只能让咱们对它望而轻叹了,这货只能用来DIY玩一下,这样的售价在产品上应有将直接将产品的成本推到难以承受的地步,因此它必须被取代!php
疯狂地google后终于也让咱们找到了取代这个必备级土豪的最佳解决方案,开始还真是没低,但后来经多番在google上翻阅老外的博客后也肯定了这一点,老外都以为XBee贵!这个解决方案就是基于 nRF24L01 的 2.4G通讯方案,它在淘宝上的售价极低,看了他的价格,若是在没有特殊状况下都没人再原意选择 ZigBee 和 蓝牙了吧,不相信?去淘一下就知道我是否言过其实。前端
好吧,废话很少说,先来看看 nRF23L01的介绍,(如下的文字来源于某厂商的文档资料,具体的文件请看附件)git
NRF24L01是一款工做在2.4-2.5GHz世界通用ISM频段的单片收发芯片,无线收发器包括:频率发生器 加强型 SchockBurstTM 模式控制器 功率放大器 晶体放大器 调制器 解调器 输出功率频道选择和协议的设置能够经过SPI接口进行设置极低的电流消耗,当工做在发射模式下发射功率为6dBm时电流消耗为9.0mA 接受模式为12.3mA掉电模式和待机模式下电流消耗模式更低。github
2.4Ghz
全球开放 ISM 频段免许可证使用2Mbps
,高效 GFSK 调制,抗干扰能力强,特别 适合工业控制场合1.9 - 3.6V
工做,待机模式下状态为 22uA
;掉电模 式下为 900nA
2.54MM
间距接口,DIP 封装10mA
,须要串联NRF2401 有工做模式有五种:编程
收发模式有 Enhanced ShockBurstTM 收发模式、ShockBurstTM 收 发模式和直接收发模式三种,收发模式由器件配置字决定,具体配置 将在器件配置部分详细介绍。数组
Enhanced ShockBurstTM 收发模式下,使用片内的先入先出堆栈区, 数据低速从微控制器送入,但高速(1Mbps)发射,这样能够尽可能节能, 所以,使用低速的微控制器也能获得很高的射频数据发射速率。与射 频协议相关的全部高速信号处理都在片内进行,这种作法有三大好处: 尽可能节能;低的系统费用(低速微处理器也能进行高速射频发射);数 据在空中停留时间短,抗干扰性高。Enhanced ShockBurstTM 技术同时也减少了整个系统的平均工做电流。less
在 Enhanced ShockBurstTM 收发模式下, NRF24L01 自动处理字头
和 CRC 校验码。在接收数据时,自动把字头和 CRC 校验码移去。在送 数据时,自动加上字头和 CRC 校验码,在发送模式下,置 CE 为高,至 少 10us,将时发送过程完成后。函数
130us
后,NRF24L01 进入监视状态,等待数据包的到来; D.收到 正确的数据包(正确的地址和 CRC 校验码),NRF2401 自动把字头、地址ShockBurstTM 收发模式能够与 Nrf2401a,02,E1 及 E2 兼容oop
NRF24L01 的空闲模式是为了减少平均工做电流而设计,其最大的 优势是,实现节能的同时,缩短芯片的起动时间。在空闲模式下,部分片内晶振仍在工做,此时的工做电流跟外部晶振的频率有关。ui
在关机模式下,为了获得最小的工做电流,通常此时的工做电流为 900nA
左右。关机模式下,配置字的内容也会被保持在 NRF2401 片内, 这是该模式与断电状态最大的区别。
NRF2401 的全部配置工做都是经过 SPI 完成,共有 30 字节的配置字。
官方推荐 NRF24L01 工做于 Enhanced ShockBurstTM 收发模式,这 种工做模式下,系统的程序编制会更加简单,而且稳定性也会更高, 所以,下文着重介绍把 NRF24L01 配置为 Enhanced ShockBurstTM 收发 模式的器件配置方法。
ShockBurstTM 的配置字使 NRF24L01 可以处理射频协议,在配置完 成后,在 NRF24L01 工做的过程当中,只需改变其最低一个字节中的内容, 以实现接收模式和发送模式之间切换。
ShockBurstTM 的配置字能够分为如下四个部分:
当使用 NRF24L01 片内的 CRC 技术时,要确保在配置字(CONFIG 的 EN_CRC)
中 CRC 校验被使能,而且发送和接收使用相同的协议。 NRF24L01 配置字的 CONFIG 寄存器的位描述以下表所示。
工做模式由 PWR_UP register 、PRIM_RX register 和 CE 决定,下表
从开始到如今可能你一直纳闷为何一直没有看到哪里说这NRF24L01能够与Arduino吧,是的 NRF24L01是在STC和51单片机上的应用不少,开始时我也一直很担忧他可否与Arduino兼容,不过从他的通讯原来看他原本就是使用SPI接口来通讯,而只要支持SPI的板子,只要原理相通那就应该能够与Arduino共同工做。在Github上狂扫一次,证实了个人想法是对的。这里要感谢那些为NRF24L01编写使用库的贡献者们,没有他们那就没法将这么好的板子用在Arduino上了,在Github上只要搜一下NRF24L01会找到不少的repository, 要NRF24L01只须要在程序中引入一个为NRF24L01写的库就好了。 我是使用这个库的:https://github.com/aaronds/arduino-nrf24l01。
下载库后将其解压至arduino 的libraries目录下便可使用。
如下一项使用两块Arduino 模块 + nRF24L01 实现2.4G的双向通讯实验,旨在实现普通的2.4G 遥控发射器与遥控接收器。
此实验中必须注意如下事项,不然可能出现烧坏模块的风险或没法正确完成实验。
基准电压采用3.3v,可从Arduino 的3.3v输出获取。此处须要说明的是若是使用 Arduino pro min 5v 16MHZ 的板子进行实验可链接FT232RL的USB转TTL的板子,因为 Arduino pro min 上只有一个电压输出,所以3.3v电压要从USB转TTL板上获取。
如图所示,只有让Arduino和nRF24L01 同源同压降能够避免另外加装电平转换板匹配Arduino的输出电平电压与nRF24L01的输入电平电压的麻烦。
另外,如采用3.7v电池供电时须要将nRF24L01的,电源输入VCC端接一阻值为20K
的电阻,将nRF2401的工做电压稳定在 2.4v
避免nRF24L01(1.8v~3.6v)超压工做损坏板子。
当将Arduino pro mini转用3.3v供电时能够从Arduino IDE中选择 Arduino pro min 3.3v 8MHZ 的驱动写板,这是一次失误中获取的经验,若是采用了这个选项向板子写入程序不会报错,且会显示成功。可是,写入的程序将会以 8Mhz
的时钟频率运行,这一点甚为之使人不解,所以即便使用3.3v供电也应该采用Arduino pro min 5v 16MHZ 的驱动写板。做此注解的缘由是要明确指出,发射板与接收板的工做时钟频率必定要保持一至,不然能够通讯成功但数据将会出现乱码。
此实验有两个重大的意义:
须要链接两件Arduino 板子,一为发射机(Teransmiter),一为接收机 (Receiver),Arduino 与 nRF24L01 的接线是固定的,在程序中无须要从新对 Arduino 的针脚进行定义,由于引入的Mrif
库会提供默认定义,Arduino 的默认定义以下,(此为固定的定义不可修改):
如下是Arduino 与 NRF24L01链接的针口对照表,下文的代码会按此针脚链接编写:
功能口 | NRF2401 | Arduino |
---|---|---|
MISO | 7 | 12 |
MOSI | 6 | 11 |
SCK | 5 | 13 |
CE | 3 | 8 |
CSN | 4 | 7 |
VCC | 2 | 3V3 |
GND | 1 | GND |
IRQ* | 8 | 0 |
将对应的针脚链接到nRF24L01中,完成接线。
发射机还须要使用JoyStick模块,此模块就是两个10K可调电位器与一个微触开关构成,既有模拟数据输入也有数字输入。针脚与Arduino 的链接以下:
A0
A1
如下的示例只是从客户端向指定的服务端发送当前的时间,而后服务端收到数据后做出响应,将原数据包发回给客户端。这个示例是很经常使用的,由于两个设备在工做前一般都须要进行匹配,用通讯的术语来讲就是“握手”,如下是 客户端的代码:
发射机代码:
/** * Transmitter 发射机 (遥控器) * * Pins: * Hardware SPI: * MISO -> 12 * MOSI -> 11 * SCK -> 13 * * Configurable: * CE -> 8 * CSN -> 7 * */ #include <SPI.h> #include <Mirf.h> #include <nRF24L01.h> #include <MirfHardwareSpiDriver.h> const int VRX=A0; const int VRY=A1; const int SW = 5; const int X0=525; const int Y0=496; byte joyStick[2]; void setup(){ Serial.begin(9600); pinMode(SW, INPUT); pinMode(VRX, INPUT); pinMode(VRY, INPUT); digitalWrite(SW, LOW); Mirf.spi = &MirfHardwareSpi; Mirf.init(); Mirf.setRADDR((byte *)"clie1"); Mirf.payload = sizeof(joyStick); Mirf.config(); Serial.println("Transmitter ready"); } void loop() { int val_x =map(analogRead(VRX),0,1024,0,255); int val_y=map(analogRead(VRY),0,1024,0,255); //技巧:有多少个数就用多少个数组单元去储存 joyStick[0] = val_x; joyStick[1] = val_y; //这个时间值用于判断是否链接超时 //unsigned long time = millis(); //long time_long=long(time); Serial.print("X:"); Serial.print(val_x); Serial.print(" Y:"); Serial.println(val_y); //设置当前发射器的地址 Mirf.setTADDR((byte *)"serv1"); //将读入的值写到SPI中 Mirf.send((byte *) &joyStick); while(Mirf.isSending()) { // 若是正在发送时什么都不作,这里只是一个等待,等isSending完成后才能退出循环 } }
这个发射机代码极为之简单,如下对其进行逐一分解诠释:
setup()
Mirf
在 setup()
函数中有两个地方须要注意:
setRADDR()
函数须要设定当前发射机的地址 clie1
这个地址是随机定的,但也是惟一用于此机上的地址,要与此机通讯则须要这个地址。Mirf.payload=sizeof(joyStick)
而非 Mirf.payload=sizeof(unsigned long)
loop()
这里采用 map()
函数,将 0~1024 之间的值映射成 0~255的值域,由于接收机的模拟输出范围也只有255,另外包中的每一位,即joyStick
的每一个元素的值域是 0~255 的8位整数,因此必须先进行此转换。
接下来是采用 setTADDR()
指定目标收受器的地址,调用 send
方法送数据。
另外 此处的 while
循环是一个死循环,若是发送未能完成(Mirf.isSending()==false)将会一直在此周期内不退出,固然死循环是不会出现的,由于硬件上会自动置位isSending()
函数也会返会flase
。
全部代码以下:
/** * 接收机 * * Pins: * Hardware SPI: * MISO -> 12 * MOSI -> 11 * SCK -> 13 * * Configurable: * CE -> 8 * CSN -> 7 * * Note: To see best case latency comment out all Serial.println * statements not displaying the result and load * 'ping_server_interupt' on the server. */ #include <SPI.h> #include <Mirf.h> #include <nRF24L01.h> #include <MirfHardwareSpiDriver.h> byte joyStick[2]; void setup() { Serial.begin(9600); Mirf.spi = &MirfHardwareSpi; Mirf.init(); Mirf.setRADDR((byte *)"serv1"); Mirf.payload = sizeof(joyStick); /* * Write channel and payload config then power up reciver. */ /* * To change channel: * * Mirf.channel = 10; * * NB: Make sure channel is legal in your area. */ Mirf.config(); Serial.println("Receiver ready"); } void loop() { while(!Mirf.dataReady()){ } Mirf.getData((byte *) &joyStick); int vx=joyStick[0]; int vy=joyStick[1]; if (vx < 130) { } // step forward if (vx > 130) { } // step backward if (vy < 123) { } // step left if (vy > 123) { } // step right Serial.print("X:"); Serial.print(vx); Serial.print(" Y:"); Serial.println(vy); }
此处代码就不做过多解释,在了解完发射机原理这里的原理是一至的,只是过程相反,用while
循环来等待数据接收完成,并从具备相同位数的 joyStick
数据缓冲中取出数组。
值得注意的是这里有两个常量:130
与 123
,其实这两个值是对控制杆上取得的中位0值,理论值应该是255的一半也就是128,但板子没有这么精确,这里只是取出这个两相对的中位0值。
这里有4个if
语句,做用是将两路的VRX和VRY信号分拆为VRF(前),VRB(后),VRL(左),VRR(右),代码中还有一个逻辑还没有写入,就是须要将VRF,VRB,VRL,VRR的位移量计算出来。公式以下:
说明:
XZero=130
,YZero=123
若是须要向接收机发送更为精确的控制值,可在发射机读入 VRX和VRY后就开始分解并计算出VRF,VRB,VRL,VRR而后进行8位数映射再传送至接收机,,这样能够更为精确地计算出位移量,而不至于在转换时丢失。