原创发贴!
论坛上看到几位大神DIY航模遥控器,特别是罗莉的方案,很给力很亲民。
针对arduino简单强大的用途,打算DIY一款基于arduino的航模遥控器。
arduino对外设的扩展性非常方便,可以扩展蓝牙接口,通过手机蓝牙连接遥控器进行
参数设置,这样代码中对于屏幕和菜单部分可以简化很多,代码逐步完善,毕竟要工作
不可能那么快。后续手机端打算写个安卓的apk来对遥控器进行设置。
要搞定DIY遥控,亲民价格中基本绕不开nrf24L01这个东东。arduino针对这个芯片
有不少人写过库,但是在此处使用有很多不方便的地方,所以我重写了nrf的库,后面直接贴代码出来
大家复制粘贴就可以使用。代码我已尽量完善的写了注释,方便大家理解。
以下代码,可以通过arduino的串口助手直接对nRF24L01的寄存器进行读写操作,方便理解无线模块的控制,无线模块控制没弄懂
写完整的遥控器代码完全是空中楼阁。
下面代码复制粘贴在ardino的新工程中 - #include "nrf.h"
- /************************************************************************
- * 代码禁止应用商业用途,转载需说明原著作者
- * 版权 D调的华丽
- * 2017-09-10
- **********************************************************************/
- /************************************************************************
- * //引脚定义在nrf.h中,可以根据实际情况修改
- #define CE 9 // 模式控制
- #define CSN 13 //SS片选 ,LOW工作
- #define SCK 11 //时钟信号
- #define MISO 12
- #define MOSI 10
- #define IRQ 2 //中断信号
- **************************************************************************
- *调试nRF24L01时,可直接串口对nRF24L01的寄存器进行访问,比如要读取
- *0x00地址的设置时,直接串口字符串模式 输入 R00@aa; 读取时,@后的aa为格式补位,可随意输入16进制数
- *前面寄存器地址00是16进制格式的0x00,注意16进制数只能用小写,大写支持没加进代码里。
- *************************************************************************/
- String inputString = ""; //定义串口指令接收字符串
- boolean stringComplete = false; //串口接收指令完后置true
- boolean flagRe = false; //串口接收过程中的辅助标志
- byte Order[3]; //指令存放地址 ,0位为'W'-写或'R'-读;1位为欲操作的寄存器地址,2位为操作值
- //////////////////////////////////////////////////////
- void setup() {
- Serial.begin(9600);
- delay(500);
- NRF_begin(); //启动虚拟SPI端口
- inputString.reserve(200); //串口接收中断开启,最大缓存区200
-
- Serial.println("NRF begin!");
-
- }
- /////////////////////串口中断服务程序/////////////////////////////
- void serialEvent() {
- while (Serial.available()) { //串口数据传入时
-
- char inChar = (char)Serial.read(); //读取一个字节
- if(!flagRe) //默认标志flagRe为0,第一次进入,进行报文头识别,必须为W 或 R 才会进行后续接收
- {
- if((inChar == 'W') || (inChar == 'R')){
- flagRe = true; //遇到报文头,标志置1,开启接收过程
- }
- }
- if(flagRe){
- inputString += inChar; //接收报文
- if (inChar == '\n'){ // 遇到结束符结束
- stringComplete = true; //接收完一条报文后处理标志开启,进行报文处理
- flagRe = false; //复位接收标志
- }
- }
- }
- }
- /////////////////////两位字符拼接成1位byte数的函数///////////////////////////////////////
- //由于串口接收过来的数据是ASCII格式,要对寄存器和数据进行十六进制翻译
- byte CharToHex(char Hnum,char Lnum)
- {
- byte re = 0;
- if(Hnum >=0x30 && Hnum <= 0x39){re |= (Hnum - 0x30) << 4;}
- else if(Hnum >= 'a' && Hnum <= 'f'){re |= (Hnum - 87) << 4;}
- else return 0;
- if(Lnum >=0x30 && Lnum <= 0x39){re |= (Lnum - 0x30);}
- else if(Lnum >= 'a' && Lnum <= 'f'){re |= (Lnum - 87);}
- else return 0;
- return re;
-
- }
- void loop() {
-
- int flagV = 0,flagEnd = 0; //定义@符位置标记和结束符;的位置标记
- if(stringComplete) //串口中断事件中接收到一条完整报文后进行下列处理
- {
- flagV = inputString.indexOf('@'); //查找@的位置
- flagEnd = inputString.indexOf(';'); //查找;的位置
- if(flagV == 3 && flagEnd == 6) //根据如:"W0a@1f;"的报文格式,@位于第3,;位于第6,再次判断报文可靠
- {
- Order[0] = inputString.charAt(0); //如报文格式正确,将W或R写入第一位
- Order[1] = CharToHex(inputString.charAt(1),inputString.charAt(2)); //寄存器地址写入第二位
- Order[2] = CharToHex(inputString.charAt(4),inputString.charAt(5)); //值写入第三位
- }
- else
- { //否则输出错误的原因
- Serial.print(inputString);
- Serial.print("flagV is:");Serial.print(flagV);Serial.print("flagEnd is:");Serial.println(flagEnd);
- Serial.println("Enter Err! Enter like Wff@ff; or Rff@ff;");
- }
- if(Order[0] == 'W') //写命令时执行
- {
- Serial.print(inputString); //回传执行的命令
- SPI_RW_Reg(Order[1]+0x20,Order[2]); //写入值到相应的寄存器中,注意,nRF24L01的写操作要加0x20的操作指令偏移,不明白去看芯片手册~~
- Serial.print("Write 0x"); Serial.print(Order[1],HEX); Serial.println(" OK!");
- Serial.print(Order[1],HEX); Serial.print(" Now is: ");
- byte ccc = SPI_Read(Order[1]); //写入完成后读取一次确认
- if(ccc<16)
- Serial.print("0x0");else Serial.print("0x");
- Serial.println(ccc,HEX);
- }
- if(Order[0] == 'R') //读命令时执行
- {
- Serial.print(inputString);
- byte aaa = SPI_Read(Order[1]); //读取
- Serial.print("Read 0x"); Serial.print(Order[1],HEX); Serial.print(" is: ");
- if(aaa<16)
- Serial.print("0x0");else Serial.print("0x");
- Serial.println(aaa,HEX);
- }
- //数据复位
- Serial.println("");
- stringComplete = false;
- inputString = "";
- Order[0] = 0;
- Order[1] = 0;
- Order[2] = 0;
- }
- }
复制代码
下面代码复制粘贴后文件名保存为 nrf.h 放在ardino的libraries下可以新建一个RNF04L01的文件夹下 - #ifndef _NRF_H_
- #define _NRF_H_
- #include "Arduino.h"
- //引脚
- #define CE 9 // 模式控制
- #define CSN 13 //SS片选 ,LOW工作
- #define SCK 11
- #define MISO 12
- #define MOSI 10
- #define IRQ 2 //中断信号
- #define TX_ADR_WIDTH 5 // 5 bytes TX(RX) address width
- #define TX_PLOAD_WIDTH 32 // 32 bytes TX payload
- #define READ_REG 0x00 // Define read command to register
- #define WRITE_REG 0x20 // Define write command to register
- #define RD_RX_PLOAD 0x61 // Define RX payload register address
- #define WR_TX_PLOAD 0xA0 // Define TX payload register address
- #define FLUSH_TX 0xE1 // Define flush TX register command
- #define FLUSH_RX 0xE2 // Define flush RX register command
- #define REUSE_TX_PL 0xE3 // Define reuse TX payload register command
- #define NOP 0xFF // Define No Operation, might be used to read status register
- //***************************************************//
- // SPI(nRF24L01) registers(addresses)
- #define CONFIG 0x00 // 'Config' register address
- #define EN_AA 0x01 // 'Enable Auto Acknowledgment' register address
- #define EN_RXADDR 0x02 // 'Enabled RX addresses' register address
- #define SETUP_AW 0x03 // 'Setup address width' register address
- #define SETUP_RETR 0x04 // 'Setup Auto. Retrans' register address
- #define RF_CH 0x05 // 'RF channel' register address
- #define RF_SETUP 0x06 // 'RF setup' register address
- #define STATUS 0x07 // 'Status' register address
- #define OBSERVE_TX 0x08 // 'Observe TX' register address
- #define CD 0x09 // 'Carrier Detect' register address
- #define RX_ADDR_P0 0x0A // 'RX address pipe0' register address
- #define RX_ADDR_P1 0x0B // 'RX address pipe1' register address
- #define RX_ADDR_P2 0x0C // 'RX address pipe2' register address
- #define RX_ADDR_P3 0x0D // 'RX address pipe3' register address
- #define RX_ADDR_P4 0x0E // 'RX address pipe4' register address
- #define RX_ADDR_P5 0x0F // 'RX address pipe5' register address
- #define TX_ADDR 0x10 // 'TX address' register address
- #define RX_PW_P0 0x11 // 'RX payload width, pipe0' register address
- #define RX_PW_P1 0x12 // 'RX payload width, pipe1' register address
- #define RX_PW_P2 0x13 // 'RX payload width, pipe2' register address
- #define RX_PW_P3 0x14 // 'RX payload width, pipe3' register address
- #define RX_PW_P4 0x15 // 'RX payload width, pipe4' register address
- #define RX_PW_P5 0x16 // 'RX payload width, pipe5' register address
- #define FIFO_STATUS 0x17 // 'FIFO Status Register' register address
- #define STA_MARK_RX 0X40
- #define STA_MARK_TX 0X20
- #define STA_MARK_MX 0X10
- void NRF_begin();
- byte SPI_RW(byte BYTE); // Single SPI read/write
- byte SPI_Read(byte reg); // Read one byte from nRF24L01
- byte SPI_RW_Reg(byte reg, byte BYTE); // Write one byte to register 'reg'
- byte SPI_Write_Buf(byte reg, byte *pBuf, byte bytes); // Writes multiply bytes to one register
- byte SPI_Read_Buf(byte reg, byte *pBuf, byte bytes); // Read multiply bytes from one register
- void RX_Mode(void);
- void TX_Mode(void);
- void NRF_power(byte P);
- void NRF_size(byte l);
- void NRF_channel(byte c);
- void NRF_init(); //未写完
- void NRF_test(); //未写完
- #endif
复制代码
下面代码复制粘贴后文件名保存为 nrf.cpp 放在ardino的libraries下可以新建一个RNF04L01的文件夹下 - #include "nrf.h"
- byte rx_buf[TX_PLOAD_WIDTH];
- byte tx_buf[TX_PLOAD_WIDTH];
- byte TX_ADDRESS[TX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01};
- void NRF_begin()
- {
- //SPI.begin();
-
- pinMode(CSN, OUTPUT);
- pinMode(SCK, OUTPUT);
- pinMode(MOSI, OUTPUT);
- pinMode(CE, OUTPUT);
- pinMode(MISO, INPUT);
-
- digitalWrite(CSN,HIGH);
- digitalWrite(CE,LOW);
- digitalWrite(SCK,LOW);
-
- //NRF_init();
- }
- byte SPI_RW(byte BYTE)
- {
- //SPI.transfer(BYTE);
- byte bit_ctr;
-
- for(bit_ctr=0;bit_ctr<8;bit_ctr++)
- {
- if((BYTE&0x80)==0){digitalWrite(MOSI,LOW);} else {digitalWrite(MOSI,HIGH);}
- // MOSI=(BYTE&0x80);
- BYTE = (BYTE<<1);
- //delayMicroseconds(500);
- digitalWrite(SCK,HIGH);
- // delayMicroseconds(500);
- BYTE |= digitalRead(MISO);
- // delayMicroseconds(500);
- digitalWrite(SCK,LOW);
- // delayMicroseconds(500);
-
- }
- return(BYTE);
- }
- // 写数据BYTE到reg寄存器
- byte SPI_RW_Reg(byte reg, byte BYTE)
- {
- byte Status;
- digitalWrite(CSN,LOW);
- Status = SPI_RW(reg);
- delayMicroseconds(20);
- SPI_RW(BYTE);
- digitalWrite(CSN,HIGH);
- return(Status);
-
- }
- //从reg寄存器读数据出来
- byte SPI_Read(byte reg)
- {
- byte reg_val;
- digitalWrite(CSN,LOW);
- SPI_RW(reg);
- reg_val = SPI_RW(0);
- digitalWrite(CSN,HIGH);
- return(reg_val);
-
- }
- //从reg寄存器中读出bytes个字节
- byte SPI_Read_Buf(byte reg, byte *pBuf, byte bytes)
- {
- byte Status,byte_ctr;
- digitalWrite(CSN,LOW);
- Status = SPI_RW(reg);
- for(byte_ctr = 0; byte_ctr < bytes; byte_ctr++)
- {
- pBuf[byte_ctr] = SPI_RW(0);
- }
- digitalWrite(CSN,HIGH);
- //Serial.println("send OK!");
- return(Status);
- }
- //把pBuf缓存中的数据写入到nRF24L01中
- byte SPI_Write_Buf(byte reg, byte *pBuf, byte bytes)
- {
- byte Status,byte_ctr;
- digitalWrite(CSN,LOW);
- Status = SPI_RW(reg);
-
- for(byte_ctr=0; byte_ctr<bytes; byte_ctr++)
- SPI_RW(*pBuf++);
-
- digitalWrite(CSN,HIGH);
-
- return(Status);
-
- }
- //设置为接收模式
- void RX_Mode(void)
- {
- digitalWrite(CE,LOW);
- 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
- SPI_RW_Reg(WRITE_REG + EN_AA, 0x01); // Enable Auto.Ack:Pipe0
- SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // Enable Pipe0
- SPI_RW_Reg(WRITE_REG + RF_CH, 40); // Select RF channel 40
- SPI_RW_Reg(WRITE_REG + RX_PW_P0, TX_PLOAD_WIDTH); // Select same RX payload width as TX Payload width
- SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); // TX_PWR:0dBm, Datarate:2Mbps, LNA:HCURR
- SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f); // Set PWR_UP bit, enable CRC(2 bytes) & Prim:RX. RX_DR enabled..
- digitalWrite(CE,HIGH);
-
- }
- void TX_Mode(void)
- {
- digitalWrite(CE,LOW);
-
- SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH); // Writes TX_Address to nRF24L01
- SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // RX_Addr0 same as TX_Adr for Auto.Ack
- SPI_Write_Buf(WR_TX_PLOAD, tx_buf, TX_PLOAD_WIDTH); // Writes data to TX payload
- SPI_RW_Reg(WRITE_REG + EN_AA, 0x01); // Enable Auto.Ack:Pipe0
- SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // Enable Pipe0
- SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x1a); // 500us + 86us, 10 retrans...
- SPI_RW_Reg(WRITE_REG + RF_CH, 40); // Select RF channel 40
- SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); // TX_PWR:0dBm, Datarate:2Mbps, LNA:HCURR
- SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);
- digitalWrite(CE,HIGH);
-
- }
- void NRF_power(byte P)
- {
- digitalWrite(CE,LOW);
-
- if(P==3)SPI_RW_Reg(0x06,0x27); //0db 修正之前注释错误
- else if(P==2)SPI_RW_Reg(0x06,0x25); //-6db
- else if(P==1)SPI_RW_Reg(0x06,0x23); //-12db
- else if(P==0)SPI_RW_Reg(0x06,0x21); //-18db
-
- digitalWrite(CE,HIGH);
- }
- void NRF_size(byte l)
- {
- digitalWrite(CE,LOW);
- SPI_RW_Reg(0x11,l);
- digitalWrite(CE,HIGH);
- }
- void NRF_channel(byte c)
- {
- digitalWrite(CE,LOW);
- SPI_RW_Reg(0x05,c);
- digitalWrite(CE,HIGH);
- }
- void NRF_init()
- {
- digitalWrite(CE,LOW);
- //SCK=0;
- SPI_RW_Reg(0x01,0x00); //禁止 自动应答
- SPI_RW_Reg(0x02,0x01); //允许 P0信道
- SPI_RW_Reg(0x04,0x00); //禁止 自动重发
- TX_Mode();
- NRF_channel(66);
- NRF_power(1);
- NRF_size(11);
- digitalWrite(CE,HIGH);
- // TX_address(address);
- // RX_address(address);
- }
- /*
-
复制代码
|