nrf2401 - 最廉價的2.4G無線通訊方案

nRF2401

全部的使用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

  1. 2.4Ghz 全球開放 ISM 頻段免許可證使用
  2. 最高工做速率 2Mbps,高效 GFSK 調製,抗干擾能力強,特別 適合工業控制場合
  3. 126 頻道,知足多點通訊和跳頻通訊須要
  4. 內置硬件 CRC 檢錯和點對多點通訊地址控制
  5. 低功耗 1.9 - 3.6V 工做,待機模式下狀態爲 22uA;掉電模 式下爲 900nA
  6. 內置 2.4Ghz 天線,體積種類多樣
  7. 模塊可軟件設地址,只有收到本機地址時纔會輸出數據(提 供中斷指示),可直接接各類單片機使用,軟件編程很是方便
  8. 內置專門穩壓電路,使用各類電源包括 DC/DC 開關電源均有 很好的通訊效果
  9. 2.54MM 間距接口,DIP 封裝
  10. 工做於 Enhanced ShockBurst 具備 Automatic packet handling, Auto packet transaction handling,具備可選的內置包 應答機制,極大的下降丟包率。
  11. 與 51 系列單片機 P0 口鏈接時候,須要加 10K 的上 拉電阻,與其他口鏈接不須要。
  12. 其餘系列的單片機,若是是 5V 的,請參考該系列單片機 IO 口輸出電流大小,若是超過 10mA,須要串聯
    電阻分壓,不然容易燒燬模塊! 若是是3.3V的,能夠 直接和RF24l01模塊的IO口線鏈接。 好比AVR系列單片機
    若是是5V 的,通常串接2K 的電阻

工做方式

NRF2401 有工做模式有五種:編程

  • 收發模式
  • 配置模式
  • 空閒模式
  • 關機模式

收發模式

收發模式有 Enhanced ShockBurstTM 收發模式、ShockBurstTM 收 發模式和直接收發模式三種,收發模式由器件配置字決定,具體配置 將在器件配置部分詳細介紹。數組

Enhanced ShockBurstTM 收發模式

Enhanced ShockBurstTM 收發模式下,使用片內的先入先出堆棧區, 數據低速從微控制器送入,但高速(1Mbps)發射,這樣能夠儘可能節能, 所以,使用低速的微控制器也能獲得很高的射頻數據發射速率。與射 頻協議相關的全部高速信號處理都在片內進行,這種作法有三大好處: 儘可能節能;低的系統費用(低速微處理器也能進行高速射頻發射);數 據在空中停留時間短,抗干擾性高。Enhanced ShockBurstTM 技術同時也減少了整個系統的平均工做電流。less

在 Enhanced ShockBurstTM 收發模式下, NRF24L01 自動處理字頭
和 CRC 校驗碼。在接收數據時,自動把字頭和 CRC 校驗碼移去。在送 數據時,自動加上字頭和 CRC 校驗碼,在發送模式下,置 CE 爲高,至 少 10us,將時發送過程完成後。函數

Enhanced ShockBurstTM 發射流程

  1. 把接收機的地址和要發送的數據按時序送入 NRF24L01;
  2. 配置 CONFIG 寄存器,使之進入發送模式。C. 微控制器把 CE 置高 (至少 10us),激 發 NRF24L01 進行 Enhanced ShockBurstTM 發射; D. N24L01 的 Enhanced ShockBurstTM 發射(1) 給射頻前端供電; (2) 射頻數據打包(加字頭、CRC 校驗碼); (3) 高速發射數據包; (4) 發射完成,NRF24L01 進入空閒狀態。4.1.1.2 Enhanced ShockBurstTM 接收流程 A. 配置本機地址和要接收的數據包大小;B. 配置 CONFIG 寄存器,使之進入接收模式,把 CE 置高。
  3. 130us 後,NRF24L01 進入監視狀態,等待數據包的到來; D.收到 正確的數據包(正確的地址和 CRC 校驗碼),NRF2401 自動把字頭、地址
    和 CRC 校驗位移去;
  4. NRF24L01 經過把 STATUS 寄存器的 RX_DR 置位(STATUS 通常引發微
    控制器中斷)通知微控制器;F. 微控制器把數據從 NewMsg_RF2401 讀 出;
  5. 全部數據讀取完畢後,能夠清除 STATUS 寄存器。NRF2401 能夠進入 [ShockBurstTM 收發模式]

ShockBurstTM 收發模式能夠與 Nrf2401a,02,E1 及 E2 兼容oop

空閒模式

NRF24L01 的空閒模式是爲了減少平均工做電流而設計,其最大的 優勢是,實現節能的同時,縮短芯片的起動時間。在空閒模式下,部分片內晶振仍在工做,此時的工做電流跟外部晶振的頻率有關。ui

關機模式

在關機模式下,爲了獲得最小的工做電流,通常此時的工做電流爲 900nA 左右。關機模式下,配置字的內容也會被保持在 NRF2401 片內, 這是該模式與斷電狀態最大的區別。

配置 NRF24L01 模塊

NRF2401 的全部配置工做都是經過 SPI 完成,共有 30 字節的配置字。
官方推薦 NRF24L01 工做於 Enhanced ShockBurstTM 收發模式,這 種工做模式下,系統的程序編制會更加簡單,而且穩定性也會更高, 所以,下文着重介紹把 NRF24L01 配置爲 Enhanced ShockBurstTM 收發 模式的器件配置方法。

