极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 84996|回复: 21

Arduino DIY航模遥控器第一步 ---搞定nRF24L01!

[复制链接]
发表于 2017-9-10 22:00:12 | 显示全部楼层 |阅读模式
原创发贴!


    论坛上看到几位大神DIY航模遥控器,特别是罗莉的方案,很给力很亲民。
针对arduino简单强大的用途,打算DIY一款基于arduino的航模遥控器。
    arduino对外设的扩展性非常方便,可以扩展蓝牙接口,通过手机蓝牙连接遥控器进行
参数设置,这样代码中对于屏幕和菜单部分可以简化很多,代码逐步完善,毕竟要工作
不可能那么快。后续手机端打算写个安卓的apk来对遥控器进行设置。
    要搞定DIY遥控,亲民价格中基本绕不开nrf24L01这个东东。arduino针对这个芯片
有不少人写过库,但是在此处使用有很多不方便的地方,所以我重写了nrf的库,后面直接贴代码出来
大家复制粘贴就可以使用。代码我已尽量完善的写了注释,方便大家理解。




    以下代码,可以通过arduino的串口助手直接对nRF24L01的寄存器进行读写操作,方便理解无线模块的控制,无线模块控制没弄懂
写完整的遥控器代码完全是空中楼阁。


下面代码复制粘贴在ardino的新工程中
  1. #include "nrf.h"

  2. /************************************************************************
  3. * 代码禁止应用商业用途,转载需说明原著作者
  4. * 版权 D调的华丽
  5. * 2017-09-10
  6. **********************************************************************/
  7. /************************************************************************
  8. * //引脚定义在nrf.h中,可以根据实际情况修改
  9. #define CE 9   //  模式控制
  10. #define CSN 13   //SS片选 ,LOW工作
  11. #define SCK 11  //时钟信号
  12. #define MISO 12
  13. #define MOSI 10
  14. #define IRQ 2  //中断信号

  15. **************************************************************************
  16. *调试nRF24L01时,可直接串口对nRF24L01的寄存器进行访问,比如要读取
  17. *0x00地址的设置时,直接串口字符串模式 输入  R00@aa;  读取时,@后的aa为格式补位,可随意输入16进制数
  18. *前面寄存器地址00是16进制格式的0x00,注意16进制数只能用小写,大写支持没加进代码里。
  19. *************************************************************************/



  20. String inputString = "";            //定义串口指令接收字符串
  21. boolean stringComplete = false;     //串口接收指令完后置true
  22. boolean flagRe = false;             //串口接收过程中的辅助标志
  23. byte Order[3];                      //指令存放地址 ,0位为'W'-写或'R'-读;1位为欲操作的寄存器地址,2位为操作值

  24. //////////////////////////////////////////////////////
  25. void setup() {
  26.   Serial.begin(9600);
  27.   delay(500);
  28.   NRF_begin();                  //启动虚拟SPI端口
  29.   inputString.reserve(200);     //串口接收中断开启,最大缓存区200
  30.   
  31.   Serial.println("NRF begin!");
  32.   
  33. }

  34. /////////////////////串口中断服务程序/////////////////////////////
  35. void serialEvent() {
  36.   while (Serial.available()) {    //串口数据传入时
  37.    
  38.     char inChar = (char)Serial.read();   //读取一个字节
  39.     if(!flagRe)         //默认标志flagRe为0,第一次进入,进行报文头识别,必须为W 或 R 才会进行后续接收
  40.     {
  41.       if((inChar == 'W') || (inChar == 'R')){
  42.       flagRe = true;           //遇到报文头,标志置1,开启接收过程
  43.       }
  44.     }
  45.     if(flagRe){
  46.        inputString += inChar;            //接收报文
  47.        if (inChar == '\n'){             // 遇到结束符结束
  48.         stringComplete = true;          //接收完一条报文后处理标志开启,进行报文处理
  49.         flagRe = false;                 //复位接收标志
  50.         }
  51.       }
  52.   }
  53. }

  54. /////////////////////两位字符拼接成1位byte数的函数///////////////////////////////////////
  55. //由于串口接收过来的数据是ASCII格式,要对寄存器和数据进行十六进制翻译

  56. byte CharToHex(char Hnum,char Lnum)
  57. {
  58.   byte re = 0;
  59.   if(Hnum >=0x30 && Hnum <= 0x39){re |= (Hnum - 0x30) << 4;}
  60.   else if(Hnum >= 'a' && Hnum <= 'f'){re |= (Hnum - 87) << 4;}
  61.   else return 0;
  62.   if(Lnum >=0x30 && Lnum <= 0x39){re |= (Lnum - 0x30);}
  63.   else if(Lnum >= 'a' && Lnum <= 'f'){re |= (Lnum - 87);}
  64.   else return 0;
  65.   return re;
  66.    
  67.   }

  68. void loop() {
  69.   
  70.   int flagV = 0,flagEnd = 0;   //定义@符位置标记和结束符;的位置标记
  71.   if(stringComplete)   //串口中断事件中接收到一条完整报文后进行下列处理
  72.   {
  73.     flagV = inputString.indexOf('@');          //查找@的位置
  74.     flagEnd = inputString.indexOf(';');        //查找;的位置
  75.     if(flagV == 3 && flagEnd == 6)             //根据如:"W0a@1f;"的报文格式,@位于第3,;位于第6,再次判断报文可靠
  76.     {
  77.       Order[0] = inputString.charAt(0);          //如报文格式正确,将W或R写入第一位
  78.       Order[1] = CharToHex(inputString.charAt(1),inputString.charAt(2));  //寄存器地址写入第二位
  79.       Order[2] = CharToHex(inputString.charAt(4),inputString.charAt(5));  //值写入第三位
  80.       }
  81.       else
  82.       {                                                         //否则输出错误的原因
  83.         Serial.print(inputString);
  84.         Serial.print("flagV is:");Serial.print(flagV);Serial.print("flagEnd is:");Serial.println(flagEnd);
  85.         Serial.println("Enter Err! Enter like Wff@ff; or Rff@ff;");
  86.       }

  87.       if(Order[0] == 'W')          //写命令时执行
  88.       {
  89.         Serial.print(inputString);     //回传执行的命令
  90.         SPI_RW_Reg(Order[1]+0x20,Order[2]);  //写入值到相应的寄存器中,注意,nRF24L01的写操作要加0x20的操作指令偏移,不明白去看芯片手册~~
  91.         Serial.print("Write 0x"); Serial.print(Order[1],HEX); Serial.println(" OK!");
  92.         Serial.print(Order[1],HEX); Serial.print(" Now is: ");
  93.         byte ccc = SPI_Read(Order[1]);           //写入完成后读取一次确认
  94.         if(ccc<16)
  95.         Serial.print("0x0");else Serial.print("0x");
  96.         Serial.println(ccc,HEX);
  97.         }
  98.       if(Order[0] == 'R')        //读命令时执行
  99.       {
  100.         Serial.print(inputString);
  101.         byte aaa = SPI_Read(Order[1]);              //读取
  102.         Serial.print("Read 0x"); Serial.print(Order[1],HEX); Serial.print(" is: ");
  103.         if(aaa<16)
  104.         Serial.print("0x0");else Serial.print("0x");
  105.         Serial.println(aaa,HEX);
  106.         }

  107.       //数据复位
  108.       Serial.println("");
  109.       stringComplete = false;
  110.       inputString = "";
  111.       Order[0] = 0;
  112.       Order[1] = 0;
  113.       Order[2] = 0;
  114.   }  
  115. }
