arduino笔记39:nrf4l01的中断详解

 

前面章节中,中断相关的内容时不时提到,这节详细说一下。
因为前面有过铺垫,所以就不按部就班的啰嗦前戏了,直接进入正题吧。
 
nrf24l01一共3种中断: TX_DS / RX_DR / MAX_RT
  • TX_DS : (TX data sent)通知本端主控,我方的数据已经发出去了。
  • RX_DR : (RX data received/ready)通知本端主控,我收到了新的数据,你要不要看看?
  • MAX_RT: (max for re-transmition)上节详细提过,不赘述了。
 
当任意一个或多个中断发生时,IRQ管脚都会被【nrf24l01主动】拉低,即IRQ管脚低电平触发。
程序知道了IRQ脚变成低电平,只能知道有中断发生了,但并不知道到底是哪个或哪些中断,所以nrf24l01通过STATUS寄存器记录了具体的中断源:
 
 
 

arduino笔记39:nrf4l01的中断详解_第1张图片

 

如上图,标出的每个bit表示对应的中断,bit=0表示当前没有此中断,bit=1表示当前此中断已经触发。
当IRQ拉低之后,程序读取这个寄存器,就能知道到底是哪个或哪些中断被触发了。
IRQ管脚被拉低之后,即使我们程序读取了STATUS,管脚【也不会自动地】回归高电平,即nrf24l01不会主动清理中断。

清理中断的工作必须由我们的程序亲自来操作,做法是:
【往STATUS寄存器对应bit位上写1】(不要觉得奇怪,这里我没写错,就是写1而不是写0)
只有当前【所有】被触发的中断【都】被清理了,IRQ管脚才会回归高电平。
除此之外,程序可以通过配置寄存器的方式来【分别禁用/屏蔽】这些中断:
 
 
arduino笔记39:nrf4l01的中断详解_第2张图片

 

如上图,nrf24l01将中断屏蔽位整合到了CONFIG寄存器中,给bit位写1则屏蔽对应中断,写0保持开启。
个人认为,这个屏蔽设计有些轻微脑残,我实在想不出需要把中断屏蔽掉的理由。
之所以在这儿添上这些内容,是为了提醒大家不要【误操作】:
在修改CONFIG其他bit位的时候,不要将中断屏蔽位给误写了,不然程序就再也收不到对应的中断通知了。
 
本节上面的描述中,暗含了一个意思:
IRQ管脚拉低时,可能是某中断单独发生,也可能是多个中断同时发生。
在【Enhanced ShockBurst】工作模式下:
PTX 存在TX_DS/RX_DR/MAX_RT这全部的3种类型的中断。
PRX 只存在TX_DS/RX_DR这2种类型的中断,MAX_RT永远不会出现在PRX端。
 
列一下中断可能出现的【所有的】组合情况:
 
 
 

arduino笔记39:nrf4l01的中断详解_第3张图片

 

如果上面表格中"说明"一栏你看了之后觉得更"说不明",那么继续往下看。
MAX_RT的含义以及什么时候出现,前面章节已经详细说过了,这里略过。
只说 TX_DS 和 RX_DR,nrf24l01具体满足什么条件时,这两个中断才会触发?

 

arduino笔记39:nrf4l01的中断详解_第4张图片

 

 

上面这张图原型来自datasheet的第7章第8节(第39页)。

描述的是在【Enhanced ShockBurst】工作模式下,PTX和PRX之间一次通信过程的详细时序。

通信的最终结果是数据传输完毕,两端分别触发了各自的IRQ。
我加了很多标注,非常详细,这里就不再解说过程了,我相信看这个标注图要比看大段文字更直观。

先去看图,确保看明白之后再往下看,不然也会很晕^_^
 
OK,标注图看明白了,我们继续。

现在从这张图中提炼一些有用的信息:
 
 
- 针对PTX发送的数据,PRX端的RX_DR在时间上要早于PTX端的TX_DS
 
这个结论也许让你有点意外,但确实是这样。
因为PTX端对通信管控负有更多的责任,必须收到对方的ACK之后才能确认自己发的数据被PRX收到了,才能放心的触发TX_DS。
而PRX不管这些,只要收到数据就触发RX_DR。
 
- PRX端的程序要好好利用回复ACK之前的这130微秒时间段
这句话里的【130微秒】指的是: 从【PRX触发IRQ】那一刻 到 【开始发送ACK包】那一刻之间的【PLL Lock】时间段。
我把这个时间段叫做【PRX的黄金时刻】。
 
这个时间段有什么特殊的地方?

