楼主: alicedodo

[经验] 开贴讲讲NRF24L01P,让你彻底搞懂它的工作原理,持续更...

[复制链接]

该用户从未签到

 楼主| 发表于 2019-5-30 13:03 | 显示全部楼层
本帖最后由 alicedodo 于 2019-5-31 13:49 编辑

【7. nrf2401模块的接口】【今天发现直接引用码云的图挂了,重新搞一下吧】
以TB上最常见最便宜的模块为例。
130337_c028b69f_409556.png
左边的这个型号最常见,最便宜,大约3/4块钱一个,双排8针,2.54mm间距,模块尺寸比右边那个大;
右边这个也不少,稍贵,大约6/7块钱一个,单排8针,1.27mm间距,带邮票孔,体积非常小巧,做工我感觉要比前一个好些;
除此之外,这两种功能上没任何区别。
都是板载nrf24l01+单芯片,没有PA(射频功率放大)芯片,功率不大,空旷通信距离百米左右。
根据你自己的实际情况选择:
如果你【对成本不敏感】且【打算自己做板】且【希望板子做的小巧漂亮】,推荐右边这个;
除此之外一律选择左边这个;
根据我自己的经验,列出一些关于硬件使用上的问题(对上面两种型号均适用):
  • 模块供电【一定/必须/不能】超过3.6V,如果你不小心接了5V供电,哪怕就一会儿,请节哀,不要问我是怎么知道的(╥╯^╰╥)。
  • 除VCC和GND之外,其余6个pin却是兼容5V电平的,这也是为什么arduino可以导线直连的方式驱动它的原因。
  • 除【买了便宜到超越底线的模块】之外,不要随便怀疑模块硬件有问题,一个模块能持续多年大量出货且不用升级换代,足以说明模块的稳定性。
  • 所有管脚直连arduino的对应管脚即可,无需【串联】电阻。
  • 用稳压芯片引出的3.3V供电可以直接给模块使用,除供电端极度不稳定之外,几乎不需要并联电容。
  • 如果你用的是带PA的模块,工作电流峰值能达到一百几十毫安,要特别注意供电端的带载能力是否足够。
