arduino的nrf24L01收发通信

最近弄了两个nrf24l01的模块,想试试之后往智能家居上用,正好之前有一个小车用来验证导航算法的,还有一个小四轴的控制手柄,所以萌生了结合三者弄个遥控车玩一玩。小四轴基于STC15系列主控,51架构,而小车主控是arduino平台,那么nrf24l01的代码就需要分开写。nrf24l01采用SPI总线通信,虽然占引脚数多,但是收发速度快,模块通信速率可达2Mbps,而功耗控制在待机22uA,如果掉电模式可以仅900nA,如果其余传感器功耗控制合理,用干电池驱动整个模块工作数月了,这可是其他通信模块不具备的特性,高速低耗,2.4Ghz的抗干扰能力也不错,穿透性稍差,但是家庭环境使用还是够的。

官方给了C语言的库,先拿两个51单片机试了下,OK,再从网上下载了arudino的库,试了下,???无法收发,换个库,还是不行,纳闷了,对照手册看源码,经过数次修改,始终不能成功,手册我都看的快背下来了。遂放弃,想到51版本的可以用,那就根据51版本的改过来用呗,首先是基本的预处理宏定义,这个可以照搬,对照手册没有问题:

RF24_config.h:

/*
 Copyright (C) 2011 J. Coliz <[email protected]>

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 version 2 as published by the Free Software Foundation.
 */

#ifndef __RF24_CONFIG_H__
#define __RF24_CONFIG_H__

#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif

#include <stddef.h>

// Stuff that is normally provided by Arduino
#ifdef ARDUINO
#include <SPI.h>
#else
#include <stdint.h>
#include <stdio.h>
#include <string.h>
extern HardwareSPI SPI;
#define _BV(x) (1<<(x))
#endif

#undef SERIAL_DEBUG
#ifdef SERIAL_DEBUG
#define IF_SERIAL_DEBUG(x) ({x;})
#else
#define IF_SERIAL_DEBUG(x)
#endif

// Avoid spurious warnings
#if 1
#if ! defined( NATIVE ) && defined( ARDUINO )
#undef PROGMEM
#define PROGMEM __attribute__(( section(".progmem.data") ))
#undef PSTR
#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
#endif
#endif

// Progmem is Arduino-specific
#ifdef ARDUINO
#include <avr/pgmspace.h>
#define PRIPSTR "%S"
#else
typedef char const char;
typedef uint16_t prog_uint16_t;
#define PSTR(x) (x)
#define printf_P printf
#define strlen_P strlen
#define PROGMEM
#define pgm_read_word(p) (*(p)) 
#define PRIPSTR "%s"
#endif

#endif // __RF24_CONFIG_H__
// vim:ai:cin:sts=2 sw=2 ft=cpp

然后照搬一个模块寄存器宏定义、引脚宏定义,nRF24L01.h:

/*
    Copyright (c) 2007 Stefan Engelke <[email protected]>

    Permission is hereby granted, free of charge, to any person 
    obtaining a copy of this software and associated documentation 
    files (the "Software"), to deal in the Software without 
    restriction, including without limitation the rights to use, copy, 
    modify, merge, publish, distribute, sublicense, and/or sell copies 
    of the Software, and to permit persons to whom the Software is 
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be 
    included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
    DEALINGS IN THE SOFTWARE.
*/

/* Memory Map */
#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 OBSERVE_TX  0x08
#define CD          0x09
#define RX_ADDR_P0  0x0A
#define RX_ADDR_P1  0x0B
#define RX_ADDR_P2  0x0C
#define RX_ADDR_P3  0x0D
#define RX_ADDR_P4  0x0E
#define RX_ADDR_P5  0x0F
#define TX_ADDR     0x10
#define RX_PW_P0    0x11
#define RX_PW_P1    0x12
#define RX_PW_P2    0x13
#define RX_PW_P3    0x14
#define RX_PW_P4    0x15
#define RX_PW_P5    0x16
#define FIFO_STATUS 0x17
#define DYNPD	    0x1C
#define FEATURE	    0x1D

/* Bit Mnemonics */
#define MASK_RX_DR  6
#define MASK_TX_DS  5
#define MASK_MAX_RT 4
#define EN_CRC      3
#define CRCO        2
#define PWR_UP      1
#define PRIM_RX     0
#define ENAA_P5     5
#define ENAA_P4     4
#define ENAA_P3     3
#define ENAA_P2     2
#define ENAA_P1     1
#define ENAA_P0     0
#define ERX_P5      5
#define ERX_P4      4
#define ERX_P3      3
#define ERX_P2      2
#define ERX_P1      1
#define ERX_P0      0
#define AW          0
#define ARD         4
#define ARC         0
#define PLL_LOCK    4
#define RF_DR       3
#define RF_PWR      6
#define RX_DR       6
#define TX_DS       5
#define MAX_RT      4
#define RX_P_NO     1
#define TX_FULL     0
#define PLOS_CNT    4
#define ARC_CNT     0
#define TX_REUSE    6
#define FIFO_FULL   5
#define TX_EMPTY    4
#define RX_FULL     1
#define RX_EMPTY    0
#define DPL_P5	    5
#define DPL_P4	    4
#define DPL_P3	    3
#define DPL_P2	    2
#define DPL_P1	    1
#define DPL_P0	    0
#define EN_DPL	    2
#define EN_ACK_PAY  1
#define EN_DYN_ACK  0

