全部的使用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位數映射再傳送至接收機,,這樣能夠更爲精確地計算出位移量,而不至於在轉換時丟失。