下面来说说除VCC/GND之外其余6个管脚的作用,可以先看一下上图中的那个管脚功能表,再看下文。
我们的程序要使用nrf24l01+,包括配置工作模式/查询工作状态/发送数据/接收数据,全部是通过这6个管脚来和模块进行交互的。
这6个管脚可以分为3组:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
CSN/SCK/MOSI/MISO算一组,是SPI接口,往模块里送数据/从模块里读数据都是使用SPI来进行的。
这里所说的数据有2个含义,即可以是我们打算向外界真实发送的数据,也可以是给这个模块自身的配置数据,也就是它内部各个寄存器的值。
从底层来说,这些数据就是一串0101,一打眼没什么区别,直接丢给你你也看不出来。
那nrf24l01+如何知道这一串0101代表什么含义呢? 当然是约定好协议格式啦:
不管这一串bit流有多长,一律按8bit即单字节为基本单元进行格式划分。
nrf24l01+规定,双方在传输字节时,必须高位bit优先输出,即输出顺序是: bit7-bit6-bit5-bit4-bit3-bit2-bit1-bit0。
CSN一旦拉低,模块的SPI接口被使能,同时也代表数据交换【或者叫“一次通信过程”吧】的开始;
主机从MOSI输出的第一个字节被叫做【命令字】,这个字节的含义是让模块知道主机想要做什么,例如读寄存器/写寄存器/写入待发送数据/读入已接收数据等等;
以写寄存器为例子,按照一般逻辑,不能只告诉我你想写寄存器,总还得告诉我你想写哪个吧,即必须还要传给我寄存器的地址。
nrf24l01+内部一共30个寄存器,编址0x00-0x1D,不多不少;
主机能做的事情也不多,读写寄存器,读写数据等等,总共十多项;
一个字节总共可以表示256种情况,不算少;
以上3者一结合,为了提高传输效率,于是砖家在设计nrf24l01+命令字的时候取了个巧,将代表【操作类型】的信息和代表【地址属性】的信息整合到一个字节中,既是【命令字】。
于是【命令字】从【编码格式】上分,一共有两大类:【只带操作信息不需要地址信息的】/【操作信息地址信息都要有的】。
下图中的3条命令就是这类复合命令字的典型。
152444_a9b0072f_409556.png
152516_ca6062c5_409556.png
以上图中读寄存器命令 R_REGISTER 为例,
操作码是0x00;
寄存器地址用bit4-bit0来表示,前面说过寄存器地址的最大值不过是0x1D, 5个bit足够容纳了。
于是修改0x1D寄存器的命令字应该是: 0x00+0x1D = 0x1D。
主机端在MOSI脚输出命令字的同时, nrf24l01+也没闲着,通过MISO脚也输出了从机端的第1个字节。
这个字节是有意义的,叫做【状态字】,是模块内部名为STATUS(地址0x07)寄存器数据的副本。
为什么偏偏选这个这个寄存器的值输出呢? 看一下这个寄存器的位值说明:
200044_ab1b4180_409556.png
上面的位值表示了模块在工作过程中的一些关键性的状态数据,包括:
数据是否已经发送成功/是否发送失败/是否收到了外界的新数据/新数据来自哪个pipe/数据缓冲区是否已被写满
我们的程序在运行时一定会非常频繁的要获知这些状态,【状态字】给我们提供了某种福利:
不需要专门使用【命令字(0x00+0x07=0x07)】去读取STATUS,任何一次与模块的交互过程都能立即得到STATUS的值。
当然你非要使用读寄存器的方式获取STATUS也是可以的,就是效率低一点儿。
【命令字】和【状态字】过后,从第二个字节开始,后面都是数据字节。而数据字节的流向就只是单向的了。
【命令字】按照【数据流向】划分的话,可以分为3类:
一类是给nrf2401送数据,比如写寄存器/写入待发送数据;
一类是从nrf2401里面拿数据,比如读寄存区/读出其他模块发来的数据;
最后一类是没有数据,只有命令,比如FLUSH_TX/FLUSH_RX只是通知模块清空内部数据缓冲区,明显不需要提供数据。
不存在既要给模块送数据同时也要从模块里读出数据的情况,所以说数据流向是单向的。
对于R_REGISTER命令,数据从模块流出,于是有效数据在MISO这根线上,MOSI上的数据就无所谓了;
同理,对于W_REGISTER命令,数据从主机流入模块,有效数据在MOSI这根线上,MISO上有啥无所谓;
绝大部分命令只带一个字节的数据就行了,个别情况需要附带多个字节的数据,以W_REGISTER为例:
数据通道pipe0的地址最长5个字节,在nrf2401内部只给这个pipe0_adr寄存器分配了1个寄存器地址而不是5个。
这个寄存器地址是0x0A,所以我们可以认为0x0A是一个长度为5字节的寄存器。
假设我们要给pipe0设置的地址为: 0xA4A3A2A1A0,拆开看从高到低5字节:0xA4 0xA3 0xA2 0xA1 0xA0
前面说过,SPI传输时字节内部高位bit优先输出,而协议规定传输多字节数据时,【低字节优先】输出。
于是MOSI信号线上字节流的顺序是这样的: CMD-A0-A1-A2-A3-A4
最后,数据传输完毕,CSN要拉高,即代表模块的SPI断开了主机的连接,也让nrf2401知晓一条命令结束了。
这个地方必须提醒,CSN拉高是必须的,哪怕你有很多命令排队等着操作模块,每条命令的写入都要遵守:【CSN拉低-数据传输-CSN拉高】这条规定。
啰嗦了一大堆话,一张图足以代表一切:
105521_edf39701_409556.png
此外关于SPI时序还有一张比较重要的图:
111152_84c46ea4_409556.png
解读一下这张图对我们写程序有什么用:
  • 模块要求MOSI/MISO在SCK上升沿的时候输出bit,不管是软件模拟SPI还是硬件SPI要特别注意这一点,尤其是stm32这类SOC当主控时,它的SPI功能非常繁多,配置SPI工作模式的时候要特别注意
  • 两条操作命令之间要在CSN高电平时添加适当延时
    上图红框标出的Tcwh,这个时间代表上一条命令结束后,下条命令开始前,必须要有个恢复时间,防止误动作。
    这个时间不要少于【100纳秒】,保守起见,建议至少延时【500纳秒】。