/* Instruction Mnemonics */
#define READ_REG    0x00
#define WRITE_REG    0x20
#define REGISTER_MASK 0x1F
#define ACTIVATE      0x50
#define R_RX_PL_WID   0x60
#define R_RX_PAYLOAD  0x61
#define W_TX_PAYLOAD  0xA0
#define W_ACK_PAYLOAD 0xA8
#define FLUSH_TX      0xE1
#define FLUSH_RX      0xE2
#define REUSE_TX_PL   0xE3
#define NOP           0xFF

/* Non-P omissions */
#define LNA_HCURR   0

/* P model memory Map */
#define RPD         0x09

/* P model bit Mnemonics */
#define RF_DR_LOW   5
#define RF_DR_HIGH  3
#define RF_PWR_LOW  1
#define RF_PWR_HIGH 2

#define TX_ADR_WIDTH    5     // 5 uints TX address width
#define RX_ADR_WIDTH    5     // 5 uints RX address width
#define TX_PLOAD_WIDTH  20    // 20 uints TX payload
#define RX_PLOAD_WIDTH  20    // 20 uints TX payload

然后是与模块通信的代码:类class RF24,头文件如下:

#ifndef __RF24_H__
#define __RF24_H__

class RF24
{
private:
  uint8_t ce_pin; /**< "Chip Enable" pin, activates the RX or TX role */
  uint8_t csn_pin; /**< SPI Chip select */
  bool wide_band; /* 2Mbs data rate in use? */
  bool p_variant; /* False for RF24L01 and true for RF24L01P */
  uint8_t payload_size; /**< Fixed size of payloads */
  bool ack_payload_available; /**< Whether there is an ack payload waiting */
  bool dynamic_payloads_enabled; /**< Whether dynamic payloads are enabled. */ 
  uint8_t ack_payload_length; /**< Dynamic size of pending ack payload. */
  uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */
  uint8_t TX_ADDRESS[TX_ADR_WIDTH];
  uint8_t RX_ADDRESS[RX_ADR_WIDTH];
public:
  RF24(uint8_t _cepin, uint8_t _cspin);
  void csn(int mode);
  void ce(int level);
  uint8_t SPI_RW(uint8_t dat);
  uint8_t  SPI_READ(uint8_t reg);
  uint8_t  SPI_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars);
  uint8_t  SPI_Read_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars);
  uint8_t  SPI_RW_Reg(uint8_t reg, uint8_t value);
  void SetRX_Mode(void);
  void SetTX_Mode(void);
  bool nRF24L01_RxPacket( uint8_t * buf, uint8_t len );
  bool nRF24L01_TxPacket( uint8_t * tx_buf);
  void init_NRF24L01(void);
};
#endif // __RF24_H__

实现根据c51版本的改:

#include "nRF24L01.h"
#include "RF24_config.h"
#include "RF24.h"

RF24::RF24(uint8_t _cepin, uint8_t _cspin):
  ce_pin(_cepin), csn_pin(_cspin), wide_band(true), p_variant(false), 
  payload_size(20), ack_payload_available(false), dynamic_payloads_enabled(false),
  pipe0_reading_address(0)
{
  // Minimum ideal SPI bus speed is 2x data rate
  // If we assume 2Mbs data rate and 16Mhz clock, a
  // divider of 4 is the minimum we want.
  // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz
#ifdef ARDUINO
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV4);
#endif
  pinMode(ce_pin,OUTPUT);
  pinMode(csn_pin,OUTPUT);
  SPI.begin();
  //TX_ADDRESS[TX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; 
  RX_ADDRESS[0]= 0x34;
  RX_ADDRESS[1]= 0x43;
  RX_ADDRESS[2]= 0x10;
  RX_ADDRESS[3]= 0x10;
  RX_ADDRESS[4]= 0x01;
  TX_ADDRESS[0]= 0x34;
  TX_ADDRESS[1]= 0x43;
  TX_ADDRESS[2]= 0x10;
  TX_ADDRESS[3]= 0x10;
  TX_ADDRESS[4]= 0x01;
}

