burst tx 功能 开启_开贴讲讲NRF24L01P,让你彻底搞懂它的工作原理,持续更...

【9. nrf24l01的数据缓冲区(TX FIFO、RX FIFO)】

【温馨提示:】

从本节开始,关于nrf24l01使用,我只会讲和【Enhanced ShockBurst(增强型短时猝发工作模式)】有关的内容,【Enhanced ShockBurst】就是前面屡次提到的【自动回复】工作模式的官方称呼。

所以那种使用PTX/PRX身份互换而实现双向通信的方式就不会做太多涉及了,因为这两种模式对模块的使用差别非常大,交叉写的话可能会对初学的同学造成困扰,对期待这部分内容的同学表示抱歉。

而其实,对于PTX/PRX身份互换双向通信的方式,当你对通信可靠性(丢包/确认/超时/重发)有很高要求时,仔细考虑一下,你会豁然发现,这些要求不早就在【Enhanced ShockBurst】中得到满足了嘛!

上一节在描述状态迁移条件时,简单提过nrf24l01内部数据缓冲区的问题,这节正式讲解一下:

111111111111.png (87.05 KB, 下载次数: 27)

2019-6-13 14:01 上传

上面这个图就是缓冲区的框图。

可以看到,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 (273.23 KB, 下载次数: 24)

2019-6-13 14:02 上传

上面这张表是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 (27.37 KB, 下载次数: 18)

2019-6-13 14:03 上传

特别说一下命令结束时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 (28.94 KB, 下载次数: 14)

2019-6-13 14:04 上传

长度拿到之后,就可以使用 R_RX_PAYLOAD 命令将数据读出来了:

111111111111.png (26.92 KB, 下载次数: 10)

2019-6-13 14:05 上传

这张图是和上面那张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 (166.28 KB, 下载次数: 16)

2019-6-13 14:06 上传

想要拿到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 (144.43 KB, 下载次数: 17)

2019-6-13 14:07 上传

限于篇幅,每一个位段的含义就不展开了,图上的注释栏写的很清楚,大家自己阅读。

任何一个可以导致TX/RX FIFO发生变化的事件:

比如读写FIFO、清空FIFO、数据已发送成功、收到新数据等等,都会触发 FIFO_STATUS 的刷新。

我们的程序只要在需要的时候读取一下这个寄存器,就能全程实时掌握TX/RX FIFO的最新状态。

写程序的时候要根据需要灵活运用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值