关于SPI接口,最后再说一下SCK时钟频率的问题:
nrf2401模块的时钟频率最高可以到10MHz,但保守起见,我们实际使用是建议时钟速度不要超过8M,留一些余量总是好的。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
CE 可以单独算一组,这个管脚和nrf2401射频模块的工作状态有关。
当nrf2401被配置成PTX时,我们通过SPI写入要对外发送的数据之后,模块并不会立即启动发送,
我们必须再拉高CE管脚并使其高电平保持至少10微秒(10微秒过后,可以拉低也可以继续保持高电平),
模块才会开启射频模块,进入【发送状态】,开始发送数据。
当nrf2401被配置成PRX时,同样模块也不会立即启动射频模块进入【接收状态】,
我们必须拉高CE并使其一直保持高电平,模块才会进入【接收状态】开始监听数据。
任意时刻,只要CE被拉低了,PRX会立即从【接收状态】退出到待机状态。
这只是CE管脚的基本用处,更多的细节我们后面在讲完【发送/接收缓冲区】以及【工作状态图】之后再说。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
IRQ 也单独算一组,这个管脚如其名,是模块用来通知主机【数据发送/数据接收相关的紧急事件】的快捷通道。
相对于单片机的运行速度来说,nrf2401是比较慢的,8M的SPI时钟下,主机要对外发送32字节数据,
只需要不到40微秒就能将数据全部塞给模块,而模块想要将这些数据发送完毕,需要的时间却是要多得多。
主机当然可以在写完数据之后,一遍遍的询问模块数据是否发送完成,但同时主机也不可能去干别的事情了,所以必须要使用中断通知的方式来减轻主机的压力。
主机写完数据之后就可以去忙别的了,后续发送过程/成功确认/发送失败确认的一系列工作交给模块自主完成,一旦结果出来之后,模块立即将IRQ管脚拉低。
主机端一般会将自己的外部中断管脚和模块的IRQ管脚连在一起,一旦IRQ管脚被模块置为有效,主机端的外部中断会立即触发,然后就可以在中断函数中访问模块,查看发生了什么。
这中紧急事件只有3种: 【数据发送成功】/【数据发送失败且已经多次失败】/【收到其他模块发来的新数据】
当中任意一个单独发生或任意多个同时发生,都会触发IRQ,所以主机程序在获知中断发生之后,必须在读取一遍模块内部的STATUS寄存器才能准确知道是哪一种事件触发了IRQ。
关于IRQ更细致的问题,后面章节中在讨论。




码字不易,如果觉得学到了真家伙,请我喝碗咸豆花

1人打赏

该用户从未签到

 楼主| 发表于 2019-6-5 20:45 | 显示全部楼层
本帖最后由 alicedodo 于 2019-6-5 20:48 编辑

8. nrf24l01的工作模式详解

之前在讲nrf24l01功耗的时候,提到过【工作模式】,为了避免过早的牵扯到很细节的内容,在那一节只是粗略归纳了一下:掉电模式/热待机模式/火力全开模式,说的其实很模糊,这一节正式的讲解一下。
先上一张nrf24l01的状态转换图:
174759_3648d234_409556.png


这张图截取自官方datasheet第6章第1节(第21页),如果觉得截图不清楚,可以直接去下载PDF文档,放大了看。
  • 圆环圈起来的是模块的工作状态
    虚线环表示临时状态,模块在此状态下仅做短暂的停留,实线环(不管粗细)表示稳定的工作状态
  • 带箭头的线(无论粗细)表示模块在两个工作状态之间进行【迁移】
  • 箭头线上的文字表示【迁移条件】,即旧状态下只有满足特定条件,模块才会进行状态迁移,没有文字表示无条件迁移
  • 最上面的【undefined】表示模块上电之前的状态
在各处的【迁移条件】上,我们一共看到了这么几个关键词:CE/PWR_UP/PRIM_RX/TX FIFO
  • CE就是上一节提到的CE管脚;
  • PWR_UP和PRIM_RX是nrf24l01内部名为CONFIG的寄存器(地址0x00)的两个bit位;