void RF24::init_NRF24L01(void)
{
   ce(LOW);
  csn(HIGH);
  //sck(LOW); 
  SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);    
  SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH); 
  SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);      
  SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);  
  SPI_RW_Reg(WRITE_REG + RF_CH, 0x6e);        
  SPI_RW_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH);
  SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x27);       
}

void RF24::csn(int mode)
{
  digitalWrite(csn_pin,mode);
}

void RF24::ce(int level)
{
  digitalWrite(ce_pin,level);
}

uint8_t RF24::SPI_RW(uint8_t dat){
    return SPI.transfer(dat);
  }

uint8_t  RF24::SPI_READ(uint8_t reg){
    uint8_t reg_val ;
    csn(LOW);
    delayMicroseconds(12);
    SPI.transfer(reg);
    delayMicroseconds(12);
    reg_val = SPI.transfer(0);
    delayMicroseconds(12);
    csn(HIGH);
    return reg_val;
  }

uint8_t  RF24::SPI_RW_Reg(uint8_t reg, uint8_t value)
{
  uint8_t sta;  
  csn(LOW);                   // CSN low, init SPI transaction
  sta = SPI.transfer(reg);      // select register
  SPI.transfer(value);             // ..and write value to it..
  csn(HIGH);                   // CSN high again
  
  return(sta);            // return nRF24L01 status
}

uint8_t  RF24::SPI_Read_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
{
  uint8_t sta,uchar_ctr;
  
  csn(LOW);            //SPIʹÄÜ       
  sta = SPI.transfer(reg);   
  for(uchar_ctr=0; uchar_ctr<uchars; uchar_ctr++) //
    pBuf[uchar_ctr]= SPI.transfer(0x0);
  csn(HIGH);           //¹Ø±ÕSPI
  return(sta);    // 
}

uint8_t  RF24::SPI_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
{
  uint8_t sta,uchar_ctr;
  
  csn(LOW);            //SPIʹÄÜ       
  sta = SPI.transfer(reg);   
  for(uchar_ctr=0; uchar_ctr<uchars; uchar_ctr++) //
  SPI.transfer(*pBuf++);
  csn(HIGH);           //¹Ø±ÕSPI
  return(sta);    // 
}

void RF24::SetRX_Mode(void)
{
  ce(LOW);
  SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f);      // IRQÊÕ·¢Íê³ÉÖжÏÏìÓ¦£¬16λCRC £¬Ö÷½ÓÊÕ
  ce(HIGH);
}

void RF24::SetTX_Mode(void)
{
  ce(LOW);
  SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);      // IRQÊÕ·¢Íê³ÉÖжÏÏìÓ¦£¬16λCRC £¬Ö÷½ÓÊÕ
  ce(HIGH);
}

bool RF24::nRF24L01_RxPacket( uint8_t * rx_buf, uint8_t len )
{
  bool revale = false;
  uint8_t sta=0x00;
  sta = SPI_READ(STATUS);
  ce(LOW);
  if(sta & _BV(RX_DR)){
    //write_register(STATUS,_BV(RX_DR) );
      SPI_Read_Buf(R_RX_PAYLOAD,rx_buf,20);
      revale =true;
    }
 SPI_RW_Reg(WRITE_REG+STATUS,sta);
 delayMicroseconds(30);
 SPI_RW_Reg(0xE2,0xff);
 ce(HIGH);
 
 return revale;
}

bool RF24::nRF24L01_TxPacket( uint8_t * tx_buf)
{
  ce(LOW);
  SPI_RW_Reg(0xE1,0xff);  
  SPI_Write_Buf(W_TX_PAYLOAD, tx_buf,payload_size);            
  SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);    
  SPI_RW_Reg(WRITE_REG+STATUS,0xff);
 ce(HIGH);   
   delayMicroseconds(12);
}

void RF24::SPI_CLR_Reg(uint8_t R_T)
{
    csn(LOW); 
  if(R_T==1)                  // CSN low, init SPI transaction
    SPI_RW(FLUSH_TX);             // ..and write value to it..
  else
  SPI_RW(FLUSH_RX);             // ..and write value to it..
    csn(HIGH);                  // CSN high again
}

void RF24::ifnnrf_CLERN_ALL()
{
  SPI_CLR_Reg(0);
  SPI_CLR_Reg(1);
  SPI_RW_Reg(WRITE_REG+STATUS,0xff);
  //IRQ=1;
}

函数名都可以顾名思义,所以不必细说,实际用的时候可以直接引用,看调用方法:

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

#define IRQ 8  //定义中断查询引脚

//const uint64_t pipe =  0x3443101001LL;
RF24 radio(9,10); //初始化类,定义引脚

void setup(void)
{
  Serial.begin(9600);
  radio.init_NRF24L01();  //初始化
  radio.SetTX_Mode();  //设置发送模式
}