复制代码



下面代码复制粘贴后文件名保存为 nrf.h 放在ardino的libraries下可以新建一个RNF04L01的文件夹下
  1. #ifndef _NRF_H_
  2. #define _NRF_H_

  3. #include "Arduino.h"

  4. //引脚
  5. #define CE 9   //  模式控制
  6. #define CSN 13   //SS片选 ,LOW工作
  7. #define SCK 11
  8. #define MISO 12
  9. #define MOSI 10
  10. #define IRQ 2  //中断信号


  11. #define TX_ADR_WIDTH    5   // 5  bytes TX(RX) address width

  12. #define TX_PLOAD_WIDTH  32  // 32 bytes TX payload

  13. #define READ_REG        0x00  // Define read command to register
  14. #define WRITE_REG       0x20  // Define write command to register
  15. #define RD_RX_PLOAD     0x61  // Define RX payload register address
  16. #define WR_TX_PLOAD     0xA0  // Define TX payload register address
  17. #define FLUSH_TX        0xE1  // Define flush TX register command
  18. #define FLUSH_RX        0xE2  // Define flush RX register command
  19. #define REUSE_TX_PL     0xE3  // Define reuse TX payload register command
  20. #define NOP             0xFF  // Define No Operation, might be used to read status register

  21. //***************************************************//
  22. // SPI(nRF24L01) registers(addresses)
  23. #define CONFIG          0x00  // 'Config' register address
  24. #define EN_AA           0x01  // 'Enable Auto Acknowledgment' register address
  25. #define EN_RXADDR       0x02  // 'Enabled RX addresses' register address
  26. #define SETUP_AW        0x03  // 'Setup address width' register address
  27. #define SETUP_RETR      0x04  // 'Setup Auto. Retrans' register address
  28. #define RF_CH           0x05  // 'RF channel' register address
  29. #define RF_SETUP        0x06  // 'RF setup' register address
  30. #define STATUS          0x07  // 'Status' register address
  31. #define OBSERVE_TX      0x08  // 'Observe TX' register address
  32. #define CD              0x09  // 'Carrier Detect' register address
  33. #define RX_ADDR_P0      0x0A  // 'RX address pipe0' register address
  34. #define RX_ADDR_P1      0x0B  // 'RX address pipe1' register address
  35. #define RX_ADDR_P2      0x0C  // 'RX address pipe2' register address
  36. #define RX_ADDR_P3      0x0D  // 'RX address pipe3' register address
  37. #define RX_ADDR_P4      0x0E  // 'RX address pipe4' register address
  38. #define RX_ADDR_P5      0x0F  // 'RX address pipe5' register address
  39. #define TX_ADDR         0x10  // 'TX address' register address
  40. #define RX_PW_P0        0x11  // 'RX payload width, pipe0' register address
  41. #define RX_PW_P1        0x12  // 'RX payload width, pipe1' register address
  42. #define RX_PW_P2        0x13  // 'RX payload width, pipe2' register address
  43. #define RX_PW_P3        0x14  // 'RX payload width, pipe3' register address
  44. #define RX_PW_P4        0x15  // 'RX payload width, pipe4' register address
  45. #define RX_PW_P5        0x16  // 'RX payload width, pipe5' register address
  46. #define FIFO_STATUS     0x17  // 'FIFO Status Register' register address
  47. #define STA_MARK_RX     0X40
  48. #define STA_MARK_TX     0X20
  49. #define STA_MARK_MX     0X10      



  50. void NRF_begin();

  51. byte SPI_RW(byte BYTE);                                // Single SPI read/write
  52. byte SPI_Read(byte reg);                               // Read one byte from nRF24L01
  53. byte SPI_RW_Reg(byte reg, byte BYTE);                  // Write one byte to register 'reg'
  54. byte SPI_Write_Buf(byte reg, byte *pBuf, byte bytes);  // Writes multiply bytes to one register
  55. byte SPI_Read_Buf(byte reg, byte *pBuf, byte bytes);   // Read multiply bytes from one register

  56. void RX_Mode(void);
  57. void TX_Mode(void);
  58. void NRF_power(byte P);
  59. void NRF_size(byte l);
  60. void NRF_channel(byte c);
  61. void NRF_init();  //未写完
  62. void NRF_test();  //未写完



  63. #endif