11111.png

           PWR_UP由0变1,则模块从掉电模式PowerDown切换为待机模式Standby-I,但需要至少1.5毫秒的转换过程;
              PWR_UP由1变0,则模块不论当前正处于什么工作状态,立即转入PowerDown模式,无需等待;
              PRIM_RX则控制模块在工作时是一个什么身份,即PTX还是PRX;
              我们的程序可以使用上一节提到的写寄存区命令 W_REGISTER 来修改这两个bit的值,从而控制模块的工作状态/工作模式
  • TX FIFO是nrf24l01内部的发送缓冲区;
    我们的程序通过SPI接口给模块写入发送数据是很快的,但模块将数据完全发送出去却比较慢,为了缓解双方速度不对等的问题,模块内部设置了一个最大容纳96字节数据的缓冲区,每32字节为一组,共3组,以组为单位构成先入先出(FIFO)缓冲区,即是TX FIFO。
    当我们需要发送很多数据的时候,先一次性将数据写满这3组缓冲区,操作CE管脚启动发送之后,模块再慢悠悠的将缓冲区里的数据以组为基本单元发送出去,发完一组后再取下一组,直到将缓冲区的数据全部发送。
    关于缓冲区的内容这里只简单说一下,后面章节会细聊。

那么状态图中粗环粗线/细环细线是个什么意思呢? 这是官方推荐的模块工作流程。
结合上面的图,从前面描述中我们知道,通过操作【CE/PWR_UP/PRIM_RX/TX FIFO】这4项,我们可以随意控制nrf24l01的状态转换。

以最底下的【TX Mode】状态为例子,根据不同的操作条件,模块在切换工作状态的时候有4条路可走:

【路线1】TX Mode --> Standby-I (条件是一包数据发送完毕且CE管脚为低电平)
【路线2】TX Mode --> PowerDown (不管模块在TX Mode下正在干什么,只要把PWR_UP的bit值改为0,那么模块立即进入掉电模式)
【路线3】TX Mode --> TX Mode (一包数据发完之后,如果缓冲区里还有未发送的且CE管脚为高电平,那么继续呆在TX Mode发送下一包)
【路线4】TX Mode --> Standby-II (缓冲区里的数据已经全部发送完毕,但CE管脚依然是高电平,那么立即转入Standby-II状态)

从写程序的角度上说,这4条路都是可以的,nrf24l01对此并不做限制,但为什么官方偏偏推荐【路线1】?

【路线2】明显过于暴力,不多说。
【路线1】和【路线3/4】唯一的区别就是对CE管脚的操作方式不一样:
【路线1】时,触发发送仅仅是在CE管脚上制造一个10微秒宽度的高电平脉冲,之后CE要回归低电平;
【路线3/4】时,触发发送要简单,只要将CE管脚置为高电平就不管了;

这么看起来,PTX模式下CE管脚一直保持高电平在程序实现上反而更方便,因为使用【路线1】工作流程的时候,每发完一包,我们的程序还必须要主动地为CE管脚制造一个高电平脉冲才能再次触发数据发送。

既然这样为啥官方还是推荐【路线1】呢?

我认为是因为功耗的问题。(真的只是"我认为",datasheet上并没有专门解释,功耗论只是我觉得可信度比较高的理由)
200747_87939c2d_409556.png
从上面这个表可知,虽然同样是待机模式,但Standby-I的功耗要比Standby-II的功耗小一个数量级还多。

当然【路线1】官方只是推荐,我们在写程序的时候不一定非要按照【路线1】来搞,一个写程序更方便,一个更省电,两者各有利弊,自己权衡即可。

那么Standby-II怎么才能转入Standby-I呢?
前面那张状态图上并没有画,其实datasheet在后面章节有提到过: 直接把CE置为低电平即可。

最后,总结一下我们写程序的时候,能从这张状态图中可以得到哪些注意事项:
  • nrf24l01上电之后,我们的程序至少要延时100毫秒,等它稳定进入PowerDown之后才能开始进行寄存器配置
  • 通过修改bit位PWR_UP,让nrf24l01从PowerDown转入Standby-I
  • 通过修改bit位PRIM_RX,可以使nrf24l01进入PTX模式或PRX模式
  • PWR_UP和PRIM_RX在同一个寄存器里,所以可以一次性配置
  • bit位PWR_UP写入1之后,程序必须至少延时1.5毫秒(建议2毫秒),模块才能稳定进入Standby-I,之后程序才能触发数据发送或数据接收
  • CE管脚脉冲式触发的时候,要保证高电平至少持续10微秒(建议15微秒)
  • 程序操作模块的时候,让nrf24l01尽量按照官方推荐的状态转换路线走