ShockBurstTM 的配置字使 NRF24L01 可以處理射頻協議,在配置完 成後,在 NRF24L01 工做的過程當中,只需改變其最低一個字節中的內容, 以實現接收模式和發送模式之間切換。

ShockBurstTM 的配置字能夠分爲如下四個部分:

  • 數據寬度:聲明射頻數據包中數據佔用的位數。這使得 NRF24L01 可以區分接收數據包中的數據和 CRC 校驗碼;
  • 地址寬度:聲明射頻數據包中地址佔用的位數。這使得 NRF24L01 可以區分地址和數據;
  • 地址:接收數據的地址,有通道 0 到通道 5 的地址;
  • CRC:使 NRF24L01 可以生成 CRC 校驗碼和解碼。

當使用 NRF24L01 片內的 CRC 技術時,要確保在配置字(CONFIG 的 EN_CRC)
中 CRC 校驗被使能,而且發送和接收使用相同的協議。 NRF24L01 配置字的 CONFIG 寄存器的位描述以下表所示。

工做模式由 PWR_UP register 、PRIM_RX register 和 CE 決定,下表

Arduino 上的實驗

從開始到如今可能你一直納悶爲何一直沒有看到哪裏說這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板上獲取。

IMG_nRF24L01_jumper

如圖所示,只有讓Arduino和nRF24L01 同源同壓降能夠避免另外加裝電平轉換板匹配Arduino的輸出電平電壓與nRF24L01的輸入電平電壓的麻煩。

另外,如採用3.7v電池供電時須要將nRF24L01的,電源輸入VCC端接一阻值爲20K的電阻,將nRF2401的工做電壓穩定在 2.4v 避免nRF24L01(1.8v~3.6v)超壓工做損壞板子。

Arduino 的時鐘頻率

當將Arduino pro mini轉用3.3v供電時能夠從Arduino IDE中選擇 Arduino pro min 3.3v 8MHZ 的驅動寫板,這是一次失誤中獲取的經驗,若是採用了這個選項向板子寫入程序不會報錯,且會顯示成功。可是,寫入的程序將會以 8Mhz 的時鐘頻率運行,這一點甚爲之使人不解,所以即便使用3.3v供電也應該採用Arduino pro min 5v 16MHZ 的驅動寫板。做此註解的緣由是要明確指出,發射板與接收板的工做時鐘頻率必定要保持一至,不然能夠通訊成功但數據將會出現亂碼。

意義

此實驗有兩個重大的意義:

  1. 提供基於2.4G廉價通訊方案的
  2. 找到基於下位機的數據傳輸格式
  3. 爲咱們之後的無線產品增長傳統遙控器的操控方案

實驗過程

須要鏈接兩件Arduino 板子,一爲發射機(Teransmiter),一爲接收機 (Receiver),Arduino 與 nRF24L01 的接線是固定的,在程序中無須要從新對 Arduino 的針腳進行定義,由於引入的Mrif庫會提供默認定義,Arduino 的默認定義以下,(此爲固定的定義不可修改):

  • MISO -> 12
  • MOSI -> 11
  • SCK -> 13
  • CE -> 8
  • CSN -> 7

如下是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引線

將對應的針腳鏈接到nRF24L01中,完成接線。

發射機

發射機還須要使用JoyStick模塊,此模塊就是兩個10K可調電位器與一個微觸開關構成,既有模擬數據輸入也有數字輸入。針腳與Arduino 的鏈接以下:

  • GND -> 連地線
  • +5V -> 主供電
  • VRX -> X軸輸出,鏈接Arduino的 A0
  • VRY -> Y軸輸出,鏈接Arduino的 A1
  • SW -> Swither 微動按鍵,接數字輸入 6

如下的示例只是從客戶端向指定的服務端發送當前的時間,而後服務端收到數據後做出響應,將原數據包發回給客戶端。這個示例是很經常使用的,由於兩個設備在工做前一般都須要進行匹配,用通訊的術語來講就是「握手」,如下是 客戶端的代碼:

發射機代碼:

/**
 * 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()

Mirfsetup()函數中有兩個地方須要注意:

  1. setRADDR() 函數須要設定當前發射機的地址 clie1 這個地址是隨機定的,但也是惟一用於此機上的地址,要與此機通訊則須要這個地址。
  2. 從軟件概念上 發射機 Transmiter 則是 通訊 客戶端這點須要緊記。
  3. 當採用數據包通訊而不是指令通訊時須要先定義數據包的大小,即 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

接收機 Receiver

全部代碼以下:

/**
 * 接收機
 *
 * 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 數據緩衝中取出數組。

值得注意的是這裏有兩個常量:130123,其實這兩個值是對控制桿上取得的中位0值,理論值應該是255的一半也就是128,但板子沒有這麼精確,這裏只是取出這個兩相對的中位0值。

這裏有4個if語句,做用是將兩路的VRX和VRY信號分拆爲VRF(前),VRB(後),VRL(左),VRR(右),代碼中還有一個邏輯還沒有寫入,就是須要將VRF,VRB,VRL,VRR的位移量計算出來。公式以下:

  • VRF = XZero - (XZero - VRX)
  • VRB = VRX - XZero
  • VRL = YZero - (YZero -VRY)
  • VRR = VRY - YZero

說明:XZero=130 , YZero=123

結論:

若是須要向接收機發送更爲精確的控制值,可在發射機讀入 VRX和VRY後就開始分解並計算出VRF,VRB,VRL,VRR而後進行8位數映射再傳送至接收機,,這樣能夠更爲精確地計算出位移量,而不至於在轉換時丟失。

參考閱讀

相關文章
相關標籤/搜索