关于NRF24l01的调试
- 格式:docx
- 大小:84.59 KB
- 文档页数:20
nRF24L01 无线模块用户手册目录产品概述 (3)基本特性 (3)引脚接口说明 (4)模块尺寸 (6)nRF2401工作模式 (7)Enhanced ShockBurstTM收发模式 (7)Enhanced ShockBurstTM数据发送流程 (8)空闲模式 (9)关机模式 (9)nRF24L01模块参数设置 (9)主要参数设置 (10)程序设计分析 (10)nRF24L01初始化 (10)nRF24L01SPI写操作 (11)nRF24L01 SPI读操作 (11)nRF24L01写寄存器函数 (12)nRF24L01连续读多个寄存器函数 (12)nRF24L01连续写多个寄存器函数 (12)nRF24L01接收模式设置 (13)nRF24L01接收数据流程 (13)nRF24L01发送数据流程 (13)无线应用注意事项 (14)我们的承诺 (15)产品概述nRF24L01是挪威NordicVLSI公司出品的一款新型射频收发器件,采用4 mm×4 mm QFN20封装;nRF24L01工作在ISM频段:2.4~2.524 GHz。
且内置频率合成器、功率放大器、晶体振荡器、调制器等功能,并融合增强型ShockBurst技术,其中地址、输出功率和通信频道可通过程序进行配置,适合用于多机通信。
nRF24L01功耗很低,在以-6 dBm的功率发射时,工作电流也只有9 mA;而对应接收机的工作电流只有12.3 mA,多种低功率工作模式(掉电模式和空闲模式)使节能设计更方便。
nRF24L01在业界领先的低功耗特点使其特别适合采用钮扣电池供电的2.4G应用,整个解决方案包括链路层和MultiCeiver功能提供了比现有的 nRF24XX 更多的功能和更低的电源消耗,与目前的蓝牙技术相比在提供更高速率的同时,而只需花更小的功耗基本特性(1) 2.4Ghz全球开放ISM 频段免许可证使用(2) 最高工作速率2Mbps,高效GFSK调制,抗干扰能力强(3) 125频道,满足多点通信和跳频通信需要(4) 内置硬件CRC 检错和点对多点通信地址控制(5) 低功耗1.9 - 3.6V 工作,适合电池供电应用(6) 待机模式下状态为22uA;掉电模式下为900nA(7) 模块可软件设地址,只有收到本机地址时才会输出数据(提供中断指示),可直接接各种单片机使用,软件编程非常方便(8) 内置专门稳压电路,即使开关电源也有很好的通信效果(9) 标准DIP间距接口,便于嵌入式应用(10)具有自动应答机制,和CRC校验,数据通讯稳定可靠。
CE 输入RX 或TX 模式选择
CSN 输入SPI 片选信号, 低电平使能,硬件不能直接接低电平
SCK 输入SPI 时钟最高8MHZ
MOSI 输入从SPI 数据输入脚
MISO 三态输出从SPI 数据输出脚
IRQ 输出中断低电平使能 TX_DS或MAX_RT或RX_DR高电平,IRQ产生中断低电平
写寄存器只有在掉电模式和待机模式下可操作。
TX模式
填充TX FIFO
PWR_UP=1,PRIM_RX=0,
CE=1(高电平持续时间最小为10us),启动发射
每一条指令的执行都必须通过一次CSN 由高到低的变化
CE一直高,TX FIFO有数据就一直发射,TX FIFO空就进入待机2模式,在这个模式,TX FIFO有数据就进入发送模式开始发送数据。
CE大于10US高脉冲,只能发一包,发完进入待机1模式。
发送数据宽度是动态的吗?
PWR_UP=1,PRIM_RX=1,CE=1启动接收
CE=0进入待机模式1
载波检测cd,跳频,
发送模块没有成功发送数据时,发送端PLOS_CNT显示数据包丢失率太高时可将其设置位接收模式检测CD值如果CD为高说明通道出现了拥挤现象需要更改通信频道
时序
SPI指令,
F340
无线传32字节,是8位一组,4组,
STM32F103
一个8位,一个32位,如何通信
两个的FLASH地址位多了,
SPI接口几位,
如何送到FIFO
测试。
nrf24l01发送接收一体程序(以调通-解决了接收端只能接收一次的问题)基于单片机无线报警系统主机(机载设别)系统程序/**************************************************/#include#include#define uchar unsigned char/***************************************************/#define TX_ADR_WIDTH 5 // 5字节宽度的发送/接收地址#define TX_PLOAD_WIDTH 4 // 数据通道有效数据宽度uchar code TX_ADDRESS[TX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01}; // 定义一个静态发送地址uchar RX_BUF[4]={0,0,0,0};uchar TX_BUF[4]={0x20,0x20,0x20,0x20};uchar flag; //无线模块接受数据标志uchar DATA = 0x01;uchar bdata sta;unsigned int a,k; //定时器延时参数uchar mark; //传感器响应标志位uchar mark1; // 延时标志位sbit RX_DR = sta^6; //接受数据成功标志位sbit TX_DS = sta^5; // 发送成功标志位sbit MAX_RT = sta^4; //最大重发上限标志位sbit HW=P2^0; //红外感应模块输入端sbit ZD=P1^7; //震动传感器输入端sbit LED=P2^1; //LED报警器输出端sbit SDA=P2^4; //语音模块数据控制端sbit ONN=P2^3; //语音芯片电源控制端口sbit FM =P2^5; //蜂鸣器/**************************************************函数:delayus()描述:延迟x微秒/**************************************************/void delayus(unsigned int t){while(t--);}函数:delayms()描述:延迟x毫秒/**************************************************/void delayms(unsigned int h){unsigned int j;while(h--)for(j=85;j>0;j--);}/************************************************** 函数:delays()描述:延迟x.xx秒/**************************************************/void delays( float h){unsigned int i,j;h*=100;while(h--){for(i=0;i<235;i++)for(j=0;j<3;j++);}}/************************************************** 函数:sendadd()描述:语音模块发送地址信号/**************************************************/ void sendadd(unsigned char addr){uchar i;SDA=0;delayms(5); /* 数据信号置于低电平5ms */for(i=0;i<8;i++){ SDA=1;if(addr & 1){delayus(60); /* 高电平比低电平为600us:200us,表示发送数据1 */SDA=0;delayus(20);}else{delayus(20);SDA=0; /* 高电平比低电平为200us:600us,表示发送数据0 */ delayus(60);}addr>>=1;}SDA=1;}/**************************************************函数:SPI_RW()描述:根据SPI协议,写一字节数据到nRF24L01,同时从nRF24L01 读出一字节/**************************************************/uchar SPI_RW(uchar byte){uchar i;for(i=0; i<8; i++) // 循环8次{MOSI = (byte & 0x80); // byte最高位输出到MOSIbyte <<= 1; // 低一位移位到最高位SCK = 1; // 拉高SCK,nRF24L01从MOSI读入1位数据,同时从MISO输出1位数据byte |= MISO; // 读MISO到byte最低位SCK = 0; // SCK置低}return(byte); // 返回读出的一字节}/**************************************************//**************************************************函数:SPI_RW_Reg()描述:写数据value到reg寄存器/**************************************************/ uchar SPI_RW_Reg(uchar reg, uchar value){uchar status;CSN = 0; // CSN置低,开始传输数据status = SPI_RW(reg); // 选择寄存器,同时返回状态字SPI_RW(value); // 然后写数据到该寄存器CSN = 1; // CSN拉高,结束数据传输return(status); // 返回状态寄存器}/**************************************************函数:clear()描述:清TX_FIFO寄存器/**************************************************/void clear(){CSN=0;SPI_RW(FLUSH_TX);CSN=1;}/**************************************************函数:SPI_Read()描述:从reg寄存器读一字节/**************************************************/ uchar SPI_Read(uchar reg){uchar reg_val;CSN = 0; // CSN置低,开始传输数据SPI_RW(reg); // 选择寄存器reg_val = SPI_RW(0); // 然后从该寄存器读数据CSN = 1; // CSN拉高,结束数据传输return(reg_val); // 返回寄存器数据}/**************************************************//**************************************************函数:SPI_Read_Buf()描述:从reg寄存器读出bytes个字节,通常用来读取接收通道数据或接收/发送地址/**************************************************/ uchar SPI_Read_Buf(uchar reg, uchar * pBuf, uchar bytes){uchar status, i;CSN = 0; // CSN置低,开始传输数据status = SPI_RW(reg); // 选择寄存器,同时返回状态字for(i=0; i<="" p="">pBuf[i] = SPI_RW(0); // 逐个字节从nRF24L01读出CSN = 1; // CSN拉高,结束数据传输return(status); // 返回状态寄存器}/**************************************************//**************************************************函数:SPI_Write_Buf()描述:把pBuf缓存中的数据写入到nRF24L01,通常用来写入发射通道数据或接收/发送地址/**************************************************/ uchar SPI_Write_Buf(uchar reg, uchar * pBuf, uchar bytes){uchar status, i;CSN = 0; // CSN置低,开始传输数据status = SPI_RW(reg); // 选择寄存器,同时返回状态字for(i=0; i<="" p="">SPI_RW(pBuf[i]); // 逐个字节写入nRF24L01CSN = 1; // CSN拉高,结束数据传输return(status); // 返回状态寄存器}/**************************************************函数:RX_Mode()描述:这个函数设置nRF24L01为接收模式,等待接收发送设备的数据包/**************************************************/void RX_Mode(void){CE = 0;SPI_RW_Reg(WRITE_REG + CONFIG, 0x0F); // CRC使能,16位CRC校验,上电,接收模式CE = 1;delayus(200); // 拉高CE启动接收设备}/**************************************************函数:TX_Mode()描述:这个函数设置nRF24L01为发送模式,(CE=1持续至少10us),130us后启动发射,数据发送结束后,发送模块自动转入接收模式等待应答信号。
一位从厌倦调试NRF24L01无线模块到成功的收发经验分享拿到这对小家伙的时候,距离现在已经有好几个月了吧。
直到大概一个月前,才将它们从抽屉里拿出来。
之所以一直搁置着,是因为想要靠自己来驱动它们。
厌倦了那种拿到模块到处找例程的感觉。
不过,这也让人吃尽了苦头。
熬了多少个夜晚,看了多少遍datasheet,甚至因为实在太困难了,所以索性再次搁在一边,拿了个较为简单的1302寻寻feel。
这一搁置,又过了一个多星期。
大学时间真的太紧张了,各种各样无聊的课占据了平日的大部分时间。
周末,才感觉是为自己活着的日子。
第三次——真正的战役,持续时间并不算长,相对于前两次的铺垫来说。
熬了一个星期的夜,时间总是在不知不觉之间溜走,往往回过神来才发觉,大家都睡下了,已经三四点了。
不过喜欢这宁静的夜,也再一次深深的体会到,走这条路的人是没有夜晚的,因为深夜才是最高效的时间。
不过,这次的收尾工作却是在今天早上进行的。
前天晚上进行最终的测试,两台机都装的自己程序,结果接收机反馈回来乱码,经过昨天一整天的排查才知道是连续读数据函数不能表达。
修修改改忙了一整晚,到昨晚三四点还是没有突破,索性睡个觉。
今早九点,熟睡中一个激灵醒过来,打开电脑再看看,还是不行。
失望中倒头呼呼大睡,十一点再次醒来,牙都还没刷,问题就解决了。
虽然如此,但是心里还是带着小小的疑问,不明白究竟为什么。
这里实现的功能比较简单:通过PC串口给发送端写入要传送的数据(定长32字节),接收端将收到的数据反馈给PC。
由于只有一台电脑,所以只能够同时开两个串口调试窗来观察。
不过这里面的收获也挺多的,例如对C的指针有了较为直观、深刻的感受,再如数据交换函数的写法,还有就是包含串口通信在内所有程序都是参考数据手册写的。
nrf24l01使用与调试经验总结(包括一收多发--1主机最多6从机)----------------------------------------------------------------------------------------------------------------------------主要特性工作在2.4GHz ISM 频段调制方式:GFSK/FSK数据速率:2Mbps/1Mbps/250Kbps超低关断功耗:<0.7uA超低待机功耗:<15uA快速启动时间:<130uS内部集成高PSRR LDO宽电源电压范围:1.9-3.6V数字IO 电压: 3.3V/5V低成本晶振:16MHz±60ppm接收灵敏度:<-83dBm @2MHz最高发射功率:7dBm接收电流(2Mbps):<15mA发射电流(2Mbps):<12mA(0dBm)10MHz 四线SPI 模块内部集成智能ARQ 基带协议引擎收发数据硬件中断输出支持1bit RSSI 输出极少外围器件,降低系统应用成本QFN20 封装或COB 封装注意:C代表了命令,S表示寄存器值,D表示数据写数据:SPI写命令+寄存器地址----->SPI写入数据读数据:SPI写寄存器地址(可以使用读命令+寄存器地址)----->SPI读取数据不论是读取或者写入数据,甚至是读/写len长度的数据都要先写寄存器地址;总的来说时候就三个模式:1.待机模式(待机模式+掉电省电模式)2.发送模式3.接受模式具体各个模式介绍参考数据手册。
----------------------------------------------------------------------------------------------------------------nrf发送数据是以包来发送。
NRF24L01 :在通信中的应用方法,经验总结(1)2011-07-31 13:15首先说一下:是一款新型单片射频收发器件,工作于 GHz~ GHz ISM频段。
内置频率合成器、功率放大器、晶体振荡器、调制器等功能模块,并融合了增强型ShockBurst技术,其中输出功率和通信频道可通过程序进行配置。
nRF24L01功耗低,在以-6 dBm的功率发射时,工作电流也只有9 mA;接收时,工作电流只有 mA,多种低功率工作模式(掉电模式和空闲模式)使节能设计更方便。
是想将这个IC调通,首先要多读一下技术文档:下载技术文档以下C51驱动的源代码库()此库文件适合发送端使用,在接收端会有所不同,请看第 2 部分的分析在使用过程中,需要引用SCK = 1; uchar |=MISO; then set SCK lowagain } return(uchar); . SPI_RW(reg);reg_val = SPI_RW(0); then read registervalue CSN =1; and write value to it.. CSN =1; // CSN highagain return(status); // return nRF24L01 statusuchar } /****************************************************************************************************/ /*函数:uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars) /*功能: 用于读数据,reg:为寄存器地址,pBuf:为待读出数据地址,uchars:读出数据的个数/*************************************************************************************** *************/ uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars) { uintstatus,uchar_ctr; CSN = 0; // Set CSN low, init SPI tranaction status = SPI_RW(reg); // Select register to write to and read statusuchar for(uchar_ctr=0;uchar_ctr<uchars;uchar_ctr++) pBuf[uchar_ctr] =SPI_RW(0); // CSN =1; return(status);// return nRF24L01 statusuchar } /******************************************************************************* ************************** /*函数:uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars) /*功能: 用于写数据:reg为寄存器地址,pBuf:为待写入数据地址,uchars:写入数据的个数/*************************************************************************************** ******************/ uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars) { uint status,uchar_ctr; CSN = 0; //SPI使能status = SPI_RW(reg); for(uchar_ctr=0; uchar_ctr<uchars; uchar_ctr++)// SPI_RW(*pBuf++); CSN = 1; //关闭SPI return(status); //} /************************************************************************************ ****************/ /*函数:void SetRX_Mode(void) /*功能:数据接收配置/*************************************************************************************** *************/ void SetRX_Mode(void) { CE=0; SPI_RW_Reg(WRITE_REG + CONFIG,0x0f); // IRQ收发完成中断响应,16位CRC ,主接收CE = 1;inerDelay_us(130); } /************************************************************** ****************************************/ /*函数:unsigned char nRF24L01_RxPacket(unsigned char* rx_buf) /*功能:数据读取后放如rx_buf接收缓冲区中/*************************************************************************************** ***************/ uchar nRF24L01_RxPacket(unsigned char* rx_buf) { unsigned char revale=0; sta=SPI_Read(STATUS); // 读取状态寄存其来判断数据接收状况if(RX_DR) // 判断是否接收到数据{ Display8bit(3,0,sta); //看一下接收机状态判断一下,IC的工作状态,在正常使用过程中,这句需要去掉 CE =0; //SPI使能SPI_Read_Buf(RD_RX_PLOAD,rx_buf,TX_PLOAD_WIDTH);// read receive payload from RX_FIFO buffer revale =1; //读取数据完成标志} SPI_RW_Reg(WRITE_REG+STATUS,sta); //接收到数据后RX_DR,TX_DS,MAX_PT都置高为1,通过写1来清楚中断标志returnrevale; } /***************************************************************************** ****************************** /*函数:void nRF24L01_TxPacket(unsigned char * tx_buf) /*功能:发送 tx_buf中数据/*************************************************************************************** *******************/ void nRF24L01_TxPacket(unsigned char *tx_buf) { CE=0; //StandBy I模式SPI_Write_Buf(WRITE_REG + RX_ADDR_P0,TX_ADDRESS, TX_ADR_WIDTH); // 装载接收端地址SPI_Write_Buf(WR_TX_PLOAD, tx_buf,TX_PLOAD_WIDTH); // 装载数据SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e); // IRQ收发完成中断响应,16位CRC,主发送SPI_RW_Reg(WRITE_REG+STATUS,0X7E); CE=1; //置高CE,激发数据发送inerDelay_us(100); }NRF24L01 :在通信中的应用方法,经验总结(3)2011-07-31 13:42再接着往下说调试过程:一般拿到IC后最大的困难就是无法知道自己 IC 是否已经工作了。
nrf24l01的调试测试程序参考了很多程序,经过测试,才看懂nrf24l01的状态机,在看数据手册时很多地方都不懂后来看了关于调试的文章再自己测试了一下某些功能,只测试了一边发送,一边接收的功能,做的很妥跟网上的程序相差无几。
#include <REG51.H>#include 'NRF24L01.h'#define TX_ADDR_WIDTH 5 //本地发射地址宽度#define RX_ADDR_WIDTH 5 //本地接收地址宽度#define TX_DATA_WIDTH 3 //发送数据字节数最大字节数为32字节#define RX_DATA_WIDTH 3 //接收数据字节数unsigned char code TX_ADDRESS[TX_ADDR_WIDTH]={0x78,0x78,0x78,0x78,0x78}; //发射通道地址unsigned char code RX_ADDRESS_P0[RX_ADDR_WIDTH]={0x78,0x78,0x78,0x78,0x78 }; //接收通道地址unsigned char bdata status; //定义一个位与字节都可以访问的变量用来监控IRQ中断信号sbit RX_DR=status^6; //当接收到有效数据后变为1sbit TX_DR=status^5; //当数据发送完成后产生中断,在自动应答模式中接收到应答信号后为1sbit MAX_RT=status^4; //当达到最大重发次数后变为1,产生此中断后必须清零后才能再次通讯void usdelay(void) //误差 -0.149305555556us 12us{unsigned char a,b;for(b=1;b>0;b--)for(a=2;a>0;a--);}void usdelay130(void) //误差 -0.451388888889us{unsigned char a,b;for(b=11;b>0;b--)for(a=4;a>0;a--);}unsigned char SPI_RW(unsigned char uchar) //SPI协议{unsigned char i;for(i=0;i<8;i++){SCK=0;MOSI=uchar&0x80; //从字节高位向NRF24L01写数据uchar=uchar<<1;SCK=1;uchar=uchar|MISO; //接收从NRF24L01发过来的数据SCK=0;}return uchar; //返回读取的值}unsigned char SPI_read(unsigned char reg) //SPI读操作两个字节完成读一次 reg:读指令{unsigned char bl;CSN=0;SPI_RW(reg); //第一个字节写读某个寄存器的指令此时返回值为状态寄存器的值bl=SPI_RW(0); //第二个字节返回所需要的寄存器的值CSN=1;return bl;unsigned char SPI_write(unsigned char reg,unsigned char value) //SPI写操作写一个字节的数据先写指令说明往哪个寄存器写数据,reg:指令,value:数据{unsigned char status;CSN=0;status=SPI_RW(reg); //写指令并返回状态寄存器的值SPI_RW(value); //写数据CSN=1;return status; //返回状态寄存器的值}unsigned char SPI_read_more(unsigned char reg,unsigned char *puf_read,unsigned char width) //读取多个数据 reg:指令,*puf 存储数据的指针,width:要读取数据的长度{unsigned char i,status;CSN=0;status=SPI_RW(reg); //写指令并返回状态寄存器的值for(i=0;i<width;i++) //循环读出数据puf_read[i]=SPI_RW(0); //将循环读出的数据存储在一个数组中}CSN=1;return status;}unsigned char SPI_write_more(unsigned char reg,unsigned char *puf_write,unsigned char width) //写多个数据 reg:指令,*puf:要写的数据,width:数据长度{unsigned char i,status;CSN=0;status=SPI_RW(reg); //写指令并返回状态寄存器的值for(i=0;i<width;i++) //循环写入数据{SPI_RW(*puf_write++);}CSN=1;return status;}void receive_model() //接收模式初始化配置{CE=0; //掉电模式此时PWR_UP为0CSN=1; //停止寄存器读写SCK=0; //时钟信号停止读写IRQ=1; //中断复位SPI_write_more(WRITE_REG+RX_ADDR_P0,RX_ADDRESS_P0, RX_ADDR_WIDTH); //写本地接收的地址SPI_write(WRITE_REG+EN_AA,0x00); //失能自动应答SPI_write(WRITE_REG+EN_RXADDR,0x01); //接收通道0允许SPI_write(WRITE_REG+RF_CH,0x00); //通信频率 2.4GSPI_write(WRITE_REG+RX_PW_P0,TX_DATA_WIDTH); //设置数据通道0传输数据宽度为3个字节SPI_write(WRITE_REG+RF_SETUP,0x09); //设置发射参数:传输速率,发射功率SPI_write(WRITE_REG+CONFIG,0x0F); //配置寄存器,设置CRC 和工作模式(接收模式)CE=1; //启动}void send_model() //发射模式初始化配置{CE=0; //掉电模式此时PWR_UP为0CSN=1; //停止寄存器读写SCK=0; //时钟信号停止读写IRQ=1; //中断复位SPI_write_more(WRITE_REG+TX_ADDR,TX_ADDRESS,TX_AD DR_WIDTH); //写本地发射地址SPI_write_more(WRITE_REG+RX_ADDR_P0,RX_ADDRESS_P0,RX_ADDR_WIDTH); //写本地接收(主要用来使能自动应答) SPI_write(WRITE_REG+EN_AA,0x00); //失能自动应答功能SPI_write(WRITE_REG+EN_RXADDR,0x00); //失能接收通道0 SPI_write(WRITE_REG+SETUP_RETR,0x00); //失能自动重发SPI_write(WRITE_REG+RF_CH,0x00); //通信频率 2.4GSPI_write(WRITE_REG+RX_PW_P0,RX_DATA_WIDTH); //设置接收数据长度为3个字节SPI_write(WRITE_REG+RF_SETUP,0x09); //设置发射参数:传输速率2MHZ,发射功率SPI_write(WRITE_REG+CONFIG,0x0e); //配置寄存器,设置CRC 和工作模式(发射模式)CE=1; //启动}void send_data(unsigned char *pArray) //发送数据{CE=0; //停止射频工作,待机模式1//SPI_write_more(WRITE_REG+RX_ADDR_P0,TX_ADDRESS,TX_ADD R_WIDTH); //装载接收端地址此地址单对单发送时可只初始化一次SPI_write_more(WR_TX_PLOAD,pArray,3); //向nrf24l01传入要发送的数据// SPI_write(WRITE_REG+CONFIG,0x0e); //IRQ收发完成中断响应,16位CRC,发送模式此功能在但对单发送时可初始化一次CE=1; //置高CE,激发数据usdelay(); //10us的延时}unsigned char read_data(unsigned char *pArray) //读取接收的数据 *pArray:传入存放数据的数组{unsigned char mark;CE=1; //接收模式usdelay130(); //等待130usstatus=SPI_read(STATUS); //读取状态寄存器的值来判断数据的接收情况if(RX_DR==1){CE=0; //接收完数据变成待机模式1SPI_read_more(RD_RX_PLOAD,pArray,RX_DATA_WIDTH); //读取数据并将其存放在RX_DATA中mark=1; //读取完成标志}SPI_write(WRITE_REG+STATUS,status); //清除中断标志位return mark;}/************************************************************** ************************************************/void main(){unsigned char mark,i; //接收完成标志delay(1000);receive_model(); //接收模式初始化配置delay(1000);while(1){mark=read_data(RX_DATA);if(mark==1){i++;SPI_write(FLUSH_RX,0); //清空RX_FIFO 中的缓存数据接受不像发送会自动清空缓存区的数据if(RX_DATA[0]==0&&RX_DATA[1]==1&&RX_DATA[2]==2) {led0=0;delay(100);RX_DATA[0]=0;RX_DATA[1]=0;RX_DATA[2]=0;}mark=0;}SPI_read(STATUS); //读状态寄存器SPI_read(FIFO_STATUS); //读FIFO状态led0=1;}}。
NRF24L01 :在通信中的应用方法,经验总结〔1〕2021-07-31 13:15首先说一下:单片射频收发器件,工作于2.4 GHz~2.5 GHz ISM频段。
内置频率合成器、功率放大器、晶体振荡器、调制器等功能模块,并融合了增强型ShockBurst技术,其中输出功率和通信频道可通过程序进行配置。
nRF24L01功耗低,在以-6 dBm的功率发射时,工作电流也只有9 mA;接收时,工作电流只有12.3 mA,多种低功率工作模式(掉电模式和空闲模式)使节能设计更方便。
是想将这个IC调通,首先要多读一下技术文档:下载技术文档以下C51驱动nRF24.L01 的源代码库〔nRF24.L01.h〕此库文件适合发送端使用,在接收端会有所不同,请看第2 局部的分析在使用过程中,需要引用//****************************************NRF24L01端口定义***************************************sbit CE =P2^0;sbit CSN =P2^1;sbit SCK =P2^2;sbit MOSI =P2^3;sbit MISO =P2^4;sbit IRQ =P2^5;//*********************************************NRF24L01********* ****************************#define TX_ADR_WIDTH 5 // 接收地址宽度,一般设置为5 不要动它#define RX_ADR_WIDTH 5 // 接收地址宽度,一般设置为5 不要动它#define TX_PLOAD_WIDTH 1 //接收数据的数据宽度〔最大为32 字节〕,这里我设置为最小的1 字节,方便调试#define RX_PLOAD_WIDTH 1 //发送数据的数据宽度〔最大为32 字节〕,这里我设置为最小的1 字节,方便调试uchar const TX_ADDRESS[TX_ADR_WIDTH]={0x35,0x43,0x10,0x10,0x03}; // 这里就是设置了5 个字节的本地地址/*此处的地址:在IC内部真实地址是反过来的。
nRF24L01点对点跳频技术应用(转载)分类:技术应用关键字:nRF24L01;射频;无线通信;跳频1 nRF24L01概述nRF24.L01是一款新型单片射频收发器件,工作于2.4 GHz~2.5 GHz ISM频段。
内置频率合成器、功率放大器、晶体振荡器、调制器等功能模块,并融合了增强型ShockBurst技术,其中输出功率和通信频道可通过程序进行配置。
nRF24L01功耗低,在以-6 dBm的功率发射时,工作电流也只有9 mA;接收时,工作电流只有12.3 mA,多种低功率工作模式(掉电模式和空闲模式)使节能设计更方便。
nRF24L01主要特性如下:GFSK调制:硬件集成OSI链路层;具有自动应答和自动再发射功能;片内自动生成报头和CRC校验码;数据传输率为l Mb/s或2Mb/s;SPI速率为0 Mb/s~10 Mb/s;125个频道:与其他nRF24系列射频器件相兼容;QFN20引脚4 mm×4 mm封装;供电电压为1.9 V~3.6 V。
2 引脚功能及描述nRF24L01的封装及引脚排列如图1所示。
各引脚功能如下:图 (1)CE:使能发射或接收;CSN,SCK,MOSI,MISO:SPI引脚端,微处理器可通过此引脚配置nRF24L01:IRQ:中断标志位;VDD:电源输入端;VSS:电源地:XC2,XC1:晶体振荡器引脚;VDD_PA:为功率放大器供电,输出为1.8 V;ANT1,ANT2:天线接口;IREF:参考电流输入。
3 工作模式通过配置寄存器可将nRF241L01配置为发射、接收、空闲及掉电四种工作模式,如表1所示。
模式 PWR_UP PRIM_RX CE FIFO寄存器状态 接收模式 1 1 1 -发射模式 1 0 1 数据在TX FIFO 寄存器中发射模式 1 0 1→0 停留在发送模式,直至数据发送完待机模式2 1 0 1 TX FIFO 为空待机模式1 1 - 0 无数据传输掉电 0 - - -表 (1)待机模式1主要用于降低电流损耗,在该模式下晶体振荡器仍然是工作的;待机模式2则是在当FIFO寄存器为空且CE=1时进入此没收;待机模式下,所有配置字仍然保留。
NRF24L01多路通讯调试成功的关键(附基于串口助手的无线通讯工具源代码)本文档部分容摘自网络,由于按照网上教程调试总不成功,特此分享自己的失败经验(红字加重)。
一、收发端共同的设置1、设置信道工作频率(收发必须一致)如:SPI_RW_Reg(WRITE_REG+RF_CH,40);2、设置发射速率(2mbps或1mbps)和发射功率(收发必须一致);如:SPI_RW_Reg(WRITE_REG+RF_SETUP,0x0f); //发射速率为2Mbps,发射功率最大为0dB二、接收端的设置(最关键)1、设置频道0-5,自动ACK应答允许如: SPI_RW_Reg(WRITE_REG+EN_AA,0x3f);2、设置接收通道全部允许如: SPI_RW_Reg(WRITE_REG+EN_RXADDR,0x3f);3、向发送地址寄存器写入本地地址(5byte)4、向各个频道的接收地址寄存器写入接收地址(调试成不成功的关键)频道0:5个字节的地址频道1:5个字节的地址(和频道0的地址必须不同)频道2:1个字节的地址(为该通道发射机地址的最后一个字节·)有一个配置为发射模式的24l01要通过该通道与接收机通信,发射机的本地地址为{0x37,0xa1,0xb3,0xc9,0xda};则接收机频道2的地址为(0x37)频道3:1个字节的地址(同上)频道4:1个字节的地址(同上)频道5:1个字节的地址(同上)5、向每个频道(用那个写那个,需要在上面配置允许通道接收和ack·)接收数据长度寄存器写入接收数据宽度(最快均为32)频道n:SPI_RW_Reg(WRITE_REG + RX_PW_Pn, RX_PLOAD_WIDTH);如:频道5:SPI_RW_Reg(WRITE_REG + RX_PW_P5, RX_PLOAD_WIDTH);6、配置为接收模式如:SPI_RW_Reg(WRITE_REG+CONFIG,0x0f);下面附上我的程序/***************************头文件******************************/#ifndef __NRF24L01_H__#define __NRF24L01_H__sbit CE = P1^2;sbit CSN = P1^3;sbit IRQ = P1^4;sbit MOSI = P1^5;sbit MISO = P1^6;sbit SCK = P1^7;void NRF24L01_init(); //初始化模块uchar TX_packet(uchar *tx_buf); //返回值判断是否成功uchar RX_packet(uchar *rx_buf); //返回值判断是否成功void TX_MODE(); //发送模式void RX_MODE(); //接收模式//****************************************************************//// SPI(nRF24L01) commands#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//***************************************************************//#endif/*********************************程序***********************************/ #include<reg52.h>#include"define.h"#include"DELAY.h"#include"NRF24L01.h"uchar code TX_ADDRESS[5] = {0,1,1,1,1};uchar code RX_ADDRESS_P0[5] = {0,1,1,1,1};uchar code RX_ADDRESS_P1[5] = {1,1,1,1,1};uchar code RX_ADDRESS_P2[1] = {2};uchar code RX_ADDRESS_P3[1] = {3};uchar code RX_ADDRESS_P4[1] = {4};uchar code RX_ADDRESS_P5[1] = {5};uchar code Data_width = 1;uchar code Data_rt = 15;uchar SPI_RW(uchar dat) // SPI读写指令{uchar i;for(i=0;i<8;i++){SCK = 0;MOSI = (dat&0x80);dat <<= 1;SCK = 1;dat |= MISO;}SCK = 0;return dat;}uchar NRF24L01_read_reg(uchar reg) //读某个寄存器的状态{uchar value;CSN=0; //拉低CSN,允许操作SPI_RW(reg); //写寄存器指令value = SPI_RW(0); //读寄存器值CSN=1; //拉高CSN,禁止操作return value; //返回寄存器状态}uchar NRF24L01_write_reg(uchar reg,uchar value) //写向某个寄存器写指令,并读出状态{uchar status;CSN=0; //拉低CSN,允许操作status = SPI_RW(reg); //写寄存器指令,并读出寄存器状态SPI_RW(value); //写寄存器值CSN=1; //拉高CSN,禁止操作return status; //返回寄存器之前的值}uchar NRF24L01_read_buf(uchar reg,uchar *pbuf,uchar n){uchar i,status;CSN=0; //拉低CSN,允许操作status = SPI_RW(reg); //写寄存器指令,并读出寄存器状态for(i=0;i<n;i++)pbuf[i] = SPI_RW(0);//从寄存器读出一字节数据CSN = 1; //拉高CSN,禁止操作return status;}uchar NRF24L01_write_buf(uchar reg,uchar *pbuf,uchar n){uchar i,status;CSN=0; //拉低CSN,允许操作status = SPI_RW(reg); //写寄存器指令,并读出寄存器状态for(i=0;i<n;i++)SPI_RW(pbuf[i]); //写一字节数据到寄存器CSN = 1; //拉高CSN,禁止操作return status;}void NRF24L01_init(){CE = 0; //射频电路工作使能,高电平工作,低电平停止CSN = 1; //SPI操作高电平允许,低电平禁止SCK = 0; //时钟拉低,禁止读写寄存器IRQ = 1; //中断复位,等待产生中断信号NRF24L01_write_reg(WRITE_REG + EN_AA, 0x3f); //所有接受通道允许自动应答NRF24L01_write_reg(WRITE_REG + EN_RXADDR, 0x3f); //接收通道全部打开NRF24L01_write_reg(WRITE_REG + SETUP_AW, 0x03); //设置接收/发射地址宽度为5字节NRF24L01_write_reg(WRITE_REG + SETUP_RETR, Data_rt); //自动发送间隔250+86us,次数15NRF24L01_write_reg(WRITE_REG + RF_CH, 0x00); //设置信道工作为2.4Ghz,收发必须一致NRF24L01_write_reg(WRITE_REG + RX_PW_P0, Data_width); //设置通道0数据字节数NRF24L01_write_reg(WRITE_REG + RX_PW_P1, Data_width); //设置通道1数据字节数NRF24L01_write_reg(WRITE_REG + RX_PW_P2, Data_width); //设置通道2数据字节数NRF24L01_write_reg(WRITE_REG + RX_PW_P3, Data_width); //设置通道3数据字节数NRF24L01_write_reg(WRITE_REG + RX_PW_P4, Data_width); //设置通道4数据字节数NRF24L01_write_reg(WRITE_REG + RX_PW_P5, Data_width); //设置通道5数据字节数NRF24L01_write_reg(WRITE_REG + RF_SETUP, 0x0f); //发送速率为1Mhz,发送功率最大值0dbNRF24L01_write_buf(WRITE_REG + TX_ADDR,TX_ADDRESS,5); //写本机地地址NRF24L01_write_buf(WRITE_REG + RX_ADDR_P0,RX_ADDRESS_P0,5); //写数据通道0接收机地址NRF24L01_write_buf(WRITE_REG + RX_ADDR_P1,RX_ADDRESS_P1,5); //写数据通道1接收机地址NRF24L01_write_buf(WRITE_REG + RX_ADDR_P2,RX_ADDRESS_P2,1); //写数据通道2接收机地址NRF24L01_write_buf(WRITE_REG + RX_ADDR_P3,RX_ADDRESS_P3,1); //写数据通道3接收机地址NRF24L01_write_buf(WRITE_REG + RX_ADDR_P4,RX_ADDRESS_P4,1); //写数据通道4接收机地址NRF24L01_write_buf(WRITE_REG + RX_ADDR_P5,RX_ADDRESS_P5,1); //写数据通道5接收机地址void RX_MODE(){NRF24L01_write_reg(WRITE_REG + CONFIG, 0x0f);//IRQ收发完成中断响应,16位CRC,接收模式CE = 1;}void TX_MODE(){NRF24L01_write_reg(WRITE_REG + CONFIG, 0x0e);//IRQ收发完成中断响应,16位CRC,发送模式CE = 1;}uchar TX_packet(uchar *tx_buf){uchar tx_flag = 1;CE = 0; //停止射频电路工作NRF24L01_write_reg(WRITE_REG + STATUS,0xff); //清除中断标志位NRF24L01_write_buf(WR_TX_PLOAD,tx_buf,Data_width); //装载要发送的数据CE = 1; //置高CE,激发数据发送Delay_ms(Data_rt/2);if(NRF24L01_read_reg(STATUS)&0x10)tx_flag = 0;return(tx_flag);}uchar RX_packet(uchar *rx_buf){uchar revalue = 0,sta;sta = NRF24L01_read_reg(STATUS); //读状态寄存器if(sta&0x40) //如果接受中断标志位为1{CE = 0; //SPI使能NRF24L01_read_buf(RD_RX_PLOAD,rx_buf,Data_width); //读取数据revalue = 1; //读取数据完成标志置1NRF24L01_write_reg(WRITE_REG + STATUS,0xff); //清除中断标志位}CE = 1;return revalue;//返回读取数据完成标志}。
nRF24L01无线通信模块使用手册12要点nRF24L01是一种常用的无线通信模块,广泛应用于无线遥控、智能家居、物联网等领域。
本文主要介绍nRF24L01无线通信模块的使用手册12要点。
1. 硬件连接将nRF24L01模块插入Arduino板的SPI接口上,然后将CE、CSN、SCK、MOSI、MISO分别连接到Arduino板的Digital口上。
2. 初始化模块在使用nRF24L01模块之前,必须对其进行初始化,在初始化代码中需要指定通信频率、发射功率、数据通道等等。
3. 设置通信频率nRF24L01可以在2.4GHz频段内进行无线通信,可以通过设置通信频率来避免干扰。
通信频率的设置需要与对方设备的频率相匹配。
4. 设置发射功率nRF24L01具有多个发射功率级别,选择发射功率级别需要权衡通信距离和电池寿命。
5. 设置数据通道nRF24L01具有多个数据通道,可以在多个设备之间相互独立传输数据。
6. 选择传输模式nRF24L01可以选择多种不同的传输模式,包括单向、双向、广播等。
7. 发送数据使用nRF24L01发送数据时,需要将数据写入到缓冲区中,并指定接收方的地址。
8. 接收数据使用nRF24L01接收数据时,需要将接收方的地址写到接收方地址寄存器中,然后从缓冲区中读取数据。
9. 检查模块状态使用nRF24L01时需要进行状态检查,可以检查发送、接收、空闲、数据发送完成等等状态。
10. 错误处理在进行nRF24L01通信时,可能会发生各种各样的错误,需要进行错误处理。
11. 调试技巧在进行nRF24L01调试时,可以使用串口进行调试,输出各种调试信息。
12. 应用注意事项在进行nRF24L01的应用时,需要注意如下事项:避免干扰、选择合适的电源、防止数据丢失等等。
以上为nRF24L01无线通信模块使用手册12要点,希望对大家有所帮助。
单片机+nRF24L01+调试笔记一切从头说起吧。
这个月本来想做一个东西:遥控开门。
想法来源是,经常晚上或是终于都睡觉了还有人来敲门,又不做声,感觉真的好烦,敲门你就做个声或是直接说有什么事,烦的是他就是不做声。
这种门我是不开的。
于是就有那种想法。
大致流程是:发射机遥控发送开门信号,接收机接收信号,控制步进电机完成开门动作,门开完之后接收机再返回一个完毕信号给发射机。
半天的时间就吧电路给焊接好了,可是3.3V的单片机就是不能驱动5V的ULN2003,没办法只能先把这部分放着;于是调试nRF24L01+,这次调试了半天就可以完成双向通信了,记得这学期开学的第一周的周六吧花了10多个小时才调通了单向的通信,之前去图书馆借书,分析别人的程序的时间还不算,上个星期的半天可以调通双向通信也就不是什么意外了。
无线通信部分算是好了。
再又回头来做3.3V单片机驱动5V的ULN2003的部分,解法又是电平转换。
给老师做得项目里有个12V转5V的一个电平转换,记得到时在老师提示用三极管的条件下,我很快就想出来了电路图,用Proteus7.5软件仿真结果正确,实际搭建电路时发现三极管的e极串接的两个电阻不能太大,即使电压满足要求,可是单片机依然不能识别,把电阻按比例缩小后好了。
从新画电路,用Multisim仿真的,仿真结果没问题,可是实际搭建的电路就是不行,没办法,只能再次想办法去解决问题,百度、谷歌、问人、在面包板上搭建电路、仔细看芯片的数据手册,最后还是STC的数据手册里找到了解决问题的方法,问题解决了;但是手里的步进电机的扭矩不够,这个其实在我去解决那个问题的时候我就知道了;换电机,加驱动器,不可能,手里没有钱,也不想为那投资太多。
只能再次做我的小车,老早就有这个想法了。
这次似乎没有电平的问题,L9110是5V的器件,nRF2401+的数据脚可以和5V的单片机的IO口直接相连,3.3V的单片机IO口不够就用5V的单片机,NOKIA5110液晶和ADXL345是5V和3.3V通吃的;nRF24L01+之前也用5V单片机玩过没有问题,但是为了保险还是在它的数据脚加了10K的下拉电阻。
24L01调试+测试程序
由于电赛的需求,使用无线芯片24L01,初次使用,期间遇到的各种问题,现在拿出来与大家分享,以便日后度友们不要为此类问题感到苦恼。
一.硬件条件
这个是整个芯片测试的基础,首先根据芯片手册去认真的连接每个引脚,随后也是最重要的,那就是
3.3V的稳压。
建议:尽量使用AMS1117芯片稳压,测
试过的电阻分压完全不靠谱。
二.软件调试
1.推荐使用这个测试方法:
/link?url=IPHIUPey0LWVfpHs NxGSTUwdkJGFnUWiug_j4mKt6x6JooApWGPpidEEK9YUyd7Y8
U6z6PXMEGE1Hd0s3v7i1_h3wqLhkc9E0ZNj7RHn_Pe
2.发射端单独测试,可以将STATUS寄存器读出来,这样就可以明了发射的成功与失败。
3.接收端测试,如果无法成功接收,那就是发射端的时序刚好没有和接收端的接收时间不同步,自己在写程序时要自己尽量的将两个时间写到同步。
(后面我的空间会上传我整理的测试程序)
三.24L01通讯
由于我的题目中需求是在需要信息发射的时候发射,所以我的方案是:默认两个芯片都是处于接收状态,通过中断去判断接受还是发射的成功,然后再将芯片置于接受状态。
nrf24l01 at指令
NRF24L01是一个低功耗的2.4 GHz无线模块,具有多种功能。
在使用NRF24L01模块时,可以通过发送AT指令来进行设置
和配置。
以下是常用的NRF24L01 AT指令:
1. AT:测试通讯是否正常,模块将返回“OK”。
2. AT+RMODE:设置为接收模式。
3. AT+TMODE:设置为发送模式。
4. AT+CH[channel]:设置信道,[channel]参数可选范围为0-125。
5. AT+ADDR[address]:设置本机地址,[address]参数可选范
围为0-65535。
6. AT+RADDR[address]:设置远程地址,[address]参数可选范
围为0-65535。
7. AT+POWER[power]:设置发射功率,[power]参数可选范围
为0-3,分别表示-18dBm、-12dBm、-6dBm、0dBm。
8. AT+UART[baudrate]:设置串口波特率,[baudrate]参数可选
范围为2400、4800、9600、14400、19200、38400、57600、115200。
9. AT+SAVE:保存设置,将当前配置保存在模块的EEPROM 中。
以上是一些常见的NRF24L01 AT指令,不同的模块厂商可能
会有一些特殊的指令,具体使用时需要查阅相关文档。
USB串口无线模块的配置说明带USB接口的模块直接插电脑进行配置,不带USB接口的模块得借助USB转串口进行配置;(如下图所示)1、USB转串口模块以及带USB的无线驱动模块需要安装CH341驱动;(文件在“CH341>>DRIVER>SETUP.EXE)2、波特率默认设置为9600(带USB与不带USB的),波特率的选择范围为:2400-115200,具体,请看配置参数对应的描述;3、配置时,必须在断电的情况下,插上跳线帽,再从新上电;(注意:不能上着电的情况下,插跳线帽!)4、配置完成之后,必须得把跳线帽拔掉;(注意:必须得在断电的情况下拔,然后,再重新上电!)5、配置的格式为8个字节:“0X00+每个数据包的长度+0X01+频道+0X02+工作模式+单向/双向运行模式+‘波特率’”注意:1、必须插上跳线帽进行配置,配置完之后,必须拔掉跳线帽才能正常使用;(配置时,在上电前就得插上跳线帽;当然,正常使用时也得在断电的情况下,拔跳线帽,再重新上电)2、串口调试助手发送数据的格式为:十六进制;3、USB转串口模块的TX,RX与NRF24L01驱动模块(无USB的)的TX,RX要交叉相连,即一方的TX与另外一方的RX相连,然后,一方的RX与另外一方的TX相连;4、两个模块的数据长度,频道得设置成一样,否则工作不正常;数据的长度选择范围:(单向工作模式最小为1个字节,最大为32个字节)(双向工作模式最小为2个字节,最大为32个字节);频道的选择范围为:从0X00到0X7F 选择一个;(即0-127,从0开始,2的6次方)5、同一个实验室的,为了不互相影响,得把频道设置成不一样,否则会互相干扰,;6、“工作模式”只分两种:TX模式(0X01)和 RX模式(0X00),注意:两个模块的工作模式不能一样,必须得其中一个模块为TX模式,另外一个模块为RX模式,否则,不能实现两个模块的无线通信;7、单向/双向运行模式,0X01:模块运行在双向通信模式,0X00:模块运行在单向通信模式;双向模式相对单向模式而言,双向模式中,可以通过发送AT 指令来切换方向,具体请看双向模式的使用要求;8、波特的选择,从小到大分别为:2400(0X07),4800(0X08), 9600(0X00),14400(0X01), 19200(0X02), 38400(0X03), 56000(0X04), 57600(0X05), 115200(0X06);9、数据的长度得选择适中,串口调试助手(单片机)的发送频率也得选择适中;单向运行模式:(注意:配置的时候记得插上跳线帽,否则,配置不成功!)(图1) (图2) 配置参数为:0X00+0X06+0X01+0X78+0X02+0X01+0X00+0X06(如图1所示)这组配置参数的意思是:单个数据包的数据长度为 6个字节,频道选择120(0X78),让模块的工作模式为TX模式(0X01),模块运行在单向通信模式(0X00),波特率将选择115200(0X06)。
nRF24L01应用笔记(一)2011-03-3111:15最近百度上一些朋友都在为nRF24L01头疼,我这段时间又比较忙不能花太多时间一个一个去帮忙调试,干脆今天抽点儿时间写个应用笔记,希望能给大家提供一些方法和帮助。
有问题可以跟帖留言,我看到会尽量帮大家。
nRF24L01是Nordic公司生产的一个单芯片射频收发器件,是目前应用比较广泛的一款无线通讯芯片,具体手册资料网上大把,我就不再重复它的特性什么的了,直接说说它的调试方法,供大家参考。
24L01是收发双方都需要编程的器件,这就对调试方法产生了一定的要求,如果两块一起调,那么通讯不成功,根本不知道是发的问题还是收的问题,不隐晦的说,我当时也是没理清调试思路才浪费了大半天时间看着模块干瞪眼。
正确的方法应该是先调试发送方,能保证发送正确,再去调接收,这样就可以有针对性的解决问题。
至于怎么去调发送方,先说下发送方的工作流程:·配置寄存器使芯片工作于发送模式后拉高CE端至少10us·读状态寄存器STATUS·判断是否是发送完成标志位置位·清标志·清数据缓冲网上的程序我也看过,大多都是成品,发送方发送-等应答-(自动重发)-触发中断。
可是这样的流程就已经把接收方给牵涉进来了,就是说一定要接收方正确收到数据并且回送应答信号之后发送方才能触发中断,结束一次完整的发送。
可是这跟我们的初衷不相符,我们想单独调试发送,完全抛开接收,这样就要去配置一些参数来取消自动应答,取消自动重发,让发送方达到发出数据就算成功的目的。
SPI_RW_Reg(WRITE_REG + EN_AA, 0x00); //失能通道0自动应答SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x00); //失能接收通道0 SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x00); //失能自动重发(注:以下贴出的寄存器描述由于中文资料上有一个错误,故贴出原版英文资料)有了以上这三个配置,发送方的流程就变成了发送-触发中断。
NRF24l01的调试过程与方法小结心得体会:最近老板给了几块nrf24l01模块我,初次上手难免走了许多的弯路,经过近一周的时间的不断调试,模块之间终于可以相互收发数据了。
这样下来终于松了一口气。
其间的各种辛苦与艰辛难于言表。
上网大致看了一下,网上基于51的调试比较多,但是我们实验室用的是DSP2812,由于nrf24l01是SPI接口,2812上刚好有SPI的接口,这样貌似给使用带来了方便,但是51之类的芯片虽然没有SPI口,但是例程也最多,关于他的讨论比较多。
最开始我的想法也比较混乱,想直接用SPI来调试,把底层函数稍微修改了一下,发现并没有结果,这个东西就像一个黑匣子一样,即看不见也摸不着,后来我慢慢改变了思路。
既然网上基于IO口模拟的SPI的例程最多,我决定另外走一条路,先用2812的IO 口模拟SPI再用自带的SPI口去调试。
这样一来我就有了两条可以走的路。
第一条:底层SPI时序用IO口模拟去写。
第二条:底层直接用2812的SPI去操作。
虽然这样一来,路好走了一点,有各类的程序可以参考,但是这样带来最大的一个问题,这也是后来我才发现的,nrf24l01的最大读写速率是有限制的,2812在150M运行时很显然是太大了一点,由于nrf24l01对时序的要求很高,端口的读写速率和时序都有严格的要求,所以我们才看到,在网上一般是15M左右的单片机来模拟IO口,没有谁用150M的DSP来模拟IO口的,当然既然确定了这样的方法后来也发现了问题,我还是继续走下去了。
很重要的一点是系统的时钟频率。
当然时序的要求也很高,这也就是为什么,网上说这个模块不好调试的原因,既然是调试,当然我们既然是调试,肯定有一个思路和方法。
那么方法是什么呢?开始的时候我是一股脑将发送和接收的程序都写进去,然后啥现象也没有,然后就傻眼了。
在网上看了看,于是有了一点思路。
方法是将发送和接收的调试分开来调试,以读取nrf24l01内部的寄存器为手段,先调试发送方,发送方调试没有问题以后,让发送方不断的发送数据,然后再来调试接收方,直到接收方也没有问题,再接着望下面去做。
秉着这样的一个想法,我开始了调试。
这里我只对一对一的调试进行说明,后续的一对多,以及调频之类虽然我有了想法,但是还没有开始实施。
在开始调试之前建议将NRF24l01说明书读个三遍。
模块的外部端口XL24LD01 是采用挪威N O R D I C 公司的n r f 2 4 L 0 1 2.4G 无线收发IC 设计的一款高性能2.4G无线收发模块,采用GFSK 调制,工作在2400‐2483M 的国际通用ISM 频段,最高调制速率可达2MBPS。
XL24L01-D01 集成了所有与RF 协议相关的高速信号处理部分,如:自动重发丢失数据包和自动产生应答信号等,模块的SPI 接口可以利用单片机的硬件SPI 口连接或用单片机的I/O 口进行模拟,内部有FIFO 可以与各种高低速微处理器接口,便于使用低成本单片机。
在连线的时候要特别注意电源线不要弄反过来了,要注意电源的最大值不能超过3.6V,这个电压直接影响了模块的端口的输出电压值,由于我们的DSP端口的高电平在3.3V,一般将电压设置在这个附近左右。
只有在硬件没有问题的基础上才能开始调试软件,否者会出现很多的问题。
还有一个,工作模式的问题,要注意在待机和掉电的模式下才能够进行寄存器的写操作。
模块内部的寄存器在调试中对寄存器的读取操作是一个非常有效的验证方法。
对芯片资料中给出的23个寄存器内容请看手册。
对应的寄存器在头文件里面。
用IO口模拟SPI的方法头文件如下:#ifndef _NRF24L01_H_#define _NRF24L01_H_#define CPU_RATE 6.667L // for a 150MHz CPU clock speed (SYSCLKOUT)// DO NOT MODIFY THIS LINE.#define DELAY_US(A) DSP28x_usDelay(((((long double) A * 1000.0L) / (long double)CPU_RATE) - 9.0L) / 5.0L)//这个函数是在150M下的us级延时函数/*******************************************************/#define TX_ADDR_WITDH 5 //发送地址宽度设置为5个字节#define RX_ADDR_WITDH 5 //接收地址宽度设置为5个字节#define TX_DATA_WITDH 1//发送数据宽度1个字节#define RX_DATA_WITDH 1//接收数据宽度1个字节/*******************命令寄存器***************************/#define R_REGISTER 0x00//读取配置寄存器#define W_REGISTER 0x20//写配置寄存器#define R_RX_PAYLOAD 0x61//读取RX有效数据#define W_TX_PAYLOAD 0xa0//写TX有效数据#define FLUSH_TX 0xe1//清除TXFIFO寄存器#define FLUSH_RX 0xe2//清除RXFIFO寄存器#define REUSE_TX_PL 0xe3//重新使用上一包有效数据#define NOP 0xff//空操作/******************寄存器地址****************************/#define CONFIG 0x00//配置寄存器#define EN_AA 0x01//使能自动应答#define EN_RXADDR 0x02//接收通道使能0-5个通道#define SETUP_AW 0x03//设置数据通道地址宽度3-5#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//数据通道0接收地址#define RX_ADDR_P1 0x0b//数据通道1接收地址#define RX_ADDR_P2 0x0c//数据通道2接收地址#define RX_ADDR_P3 0x0d//数据通道3接收地址#define RX_ADDR_P4 0x0e//数据通道4接收地址#define RX_ADDR_P5 0x0f//数据通道5接收地址#define TX_ADDR 0x10//发送地址#define RX_PW_P0 0x11//P0通道数据宽度设置#define RX_PW_P1 0x12//P1通道数据宽度设置#define RX_PW_P2 0x13//P2通道数据宽度设置#define RX_PW_P3 0x14//P3通道数据宽度设置#define RX_PW_P4 0x15//P4通道数据宽度设置#define RX_PW_P5 0x16//P5通道数据宽度设置#define FIFO_STATUS 0x17//FIFO状态寄存器/*******************相关函数声明**************************/ unchar NRFACK();unchar NRFSPI(unchar date);unchar NRFReadReg(unchar RegAddr);unchar NRFWriteReg(unchar RegAddr,unchar date);unchar NRFReadRxDate(unchar RegAddr,unchar *RxDate,unchar DateLen); unchar NRFWriteTxDate(unchar RegAddr,unchar *TxDate,unchar DateLen); unchar NRFRevDate(unchar *RevDate);void NRFSetTxMode(unchar *TxDate);void NRF24L01Int();void NRFSetRXMode();unchar CheckACK();/**nrf24l01状态寄存器的申明**/struct STA_BITS { // bit descriptionUint16 TX_FULL:1; // 0 FIFO寄存器满标志Uint16 RX_P_NO:3; // 1:3 接收通道号Uint16 MAX_RT:1; // 4 最大重发次数Uint16 TX_DS:1; // 5 数据发送完成标志Uint16 RX_DR:1; // 6 接收数据中断Uint16 rsvd:1; // 7 reserved};union STA_REG {Uint16 all;struct STA_BITS bit;};struct LOOK_BITS { // bit descriptionUint16 bit0:1; // 0Uint16 bit1:1; // 1Uint16 bit2:1; // 2Uint16 bit3:1; // 3Uint16 bit4:1; // 4Uint16 bit5:1; // 5Uint16 bit6:1; // 6Uint16 bit7:1; // 7};union LOOK_REG {Uint16 all;struct LOOK_BITS bit;};/*********************************************************/#endif函数的底层如下:/******************************************创建:那是星期天*时间:2015.4.15*功能:NRF24L01射频模块C文件*****************************************/#include "DSP28_Device.h"#include "NRF24L01.h"//nrf24l01的射频模块的头文件unchar TxAddr[]={0x34,0x43,0x10,0x10,0x01};//发送地址union STA_REG sta={0};/*****************SPI时序函数******************************************/unchar NRFSPI(unchar data){unchar i;for(i=0;i<8;i++) // 循环8次{if(data&0x80)GpioDataRegs.GPADAT.bit.GPIOA3=1;//MOSIelseGpioDataRegs.GPADAT.bit.GPIOA3=0;// byte最高位输出到MISO data<<=1; // 低一位移位到最高位GpioDataRegs.GPADAT.bit.GPIOA1=1;//SCLK=1if(GpioDataRegs.GPADAT.bit.GPIOA2)//MISOdata|=0x01;GpioDataRegs.GPADAT.bit.GPIOA1=0;//SCLK=0}data=data&0x00ff;return(data);//返回读出的一个字节}/**********************NRF24L01初始化函数*******************************/void NRF24L01Int(){DELAY_US(2);//延时2us//让系统什么都不干GpioDataRegs.GPADAT.bit.GPIOA5=0; //ce=0待机模式1GpioDataRegs.GPADAT.bit.GPIOA0=1; //CSN=1;GpioDataRegs.GPADAT.bit.GPIOA1=0;// SCLK=0;GpioDataRegs.GPADAT.bit.GPIOA4=1;// IRQ=1;}/*****************SPI读寄存器一字节函数*********************************/unchar NRFReadReg(unchar RegAddr){unchar BackDate;GpioDataRegs.GPADAT.bit.GPIOA0=0; //CSN=0启动时序NRFSPI(RegAddr);//写寄存器地址BackDate=NRFSPI(0x00);//写入读寄存器指令GpioDataRegs.GPADAT.bit.GPIOA0=1;// CSN=1return(BackDate); //返回状态}/*****************SPI写寄存器一字节函数*********************************/unchar NRFWriteReg(unchar RegAddr,unchar date){unchar BackDate;GpioDataRegs.GPADAT.bit.GPIOA0=0; //CSN=0启动时序BackDate=NRFSPI(RegAddr);//写入地址NRFSPI(date);//写入值GpioDataRegs.GPADAT.bit.GPIOA0=1;// CSN=1return(BackDate);}/*****************SPI读取RXFIFO寄存器的值********************************/unchar NRFReadRxDate(unchar RegAddr,unchar *RxDate,unchar DateLen){ //寄存器地址//读取数据存放变量//读取数据长度//用于接收unchar BackDate,i;GpioDataRegs.GPADAT.bit.GPIOA0=0; //CSN=0启动时序BackDate=NRFSPI(RegAddr);//写入要读取的寄存器地址for(i=0;i<DateLen;i++) //读取数据{RxDate[i]=NRFSPI(0);}GpioDataRegs.GPADAT.bit.GPIOA0=1;// CSN=1return(BackDate);}/*****************SPI写入TXFIFO寄存器的值**********************************/unchar NRFWriteTxDate(unchar RegAddr,unchar *TxDate,unchar DateLen){ //寄存器地址//写入数据存放变量//读取数据长度//用于发送unchar BackDate,i;GpioDataRegs.GPADAT.bit.GPIOA0=0; //CSN=0启动时序BackDate=NRFSPI(RegAddr);//写入要写入寄存器的地址for(i=0;i<DateLen;i++)//写入数据{NRFSPI(*TxDate++);}GpioDataRegs.GPADAT.bit.GPIOA0=1;// CSN=1return(BackDate);}/*****************NRF设置为发送模式并发送数据******************************/void NRFSetTxMode(unchar *TxDate){//发送模式GpioDataRegs.GPADAT.bit.GPIOA5=0; //CE=0NRFWriteReg(W_REGISTER+CONFIG,0x00);//在掉电模式中配置寄存器NRFWriteReg(W_REGISTER+FLUSH_TX,0x00);//清除TX FIFO寄存器NRFWriteReg(W_REGISTER+SETUP_AW,0x03);//设置发射地址的宽度为5位 NRFWriteTxDate(W_REGISTER+TX_ADDR,TxAddr,TX_ADDR_WITDH);//写寄存器指令+接收地址使能指令+接收地址+地址宽度NRFWriteTxDate(W_REGISTER+RX_ADDR_P0,TxAddr,TX_ADDR_WITDH);//为了应答接收设备,接收通道0地址和发送地址相同NRFWriteTxDate(W_TX_PAYLOAD,TxDate,TX_DATA_WITDH);//写入数据/******下面有关寄存器配置**************/NRFWriteReg(W_REGISTER+EN_AA,0x00);//NRFWriteReg(W_REGISTER+EN_AA,0x01); // 使能接收通道0自动应答NRFWriteReg(W_REGISTER+EN_RXADDR,0x00);//NRFWriteReg(W_REGISTER+EN _RXADDR,0x01); // 使能接收通道0NRFWriteReg(W_REGISTER+SETUP_RETR,0x00);//NRFWriteReg(W_REGISTER+SE TUP_RETR,0x0a); // 自动重发延时等待250us+86us,自动重发10次NRFWriteReg(W_REGISTER+RF_CH,0x40); // 选择射频通道0x40NRFWriteReg(W_REGISTER+RF_SETUP,0x07); // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益NRFWriteReg(W_REGISTER+CONFIG,0x0e); // CRC使能,16位CRC校验,上电GpioDataRegs.GPADAT.bit.GPIOA5=1;//CE=1DELAY_US(10);//保持10us以上}/*****************NRF设置为接收模式并接收数据******************************///主要接收模式void NRFSetRXMode(){GpioDataRegs.GPADAT.bit.GPIOA5=0;// CE=0;NRFWriteReg(W_REGISTER+CONFIG,0x00);//在掉电模式中配置寄存器NRFWriteReg(W_REGISTER+FLUSH_RX,0x00);//清除TX FIFO寄存器NRFWriteReg(W_REGISTER+SETUP_AW,0x03);//设置发射地址的宽度为5位NRFWriteTxDate(W_REGISTER+RX_ADDR_P0,TxAddr,TX_ADDR_WITDH); // 接收设备接收通道0使用和发送设备相同的发送地址NRFWriteReg(W_REGISTER+EN_AA,0x01); // 使能接收通道0自动应答NRFWriteReg(W_REGISTER+EN_RXADDR,0x01); // 使能接收通道0 NRFWriteReg(W_REGISTER+RF_CH,0x40); // 选择射频通道0x40NRFWriteReg(W_REGISTER+RX_PW_P0,TX_DATA_WITDH); // 接收通道0选择和发送通道相同有效数据宽度NRFWriteReg(W_REGISTER+RF_SETUP,0x07); // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益*/NRFWriteReg(W_REGISTER+CONFIG,0x0f); // CRC使能,16位CRC校验,上电,接收模式GpioDataRegs.GPADAT.bit.GPIOA5=1;// CE = 1;DELAY_US(10);//保持10us以上}/****************************检测应答信号******************************/unchar CheckACK(){ //用于发射sta.all=NRFReadReg(R_REGISTER+STATUS); // 返回状态寄存器// sta.all=NRFReadReg(R_REGISTER+0x17);if(sta.bit.TX_DS||sta.bit.MAX_RT) //发送完毕中断{NRFWriteReg(W_REGISTER+STATUS,0xff); // 清除TX_DS或MAX_RT中断标志GpioDataRegs.GPADAT.bit.GPIOA0=0;// CSN=0;NRFSPI(FLUSH_TX);//用于清空FIFO !!关键!!不然会出现意想不到的后果!!!大家记住!!GpioDataRegs.GPADAT.bit.GPIOA0=1;// CSN=1;return(0);}elsereturn(1);}/******************判断是否接收收到数据,接到就从RX取出*********************///用于接收模式unchar NRFRevDate(unchar *RevDate){unchar RevFlags=0;sta.all=NRFReadReg(R_REGISTER+STATUS);//发送数据后读取状态寄存器if(sta.bit.RX_DR) // 判断是否接收到数据{GpioDataRegs.GPADAT.bit.GPIOA5=0; // CE=0; //SPI使能NRFReadRxDate(R_RX_PAYLOAD,RevDate,RX_DATA_WITDH);// 从RXFIFO读取数据RevFlags=1; //读取数据完成标志}NRFWriteReg(W_REGISTER+STATUS,0xff); //接收到数据后RX_DR,TX_DS,MAX_PT都置高为1,通过写1来清楚中断标return(RevFlags);}void InitGpio(void){EALLOW;///设置SPI口外设功能GpioMuxRegs.GPAMUX.bit.PWM1_GPIOA0=0;//CSNGpioMuxRegs.GPAMUX.bit.PWM2_GPIOA1=0;//sckGpioMuxRegs.GPAMUX.bit.PWM3_GPIOA2=0;//MISOGpioMuxRegs.GPAMUX.bit.PWM4_GPIOA3=0;//MOSIGpioMuxRegs.GPAMUX.bit.PWM5_GPIOA4=0;//IRQGpioMuxRegs.GPAMUX.bit.PWM6_GPIOA5=0;//CEGpioMuxRegs.GPADIR.bit.GPIOA0=1;GpioMuxRegs.GPADIR.bit.GPIOA1=1;GpioMuxRegs.GPADIR.bit.GPIOA2=0; //MISO为输入GpioMuxRegs.GPADIR.bit.GPIOA3=1;GpioMuxRegs.GPADIR.bit.GPIOA4=1;GpioMuxRegs.GPADIR.bit.GPIOA5=1;EDIS;}主函数中包涵了调试的内容:#include "DSP28_Device.h"#include "NRF24L01.h"void WriteLED(unchar data); //点亮LED2函数unchar TxDate[4];union LOOK_REG look0={0};union LOOK_REG look1={0};union LOOK_REG look2={0};union LOOK_REG look3={0};union LOOK_REG look4={0};union LOOK_REG look5={0};union LOOK_REG look6={0};union LOOK_REG look7={0}; union LOOK_REG look8={0}; union LOOK_REG look9={0}; union LOOK_REG look10={0}; union LOOK_REG look11={0}; union LOOK_REG look12={0}; union LOOK_REG look13={0}; union LOOK_REG look14={0}; union LOOK_REG look15={0}; union LOOK_REG look16={0}; union LOOK_REG look17={0}; union LOOK_REG look18={0}; union LOOK_REG look19={0}; union LOOK_REG look20={0}; union LOOK_REG look21={0}; union LOOK_REG look22={0}; union LOOK_REG look23={0};void main(void){/*初始化系统*/InitSysCtrl();/* 关中断*/DINT;IER = 0x0000;IFR = 0x0000;/* 初始化PIE控制寄存器*/InitPieCtrl();/* 初始化PIE参数表*/InitPieVectTable();/* 初始化外设寄存器*/InitGpio();InitSpi();/*设置CPU*/EINT; // 开全局中断ERTM; // 开实时中断NRF24L01Int();while(1){int ReadTempDate=6;TxDate[0]=ReadTempDate;// TxDate[1]=0;//TxDate[2]=0;//TxDate[3]=0;NRFSetTxMode(TxDate);//发数据while(CheckACK()); //检测是否发送完毕// GpioDataRegs.GPADAT.bit.GPIOA0=1;look0.all=NRFReadReg(R_REGISTER+0x00);look1.all=NRFReadReg(R_REGISTER+0x01);look2.all=NRFReadReg(R_REGISTER+0x02);look3.all=NRFReadReg(R_REGISTER+0x03);look4.all=NRFReadReg(R_REGISTER+0x04);look5.all=NRFReadReg(R_REGISTER+0x05);look6.all=NRFReadReg(R_REGISTER+0x06);look7.all=NRFReadReg(R_REGISTER+0x07);look8.all=NRFReadReg(R_REGISTER+0x08);look9.all=NRFReadReg(R_REGISTER+0x09);look10.all=NRFReadReg(R_REGISTER+0x0a);look11.all=NRFReadReg(R_REGISTER+0x0b);look12.all=NRFReadReg(R_REGISTER+0x0c);look13.all=NRFReadReg(R_REGISTER+0x0d);look14.all=NRFReadReg(R_REGISTER+0x0e);look15.all=NRFReadReg(R_REGISTER+0x0f);look16.all=NRFReadReg(R_REGISTER+0x10);look17.all=NRFReadReg(R_REGISTER+0x11);look18.all=NRFReadReg(R_REGISTER+0x12);look19.all=NRFReadReg(R_REGISTER+0x13);look20.all=NRFReadReg(R_REGISTER+0x14);look21.all=NRFReadReg(R_REGISTER+0x15);look22.all=NRFReadReg(R_REGISTER+0x16);look23.all=NRFReadReg(R_REGISTER+0x17);TxDate[0]=ReadTempDate;}}/******************************************************************** **********名称:WriteLED()**功能:接收到的数据点亮LED**入口参数:char data,需要发送的数据**出口参数:无********************************************************************** *******/void WriteLED(unchar data){int n,j;for(n=0;n<data;n++){GpioDataRegs.GPFDAT.bit.GPIOF8=1;for(j=0;j<10000;j++);GpioDataRegs.GPFDAT.bit.GPIOF8=0;for(j=0;j<10000;j++);}}SPI接口直接调试的方法DSP2812的SPI的调试是nrf24l01的第一步,2812的spi在读写nrf24l01过程中会出现写入数据的错误,这个也是后来我用示波器观测的时候发现的现象,一切的调试都是建立在SPI的底层函数上,SPI有问题那么对24l01的操作就会出现问题,在我的调试过程中就发现这样的问题,我不知道这种问题是不是共性,但是SPI的调试出现问题无线收发模块就有问题。