码字不易,如果觉得学到了真家伙,请我喝碗咸豆花

1人打赏

该用户从未签到

发表于 2019-6-10 10:30 | 显示全部楼层
楼主,最近刚过完节,今天又来学习了一下,看到您5号发的这个图25 SPI NOP timing diagram里面的Tcc Tcch,这些不知道代表什么,这个图不是很理解,特地过来请教一下。

该用户从未签到

发表于 2019-6-10 10:31 | 显示全部楼层
a727409529 发表于 2019-6-10 10:30
楼主,最近刚过完节,今天又来学习了一下,看到您5号发的这个图25 SPI NOP timing diagram里面的Tcc Tcch, ...

5月30日发的那篇

该用户从未签到

 楼主| 发表于 2019-6-10 16:46 | 显示全部楼层
a727409529 发表于 2019-6-10 10:30
楼主,最近刚过完节,今天又来学习了一下,看到您5号发的这个图25 SPI NOP timing diagram里面的Tcc Tcch, ...

你的问题有点儿偏门啊

你提到的这张图 名字叫 “SPI NOP timing diagram”【主控端向nrf2401发送NOP指令时,SPI的4根线上的时序图】
关于NOP指令的作用,可以参见官方datasheet第48页的Table 19

紧挨着这个图,第50页有这么个表:
111111111111.png

这个表详细解释了每个时间参数的含义以及参数值
以Tcc为例子,文档上解释的意思是: 从【CSN下降沿开始】到【SCK第一个上升沿】之间的时间间隔。
从上表可以看出,nrf2401要求这个时间间隔必须不能短于2纳秒(Min:2 ns),而Max没填,意思是说只要大于2纳秒,那么你想抻多长时间都可以,不做限制。   
为什么要有个最短时间要求?  
这个很好理解,nrf2401内部的电路对输入信号的反应速度不是无限快的,CSN拉低使能SPI接口之后,电路不可能瞬间就进入工作状态,肯定需要做些准备才能正确识别SCK上的信号变化,所以SCK上的时钟信号必须多等一点时间才能输出。

其他时间参数都类似,就不一一介绍了,可以看下文档。

其实关于这张表里的参数,除了我在帖子正文里提到的那两项,arduino编程的时候,其他参数不需要关心即可,如果arduino通过硬件SPI和nrf2401通信,配置好SPI时钟之后,这些时序上的细节,硬件完全替你代劳了。
码字不易,如果觉得学到了真家伙,请我喝碗咸豆花

该用户从未签到

 楼主| 发表于 2019-6-13 14:07 | 显示全部楼层
9. nrf24l01的数据缓冲区(TX FIFO、RX FIFO)
【温馨提示:】
从本节开始,关于nrf24l01使用,我只会讲和【Enhanced ShockBurst(增强型短时猝发工作模式)】有关的内容,【Enhanced ShockBurst】就是前面屡次提到的【自动回复】工作模式的官方称呼。
所以那种使用PTX/PRX身份互换而实现双向通信的方式就不会做太多涉及了,因为这两种模式对模块的使用差别非常大,交叉写的话可能会对初学的同学造成困扰,对期待这部分内容的同学表示抱歉。
而其实,对于PTX/PRX身份互换双向通信的方式,当你对通信可靠性(丢包/确认/超时/重发)有很高要求时,仔细考虑一下,你会豁然发现,这些要求不早就在【Enhanced ShockBurst】中得到满足了嘛!
上一节在描述状态迁移条件时,简单提过nrf24l01内部数据缓冲区的问题,这节正式讲解一下:
111111111111.png
上面这个图就是缓冲区的框图。
可以看到,nrf24l01既有发送缓冲区 TX FIFO,也有接收缓冲区 RX FIFO。
FIFO意思是先入先出队列,一个数据结构的概念,不多说,不了解的可以搜索引擎之。
首先需要说明的是:
仅就数据无线传输这个功能来说,数据缓冲区并不是必要的,这个东西仅仅是为了缓解SPI接口和射频模块之间数据传输速度差距巨大的问题而存在的。
就算不给nrf24l01设计这两组缓冲区,无线传输照样可以实现。
实际上,我相信绝大多数人在使用nrf24l01进行双向通信的时候,根本没把【数据缓冲】的这个功能利用上:
发一包 --> 收一包 --> 再发一包 --> 再收一包 【你中枪了没?】
不管是发数据还是收数据,我们的程序其实只和TX/RX FIFO打交道:
发送数据时,程序通过SPI指令将数据写入TX FIFO,启动发送后,nrf24l01再从TX FIFO中取出数据发送出去;
接收数据时,nrf24l01先将收取到的数据存入RX FIFO,程序通过IRQ获知有数据之后,通过SPI指令将RX FIFO中的数据读取出来;
从上面的框图中可以看出:
每32字节组成一个缓冲区单元;
TX/RX FIFO 各有3个缓冲区单元,理论上一次性可以最多存储96字节的待发送/待接收数据;
TX FIFO 和 RX FIFO是各自独立的,你存你的,我存我的,互不影响;
TX/RX FIFO是一个环形先入先出队列,3个单元没有编号,地位完全相同;
那么,我们在写程序的时候,如何才能操作TX/RX FIFO呢?
所谓对TX/RX FIFO的操作,可以划分为两大类:
  • 读写类操作,包括如何给TX FIFO写入数据,如何从RX FIFO读出数据,如果数据不想要了如何清空FIFO中的数据等等
  • 查询类操作,包括FIFO中是否存在有效数据,FIFO是否已经写满,是否还有空余位置,RX FIFO某单元中有效数据来自哪个pipe,32字节里有多少是有效数据等等