复制代码

下面代码复制粘贴后文件名保存为 nrf.cpp 放在ardino的libraries下可以新建一个RNF04L01的文件夹下
  1. #include "nrf.h"

  2. byte rx_buf[TX_PLOAD_WIDTH];
  3. byte tx_buf[TX_PLOAD_WIDTH];
  4. byte TX_ADDRESS[TX_ADR_WIDTH]  = {0x34,0x43,0x10,0x10,0x01};

  5. void NRF_begin()
  6. {
  7.   //SPI.begin();
  8.   
  9.   pinMode(CSN, OUTPUT);
  10.   pinMode(SCK, OUTPUT);
  11.   pinMode(MOSI, OUTPUT);
  12.   pinMode(CE, OUTPUT);
  13.   pinMode(MISO, INPUT);
  14.   
  15.   digitalWrite(CSN,HIGH);
  16.   digitalWrite(CE,LOW);
  17.   digitalWrite(SCK,LOW);
  18.   
  19.   //NRF_init();
  20.   }

  21. byte SPI_RW(byte BYTE)
  22. {
  23.   //SPI.transfer(BYTE);
  24.   byte bit_ctr;
  25.   
  26.   for(bit_ctr=0;bit_ctr<8;bit_ctr++)
  27.   {
  28.           if((BYTE&0x80)==0){digitalWrite(MOSI,LOW);} else {digitalWrite(MOSI,HIGH);}
  29.          // MOSI=(BYTE&0x80);
  30.           BYTE = (BYTE<<1);
  31.           //delayMicroseconds(500);
  32.           digitalWrite(SCK,HIGH);
  33.          // delayMicroseconds(500);
  34.           BYTE |= digitalRead(MISO);
  35.          // delayMicroseconds(500);
  36.           digitalWrite(SCK,LOW);
  37.          // delayMicroseconds(500);
  38.          
  39.   }
  40.   return(BYTE);
  41.   }

  42. // 写数据BYTE到reg寄存器
  43. byte SPI_RW_Reg(byte reg, byte BYTE)
  44. {
  45.   byte Status;
  46.   digitalWrite(CSN,LOW);
  47.   Status = SPI_RW(reg);
  48.   delayMicroseconds(20);
  49.   SPI_RW(BYTE);
  50.   digitalWrite(CSN,HIGH);

  51.   return(Status);
  52.   
  53.   }

  54. //从reg寄存器读数据出来
  55. byte SPI_Read(byte reg)
  56.   {
  57.     byte reg_val;
  58.     digitalWrite(CSN,LOW);
  59.     SPI_RW(reg);
  60.     reg_val = SPI_RW(0);
  61.     digitalWrite(CSN,HIGH);

  62.     return(reg_val);
  63.    
  64.     }

  65. //从reg寄存器中读出bytes个字节
  66. byte SPI_Read_Buf(byte reg, byte *pBuf, byte bytes)
  67. {
  68.   byte Status,byte_ctr;
  69.   digitalWrite(CSN,LOW);
  70.   Status = SPI_RW(reg);

  71.   for(byte_ctr = 0; byte_ctr < bytes; byte_ctr++)
  72.   {
  73.     pBuf[byte_ctr] = SPI_RW(0);
  74.     }
  75.    digitalWrite(CSN,HIGH);
  76.         //Serial.println("send OK!");
  77.    return(Status);
  78.   }

  79.   //把pBuf缓存中的数据写入到nRF24L01中
  80. byte SPI_Write_Buf(byte reg, byte *pBuf, byte bytes)
  81.   {
  82.     byte Status,byte_ctr;

  83.     digitalWrite(CSN,LOW);
  84.     Status = SPI_RW(reg);
  85.    
  86.     for(byte_ctr=0; byte_ctr<bytes; byte_ctr++)
  87.       SPI_RW(*pBuf++);
  88.       
  89.     digitalWrite(CSN,HIGH);
  90.    
  91.     return(Status);
  92.    
  93.     }


  94. //设置为接收模式
  95. void RX_Mode(void)
  96. {
  97.   digitalWrite(CE,LOW);
  98.   SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // Use the same address on the RX device as the TX device
  99.   SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);      // Enable Auto.Ack:Pipe0
  100.   SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);  // Enable Pipe0
  101.   SPI_RW_Reg(WRITE_REG + RF_CH, 40);        // Select RF channel 40
  102.   SPI_RW_Reg(WRITE_REG + RX_PW_P0, TX_PLOAD_WIDTH); // Select same RX payload width as TX Payload width
  103.   SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);   // TX_PWR:0dBm, Datarate:2Mbps, LNA:HCURR
  104.   SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f);     // Set PWR_UP bit, enable CRC(2 bytes) & Prim:RX. RX_DR enabled..

  105.   digitalWrite(CE,HIGH);
  106.    
  107.   }

  108. void TX_Mode(void)
  109. {
  110.   digitalWrite(CE,LOW);
  111.   
  112.   SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);    // Writes TX_Address to nRF24L01
  113.   SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // RX_Addr0 same as TX_Adr for Auto.Ack
  114.   SPI_Write_Buf(WR_TX_PLOAD, tx_buf, TX_PLOAD_WIDTH); // Writes data to TX payload

  115.   SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);      // Enable Auto.Ack:Pipe0
  116.   SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);  // Enable Pipe0
  117.   SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x1a); // 500us + 86us, 10 retrans...
  118.   SPI_RW_Reg(WRITE_REG + RF_CH, 40);        // Select RF channel 40
  119.   SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);   // TX_PWR:0dBm, Datarate:2Mbps, LNA:HCURR
  120.   SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);

  121.   digitalWrite(CE,HIGH);
  122.   
  123.   }

  124. void  NRF_power(byte P)
  125. {
  126.    digitalWrite(CE,LOW);
  127.    
  128.   if(P==3)SPI_RW_Reg(0x06,0x27);     //0db 修正之前注释错误
  129.   else if(P==2)SPI_RW_Reg(0x06,0x25);    //-6db
  130.   else if(P==1)SPI_RW_Reg(0x06,0x23);    //-12db
  131.   else if(P==0)SPI_RW_Reg(0x06,0x21);    //-18db
  132.   
  133.   digitalWrite(CE,HIGH);
  134.   }

  135. void NRF_size(byte l)
  136. {
  137.   digitalWrite(CE,LOW);
  138.   SPI_RW_Reg(0x11,l);  
  139.   digitalWrite(CE,HIGH);
  140. }
  141. void NRF_channel(byte c)
  142. {
  143.   digitalWrite(CE,LOW);
  144.   SPI_RW_Reg(0x05,c);  
  145.   digitalWrite(CE,HIGH);
  146. }


  147. void NRF_init()
  148. {
  149.   digitalWrite(CE,LOW);
  150.   //SCK=0;
  151.   SPI_RW_Reg(0x01,0x00); //禁止 自动应答
  152.   SPI_RW_Reg(0x02,0x01); //允许 P0信道
  153.   SPI_RW_Reg(0x04,0x00); //禁止 自动重发
  154.   TX_Mode();      
  155.   NRF_channel(66);
  156.   NRF_power(1);
  157.   NRF_size(11);
  158.   digitalWrite(CE,HIGH);
  159. //  TX_address(address);
  160. //  RX_address(address);
  161. }
  162. /*






  163.   
复制代码

回复

使用道具 举报

 楼主| 发表于 2017-9-10 22:09:09 | 显示全部楼层
最后回复下,以上代码,大家觉得  nrf24l01的串口透传是不是已经在眼前了~
其实串口透传就这么简单。
回复 支持 2 反对 0

使用道具 举报

发表于 2018-6-15 13:15:28 | 显示全部楼层
本帖最后由 a461624201 于 2018-6-30 17:18 编辑

多说一句,这个程序的引脚更改,必须在nrf.h中,在arduono代码中更改无效。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 1 反对 0

使用道具 举报

发表于 2018-8-23 14:16:29 | 显示全部楼层
谢谢老师分享!请教个问题,想要实现电脑与多个终端的通讯,比如电脑发送001#+led,001#终端收到信息后,led闪烁,并向电脑发送001#,用来确认。

谢谢老师。
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2017-9-10 22:01:29 | 显示全部楼层
二楼放两本无线模块的参考资料,相信我,看懂这两本,有这两本就够了~~









本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 2017-9-11 15:21:43 | 显示全部楼层
好东西 感谢分享~~·
回复 支持 反对

使用道具 举报

发表于 2017-9-11 15:46:04 | 显示全部楼层
早就买了,搞不定就丢一边去了。
谢谢楼主分享,先收藏有空现试着学。
回复 支持 反对

使用道具 举报

发表于 2017-9-12 15:47:52 来自手机 | 显示全部楼层
能实现双向通信嘛
回复 支持 反对

使用道具 举报

发表于 2017-9-12 18:50:12 | 显示全部楼层
靳靳 发表于 2017-9-12 15:47
能实现双向通信嘛

能 ..o k ...
回复 支持 反对

使用道具 举报

发表于 2018-1-25 14:04:06 | 显示全部楼层
难得的好资料~~~感谢分享!
回复 支持 反对

使用道具 举报

发表于 2018-1-27 21:38:03 | 显示全部楼层
能否吧萝莉的程序修改到ARDUINO上,做萝莉用51程序搞不好,还是开源的好用
回复 支持 反对

使用道具 举报

发表于 2018-1-29 00:20:45 | 显示全部楼层
我几年买的NRF24没读出数据,可能以前折腾坏了,这个和淘宝里卖的那种USB转SPI很类似,可以控制,也可以测SPI  IC好坏。
回复 支持 反对

使用道具 举报

发表于 2018-1-29 01:22:31 | 显示全部楼层
像这样,要么没连接好,要么芯片坏了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 2018-1-29 13:40:27 | 显示全部楼层
本帖最后由 a461624201 于 2018-1-29 14:10 编辑

出现一个问题,当连接后,NRF的CSN端一直为4.5V,这样久了会不会损坏芯片,要不要增加个电阻把电压拉低到3.2V?
回复 支持 反对

使用道具 举报

发表于 2018-3-2 18:54:28 | 显示全部楼层
精华里的精华,先收藏,以后学习。目前水平还有距离。
回复 支持 反对

使用道具 举报

发表于 2018-3-22 14:03:08 | 显示全部楼层
你好,看到你在“极客工坊”上发布的“arduino DIY航模遥控器”的文章。我最近想开发一款简易航模遥控器:用于室内固定翼轻型机,遥控距离50米之内。接收器要求越轻越好,最好在1.5g左右。通道只需2个,1个用于输出模拟电压量以控制一个小电机,另一个也是输出模拟的正负电压量,以控制尾翼的小电磁线圈进行左右摆动。 本人不懂这方面的知识,请问您能否给与帮助。谢谢
回复 支持 反对

使用道具 举报

发表于 2018-3-23 19:45:58 | 显示全部楼层
D调的华丽 发表于 2017-9-10 22:09
最后回复下,以上代码,大家觉得  nrf24l01的串口透传是不是已经在眼前了~
其实串口透传就这么简单。

强大的气场,楼主好牛,好好学习,天天向上。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-3-29 01:11 , Processed in 0.056504 second(s), 30 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表