如果PRX在这个时间段内,能把一些数据及时地塞进自己的TX FIFO里,那么Auto-ACK时,ACK包里就能带上这些数据!
这意味着,就在PTX端触发自己这边TX_DS的时候,就会收到这些数据,RX_DR必定会触发。
而如果PRX错过了机会,例如当ACK开始发送的时候,SPI上的数据还没写完,那么这次Auto-ACK就没机会捎上这些数据了。
这样的话,PTX想收到这些数据,必须等到下次通信了,时间被大大延后了。
 
假设这样一种应用场景:
PTX端是遥控设备(比如无线手柄),PRX端是受控设备(比如遥控小车/机械臂)。
遥控器时常发送一些指令(本质是一些约定好协议格式的数据),用来 查询/修改/操作 受控设备;
受控设备收到指令后,读取/解析/执行 指令,而且必须回复遥控器,例如回传查询结果/确认动作执行完成/参数修改成功等。
注意受控设备的工作逻辑,它是收到指令之后才去执行的,而且要及时回复数据给遥控器。
我们假设这些指令都很短,最长也不超过32字节,所以nrf24l01单包传送就能搞定。
 
这样的场景很常见吧?
受控设备(PRX)的工作逻辑决定了: 只有本端RX_DR IRQ触发以后,PRX才能知道该回复什么数据给PTX。
由此可知,想要最高效的回传数据,必须好好利用上文的【130微秒黄金时段】。
 
我们先看一下在上述场景中程序对RX_DR IRQ的一般化的处理过程:
(1)IRQ触发中断处理程序
(2)SPI访问STATUS寄存器,确认中断源为RX_DR (其实这步可以省略,PRX端IRQ响了,里面必定会有RX_DR的)
(3)SPI读取RX FIFO得到指令数据,读入RAM
(4)SPI写STATUS寄存器,清理中断源
(5)识别/解析/执行指令,然后生成回复数据
(6)SPI将回复数据写入TX FIFO
(7)处理结束
 
只要以上(1)-(6)步骤能在130微秒内全部完成,程序就能做到理论上的最快回复。
在回复数据长达32字节(当然要讨论最坏的情况)的情况下,能实现130微秒全部搞定么?

假设步骤1非常快,时间忽略。
步骤2/3/4/6的耗时和SPI的时钟速度有直接关系。
步骤5的耗时和nrf24l01无关,只和具体的程序逻辑有关。
步骤2使用NOP指令最快,SPI上要走2个字节。
步骤3根据【9. nrf24l01的数据缓冲区】这节中的内容可知,一对一通信SPI上【最多】要走大约2+33=35字节。
步骤4,SPI上要走2个字节。
步骤6,最长回复时,SPI上要走33字节。
加起来是2+35+2+33=72字节。

如果SPI的时钟速度是8MHz(对nrf24l01来说基本就是安全上限了,不要再高了),那么耗时72微秒。
还有各条命令中间CSN电平保持间隔以及杂七杂八的开销,估算10微秒应该够了。
这样就已经消耗了82微秒了。
这里提醒一下,就算你用的不是arduino,而是CPU时钟高达72M的stm32f103,这80多微秒是无法再减少的。
你的单片机再快(往往其片上SPI也会很快)也得适应nrf24l01上SPI接口的要求不是?
 
留给步骤5的时间:130-82=48微秒,约50微秒。
如果程序设计合理的话,我认为这些时间足够可以计算出回复数据了。
除此之外,还要考虑整个过程被其他中断打断的情况,这会增加额外的耗时。
如果SPI的时钟为6M甚至更低,130微秒几乎就不够了。
 
- PRX端TX_DS IRQ对分包传输的影响
再假设另外一个应用场景:
PRX作为环境数据采集终端,有一堆传感器,实时监测温度/湿度/光照等一些物理参数,每隔一分钟采集一次传感器数据;
PTX作为这些数据的接收终端,每隔十分钟从PRX取走它积攒的数据,后续用于存储/显示/统计/分析等用途。
 
在这里我们先不纠结【采集终端作为PTX还是作为PRX哪个更合理】以及【为啥不每隔一分钟就取走一次数据】的问题,
只关注在上面已设定好的方案中【数据传输】方面的问题。
 
十多次采集积攒的数据量肯定不少。
把这个场景提炼一下,其实就是: 怎样把大量的数据(大于32字节)从PRX端完整传输到PTX端?