为了实现以上各类操作,nrf24l01给我们提供了丰富的手段:
为读写类操作提供了好几条专用的SPI指令;
专门设计了几个寄存器,用来记录FIFO的相关状态,通过读寄存器指令 R_REGISTER 来读出寄存器的值,从而获取这些状态;
111111111111.png

上面这张表是nrf24l01提供的所有的SPI指令,我把但凡在FIFO操作中能用到的指令全部标了出来。
可以看到,SPI指令表中绝大部分都是和FIFO有关的。
下面逐个讲解一下,会穿插提到一些相关寄存器:

【------------W_TX_PAYLOAD / W_ACK_PAYLOAD-----------】
这两条指令是TX FIFO写入数据的专用指令,W_TX_PAYLOAD是PTX专用的,W_ACK_PAYLOAD是PRX专用的,不要用错。
指令后面附带要写入TX FIFO的数据,数据长度1字节到32字节都可以。
程序无需特意告诉nrf24l01写入的数据有多长,指令结束之后(指CSN拉高之后),模块自然就知道了数据长度。
每执行一条写入指令,不管你每次写入1字节还是32字节,都会消耗一个FIFO单元,所以最多执行3次写入指令,TX FIFO就写满了,极端情况下,你每次写入1字节,执行3次,那么整个96字节的TX FIFO实际只包含3字节有效数据。
TX FIFO写满之后,再执行写入指令,操作无效。
由于FIFO的特性,写满FIFO之后,发送数据时,哪份数据先被写入,哪份数据先被发送。
当nrf24l01确认某个FIFO单元的数据发送完成之后,会删除对应的FIFO内的数据,此单元被清空,可以再次执行指令写入新数据。(在自动回复模式下,这里的"发送完成"不是单单指数据从射频模块发出去了,必须收到对端的确认信息之后才算发送完成,这个后面章节会提到)
注意 W_ACK_PAYLOAD 指令里面还有个PPP的参数,在前面章节【5. 地址和数据通道】中提过PRX总共有6个data pipe,PPP就是用来告诉PRX【这份数据是发给哪个通道的】,PPP取值范围是000-101,分别对应pipe0-pipe5,PPP必须填对才能保证数据被发送到正确的对端上,哪怕是简单的一对一通信。
假设我们要写入6字节的数据,那么SPI的时序应该是:
111111111111.png

特别说一下命令结束时CSN拉高的问题:
CSN拉高的时机是我们的程序主动控制的,有多少数据要写入,程序肯定是知道的;
命令字节输出完毕,开始输出数据字节时,程序自己要数数统计;
每输出一个数据字节就要加1,当数量够了之后,程序就要主动拉高CSN;
nrf24l01根据CSN拉高的时机,就能知道此次一共写入了多少数据。