void loop(void)
{
  uint8_t command[20];
  bool done = false;
  char i=0;
  for(i=0;i<20;i++){ 
    command[i]='a'+i;
  }
  radio.nRF24L01_TxPacket(command);  //发送数据包
  while(digitalRead(IRQ)==HIGH);  //等待发送完成
}

接收与发射类似:

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

//const uint64_t pipe =  0x3443101001LL;
RF24 radio(9,10);

void setup(void)
{
  Serial.begin(9600);
  radio.init_NRF24L01();
}

void loop(void)
{
  uint8_t command[20];
  bool done = false;
  char i=0;
  radio.SetRX_Mode();  //设置读模式
  delay(100);
  if(radio.nRF24L01_RxPacket(command,20)){  //接受数据包
     for(i=0;i<20;i++){ 
         Serial.print(command[i]);
         Serial.print('-');
     }
     Serial.println("*");
     radio.ifnnrf_CLERN_ALL();  //判断未读完
   }
}

经测试,OK。那既然两个平台都通了,可以试试在遥控器和小车上了。小四轴遥控器原本用的就是nrf24L01,将两个摇杆的位置信息映射到0~255的uchar类型,那么小车上只需要将nrf24L01模块地址设置为相同的接收地址,解析对应通道数据并转换成速度信息即可。由于理论上摇杆回中对应的值为128,但是实际摇杆回中值会有个浮动,导致当我没控制摇杆,即摇杆处于回中状态时小车会低速运动,因此,做个判断,将128附近值屏蔽,控制代码如下:

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

RF24 radio(8,10);
//引脚定义
int motorLF = 14;
int motorLB = 15;
int motorRF = 16;
int motorRB = 17;
int pwmLSpeed = 3;
int pwmRSpeed = 5;

byte leftSpeed = 0;
byte rightSpeed = 0;
uint8_t command[20];

void setup(void)
{
    Serial.begin(9600);
    radio.init_NRF24L01();
    pinMode(motorLF, OUTPUT);
    pinMode(motorLB, OUTPUT);
    pinMode(motorRF, OUTPUT);
    pinMode(motorRB, OUTPUT);
    pinMode(pwmLSpeed, OUTPUT);
    pinMode(pwmRSpeed, OUTPUT);
    Serial.println("start");
}

void loop(void)
{
    bool done = false;
    char i=0;
    radio.SetRX_Mode();
    delay(100);
    if(radio.nRF24L01_RxPacket(command,20)){
    if(command[3]>135 ){
        analogWrite(pwmLSpeed,(command[3]-128)*2);
        analogWrite(pwmRSpeed,(command[3]-128)*2);
        turnLeft();
        Serial.print("left:");
        Serial.println((command[3]-128)*2);
    }
    else if(command[3]<120){
        analogWrite(pwmLSpeed,(127-command[3])*2);
        analogWrite(pwmRSpeed,(127-command[3])*2);
        turnRight();
        Serial.print("right:");
        Serial.println((128-command[3])*2);
    }else if(command[4]<110){
        analogWrite(pwmLSpeed,(127-command[4])*2);
        analogWrite(pwmRSpeed,(127-command[4])*2);
        moveBackward();
        Serial.print("backward:");
        Serial.println((128-command[4])*2);
    }else if(command[4]>145){
        analogWrite(pwmLSpeed,(command[4]-128)*2);
        analogWrite(pwmRSpeed,(command[4]-128)*2);
        moveForward();
        Serial.print("forward:");
        Serial.println((command[4]-128)*2);
    }
    else{
        analogWrite(pwmLSpeed,0);
        analogWrite(pwmRSpeed,0);
        }
    Serial.println("*");
    radio.ifnnrf_CLERN_ALL();
    }
}

void turnLeft(){
    digitalWrite(motorLF,HIGH);
    digitalWrite(motorLB,LOW);
    digitalWrite(motorRF,LOW);
    digitalWrite(motorRB,HIGH);
}

void turnRight(){
    digitalWrite(motorLF,LOW);
    digitalWrite(motorLB,HIGH);
    digitalWrite(motorRF,HIGH);
    digitalWrite(motorRB,LOW);
}

void moveForward(){
    digitalWrite(motorLF,LOW);
    digitalWrite(motorLB,HIGH);
    digitalWrite(motorRF,LOW);
    digitalWrite(motorRB,HIGH);
}

void moveBackward(){
    digitalWrite(motorLF,HIGH);
    digitalWrite(motorLB,LOW);
    digitalWrite(motorRF,HIGH);
    digitalWrite(motorRB,LOW);
}

代码很简单,不多说,command数组保存了全部数据,其中第4、5字节为左右、前后速度控制值。

猜你喜欢

转载自blog.csdn.net/atp1992/article/details/80783136