结合本节前面的内容,方案就很明显了:
PRX端将数据拆包,然后通过多次Auto-ACK的方式,把数据传给PTX,PTX端再把分包数据还原。
假设要传输70字节有效数据,则需要拆分成3个数据包,列一下基本流程:
(1)PTX向PRX查询有多少数据要传送
(2)PRX通过查询包的Auto-ACK将长度信息回复给PTX
(3)分包依次传输这些数据,总共3次 (长度拿到了,PTX自然知道会有几包数据)
(4)PTX收到最后一包有效数据后,还必须给PRX发一个确认包,通知PRX:你的数据我已全部收取。
 
步骤4是必要的:
PRX【必须】要知道它的最后一包数据到底有没有被PTX收到,这样才能确认数据全部发完了。
PRX【只能】通过本端 TX_DS 中断才能确认某次数据传输(附带在Auto-ACK中的有效数据)OK。
PRX端的 TX_DS 触发依赖于 PTX端 【再多启动一次】通信,这就是上面所说的【确认包】。
 
我把完整的过程做了一张时序图:
 
 
 

arduino笔记39:nrf4l01的中断详解_第5张图片

 

- 【PRX对PTX的分包传输机制】对PTX端【应用数据】传输效率的影响
先定义一下什么是【应用数据】: 双方传输的那些数据中,和具体通信手段无关的那些数据被称为【应用数据】。
这里先声明一下,"应用数据"这个词是我为了描述方面而自造的词,不是标准的术语。
有一个更简单的辨别方式:就算通信模块被替换了,双方依然要传输的那些数据就是应用数据。

比如我们用串口或者蓝牙替代nrf24l01来通信,通信双方依然需要传输某些数据,且数据格式是不变的。
在【遥控器和小车】的场景里,控制指令就是应用数据,小车响应查询指令回复的查询结果也是应用数据。
在【环境数据采集】的场景里,采集到的温度湿度光照等也是应用数据。
 
再回到PTX/PRX,上面那个时序图中,【查询数据长度】【取数据】【确认结束】这些包不是应用数据。它们仅仅是【为了分包传输应用数据】而衍生出来的,这里我给他们起个自造的名字:【传输控制数据】。如果你用串口线把无线模块替换掉,那就没有这些东西了。
 
也就是说,在nrf24l01眼中的,【有效数据】其实分为两种:【应用数据】和【传输控制数据】。
【传输控制数据】是属于【传输层】(这个是标准术语^^)层面的东西,和具体的应用无关。
【应用数据】是属于【应用层】(这个也是标准术语^
^)层面的东西,和具体的通信媒介/通信手段无关。
 
好了,现在铺垫内容介绍完毕,开始说正题:
对传输效率的影响第1项: 一些冗余数据(【传输控制数据】) 的加入会降低【应用数据】的平均传输速度。
对传输效率的影响第2项: PTX在发送应用数据时,TX FIFO的32字节不能都用掉,必须留一部分空间给协议控制使用。
 
解释一下第2项:

PTX端程序在写入TX FIFO时,当然知道写入的数据是应用数据,PRX收到后通知程序,PRX端的程序读出数据。
但PRX端的程序只知道这是有效数据,却不知道这到底是应用数据还是传输控制数据。
数据的属性是必须区分的,应用数据要送去上层处理,而传输控制数据不能往上层送,要内部处理掉。
 
因此,一包最长32字节的有效数据中,要至少留出一个字节的位置来标识数据的属性:即到底是【应用数据】还是仅仅是【传输控制数据】。
例如PTX在写TX FIFO时,如果写的是应用数据,TX FIFO第1个字节写0x00,表示后面31字节数据均是应用数据;
而如果写入的是传输控制数据,那么TX FIFO的第1个字节要写0x01,表示后面的31字节数据是传输控制数据。

PRX端的程序在读出这份数据后,发现第1字节为0x00,那么就知道是应用数据,取出后送往上层,否则当传输控制数据去解析处理。
当然,这个额外要求只是针对PTX的,PRX在回复数据的时候不需要这样做,32字节空间随便用就好。
因为PRX是【主接收端】,只是被动响应,主动权在PTX手里,PRX回复什么完全是应PTX的要求。

所以PTX对于收到的Auto-ACK数据是什么非常清楚。
 
 

温馨提示:
最后关于多包传输的这部分内容,仅仅是我自己根据对nrf24l01 IRQ的了解推演出的一些关于编程方面的东西,只是一个尽量通用的方案。这部分不是官方datasheet的内容。大家编程时仅参考即可,不必拘泥。对于你自己的项目,可以在这些内容的基础上进行简化/改造/升级。
适合自己的才是最好的!
 
【把和IRQ有关的,能想到的东西都写了,此节完^_^】

 

 

 

 

 

 

你可能感兴趣的:(arduino学习笔记)