【------------FLUSH_TX-----------】
FLUSH_TX负责将TX FIFO中的数据"一键清空"。PTX和PRX均可使用。
这个指令不需要任何参数,所以附带任何数据字节,只给nrf24l01发送一个命令字即可。
这条指令一般在【模块初始化】或【重置通信】的场景中使用。

【------------REUSE_TX_PL-----------】
这条指令为PTX专用指令,指示模块复用一下上次成功发送出去的数据包(包=数据+地址+校验+...)。单命令字,无需任何数据。
设想这样一种场景:我们有一个无线手柄和一辆无线遥控小车,手柄作为PTX,小车作为PRX。手柄发送一些固定格式的数据指示小车做一些动作。
某时候手柄需要小车多次重复做一个动作,程序细节上就是让手柄上的nrf24l01(PTX)反复的发送同一份数据给小车(PRX)。
要知道,当TX FIFO中的数据成功发送给对端之后,FIFO中的数据就会被移除,所以程序想再发送一次同样数据的话,有2种做法:
  • 重新通过 W_TX_PAYLOAD 再写一次同样的数据进去,然后操作CE启动发送。
  • 使用 REUSE_TX_PL 将刚才发送过的数据包再用一下,然后操作CE启动发送。
由于 REUSE_TX_PL 不需要附带额外数据,只需一字节的命令字即可,所以方法2的效率大大高于方法1,发送的数据越长(当然最长不能超过32字节),差距越大。
但是如果 FLUSH_TX 和 W_ACK_PAYLOAD 中有任意一个被执行过,那么 REUSE_TX_PL 就立即失效了,除非再次发送数据成功。

【------------FLUSH_RX-----------】
类似FLUSH_TX,只不过清空的是RX FIFO,不多说。PTX和PRX均可使用。

【------------R_RX_PAYLOAD / R_RX_PL_WID-----------】
这两条指令也是【PTX和PRX均可使用】,互有关联,所以放在一起讲。
R_RX_PAYLOAD 是从nrf24l01的RX FIFO中读取有效数据, R_RX_PL_WID 是查询当前这份数据的有效长度。
不管是PTX还是PRX,当nrf24l01收到对端发来的数据,检查无误后,会将数据存储到RX FIFO中,然后通过IRQ通知我们的程序。
由于一包数据里有效数据最长就是32字节,所以只会消耗RX FIFO中的一个存储单元,如果这包数据只有1字节,也会占用一整个FIFO单元。
这里需要注意:
如果我们的程序反应比较慢,没有及时将RX FIFO中的数据取走的话,RX FIFO有可能被后续新来的数据塞满。
如果RX FIFO没有可用的空闲单元了,那么nrf24l01不会再收取新的数据包,直到RX FIFO再次出现空闲的存储单元。
IRQ只是告诉程序有数据到了,但我们的程序此时并不知道这份数据有多少字节,所以真正发 R_RX_PAYLOAD 命令读取数据之前,还要必须首先发送 R_RX_PL_WID 来获取数据的长度信息。
111111111111.png
长度拿到之后,就可以使用 R_RX_PAYLOAD 命令将数据读出来了:
111111111111.png
这张图是和上面那张TX FIFO写入时序图是对应的。
对方写TX FIFO时数据字节顺序是什么样的,我方读RX FIFO时数据字节顺序就是什么样的。
CSN拉高的时机和前面类似,由程序自己统计读进来的字节数,数够了之后,主动控拉高CSN。
对于PTX来说,它只能和一个PRX通信,所以PTX的RX FIFO中的数据确定无误就是来源于那个唯一的PRX。
但是对于PRX来说却不是这样,PRX最多可以有6个pipe来监听数据,每个pipe对应一个PTX节点。
只知道数据长度的话,还是不能确定这份数据到底来自哪个PTX节点,所以还必须要知道这份数据来自哪个pipe。
当然,如果你的应用场景就只是PTX/PRX一对一通信,那么pipe编号没没必要知道了。
通过前面章节可知,PRX在收取数据的时候是知道来自哪个pipe的,所以PRX将数据存入RX FIFO后,会将这个pipe的编号存入STATUS寄存器中。
111111111111.png
想要拿到STATUS的值,一共3种方法,任选其一:
  • 使用常规的读寄存器命令 R_REGISTER ,读取 STATUS 寄存器的值
  • 使用专用的 NOP 命令获取 STATUS, 比方法1快
  • 由【7. nrf2401模块的接口】可知,无需刻意获取 STATUS,当发送 R_RX_PL_WID 命令的时候,STATUS就顺便拿到了
总结一下RX FIFO的读取过程:
PTX读取数据: 获取数据长度 ---> 实际读取数据
PRX读取数据: 获取数据长度 ---> 获取数据来自哪个pipe ---> 实际读取数据

【------------R_REGISTER-----------】
状态查询类的操作,除了 STATUS 寄存器的获取方式多样化之外,其余状态的获取就只能通过 R_REGISTER 来实现了。
nrf24l01把TX/RX FIFO的当前状态集中存储在了名为 FIFO_STATUS 的寄存器中:
111111111111.png
限于篇幅,每一个位段的含义就不展开了,图上的注释栏写的很清楚,大家自己阅读。
任何一个可以导致TX/RX FIFO发生变化的事件:
比如读写FIFO、清空FIFO、数据已发送成功、收到新数据等等,都会触发 FIFO_STATUS 的刷新。
我们的程序只要在需要的时候读取一下这个寄存器,就能全程实时掌握TX/RX FIFO的最新状态。
写程序的时候要根据需要灵活运用。



码字不易,如果觉得学到了真家伙,请我喝碗咸豆花

该用户从未签到

发表于 2019-6-13 15:46 | 显示全部楼层
alicedodo 发表于 2019-6-10 16:46
你的问题有点儿偏门啊

你提到的这张图 名字叫 “SPI NOP timing diagram”【主控端向nrf2401发送NOP ...

谢谢楼主,认真拜读了。等着继续更新学习。

该用户从未签到

 楼主| 发表于 2019-6-13 16:04 | 显示全部楼层
a727409529 发表于 2019-6-13 15:46
谢谢楼主,认真拜读了。等着继续更新学习。

中午的时候更了一节,触发人工审核了,明天应该可以看到
码字不易,如果觉得学到了真家伙,请我喝碗咸豆花

该用户从未签到

发表于 2019-6-14 10:07 | 显示全部楼层
楼主大人。协议拜读了,程序调通了。谢谢!
我想问参考设计上的问题, datasheet写的负载时15+j88,而参加设计匹配电路50怎么计算的。ANT1天线端的8.2nh 的电感是充当扼流圈吗,我太明白这个2.4g的电平信号怎么输出的。我自己按pi或者L 型都算不出来50偶。 是要把板参数放在ADS 仿真吗。
2电阻电容之间的走线也要设置50偶。叠加起来不就100了吗。

该用户从未签到

 楼主| 发表于 2019-6-14 13:11 | 显示全部楼层
SCYGO 发表于 2019-6-14 10:07
楼主大人。协议拜读了,程序调通了。谢谢!
我想问参考设计上的问题, datasheet写的负载时15+j88,而参加设 ...

咳咳 很是惭愧您的问题我回答不了我的硬件知识只是业余水平 高频电子方面更是纯粹的小白

非常抱歉
码字不易,如果觉得学到了真家伙,请我喝碗咸豆花
您需要登录后才可以回帖 登录 | 立即注册  

本版积分规则

热门推荐

【原创】全球最小口袋3D打印机mini one直播教程贴
【原创】全球最小口袋3D打
最近闲得蛋疼,没事搞个掌上3D打印机,先放效果图吧。 搞了半天,终于能正常打印,
SpiderRobot 蜘蛛
SpiderRobot 蜘蛛
关于SpiderRobot 项目实行方案 之前有人推荐我做这个项目,于是乎就有了这个 h
Arduino MEGA 与UNO 通过nRF24L模块通讯
Arduino MEGA 与UNO 通过n
之前在深水宝很“实惠”的店铺买了一些原件,随手砍了esp8266以及nRF24L*3 因为缺
求助!GM65二维码扫描模块怎么用?
求助!GM65二维码扫描模块
求助!GM65二维码扫描模块怎么用? 卖家给的资料真的看不懂该怎么弄。。( ̄▽ ̄)~*
KPM大比拼
KPM大比拼
前言:没别的就是比手快! 方案描述: 做这个的初衷其实是受到一些网红玩具的启发—
Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   ( 蜀ICP备14017632号-3 )
快速回复 返回顶部 返回列表