介绍:
Gazell是一种协议,它可以在一个主机和多达8个设备的星型网络拓扑结构中建立一个强大的无线链路。它的设计目的是减少功率敏感的无线桌面产品的功耗,也适用于一系列其他的无线应用程序。
为了尽量减少功率敏感的外围设备的功耗,Gazell使用中央枢纽(主机端),其更放松的电源限制使连接打开,而外围设备可以休眠和节省电力消耗。关于这方面一个典型的例子:一个无线鼠标与一个USB接口,插入到计算机。
Gazell有一种复杂但易于使用的通道切换和同步方案,它可以增强对干扰和良好的无线共存属性的支持,同时还能实现高吞吐量和低延迟。
特性:
支持1主机,多达8从机的星状拓扑网络;
数据包认可、自动重发包来防止数据丢失;
每个管道有独立的TX,RX FIFO;
向下兼容nRF24L系列的Gazell;
从机自动同步到主机:建立连接时无需连接包,保持连接无需轮询包,从机可以随时进入、移出;
资源:
Gazell使用一套固定的资源,并要求对Gazell进行独占访问,以确保正确的操作。要使用的资源集有两种选择:
1、gzll_arm.lib
无线电(NRF_RADIO)
计时器:NRF_TIMER2)
PPI通道0,1,和2。
软件中断0
2、gzll_sd_resources_arm。lib(重用Nordic的S1xx蓝牙智能软设备库使用的相同资源):
无线电(NRF_RADIO)
计时器:NRF_TIMER0)
PPI通道8、9和10。
软件中断1
无线电和计时器中断处理程序运行在优先级0级(最高级别),而Gazell回调函数在优先级1级运行。应用程序应该在优先级2或更高级别运行,以确保正确的操作。
Gazell可以在运行时为一系列不同的应用程序进行定制。可以查看Gazell链接层了解配置函数列表,以及默认参数和常量参数。
注意:编辑包含默认参数和常量参数的头文件在编译新项目时不会改变它们的值。当使用预编译库进行应用程序时,这些值可以作为有用的参考。
Gazell 模型:
一旦使能Gazell功能,主机在网络中一直处于监听状态,设备负责建立连接的初始化工作。设备发送的每一个包都要求主机来响应。主机很可能在发数据给设备时搭销一个应答包。因此主机在发送任何数据前必须等待设备的响应包。
创建一个Gazell应用:
Gazell能自动处理所有的同步和包处理。用户需要做的是添加负载包到TX FIFO和从RX FIFO读取负载包。当接收到一个包时Gazell会自动通知应用程序。
创建一个Gazell应用,按如下步骤做:
1、使用nrf_gzll_init()初始化Gazell,选择作为主机或从机;
2、重新配置Gazell的默认参数。至少应该重新配置地址和通道,以避免干扰其他的Gazell网络。
3、使用nrf_gzll_enable()使能Gazell;
4、如果作为从机,开始发送:
使用nrfgzlladdpackettotxfifo()将有效负载添加到TX FIFO;
当调用nrfgzlldevicetxsuccess()回调时,处理返回的ACK包。使用nrfgzllfetchpacketfromrxfifo()从RX FIFO中获取有效负载;
当调用nrfgzlldevicetx失败()回调时,处理失败的包传输。失败的数据包会自动从TX FIFO中删除;
如果作为主机,开始监听:
当调用nrf_gzll_host_packet_received()回调函数时表示在处理接收到的数据包。使用nrfgzllfetchfromrxfiro从RX FIFO中获取数据包;
使用nrfgzlladdpackettotxfifo()将任何有效载荷发送到TX FIFO;
可以使用nrf_gzll_disable()函数在任何时候禁用Gazell。当这个函数被调用,Gazell会在失能前完成正在传输的数据。当这个回调完成,Gazell环境、广播和Gazell定时器将会停止。现在可以调用任何配置集函数,一旦Gazell再次启用,这将是有效的。
数据包传输机制:
主机和从机之间的一种典型的 数据包传输机制组成包括设备发送一个初始化传输数据包给主机及主机返回一个ACK包。
当设备接收到一个ACK数据包时,它知道初始化的数据包是成功传输的,而nrf_gzll_device_tx_success()回调函数将被调用以通知应用程序。
类似地,当主机接收到初始数据包时,将调用nrf_gzll_host_rx_data_ready()回调函数,以通知应用程序已经接收了一个新数据包。
注意,这些回调函数实际上是排队的,因此应用程序可以避免竞态条件。
如果设备的初始数据包没有被主机正确接收,或者相应的ACK数据包没有被设备正确接收,那么事务就会失败。注意,有一个失败的循环冗余检查(CRC)的数据包被Gazell忽略了。
如果事务失败,设备将尝试将初始包重新传输到主机,直到最终收到ACK,或者达到最大的传输尝试次数。如果达到最大的传输尝试次数,则将停止重新传输,并且将调用nrf_gzll_device_tx_failed()回调。
如果从主机发送到设备的ACK数据包丢失了,但是初始包和随后的重新传输尝试都被主机成功地接收了。重复的数据包将被主机丢弃,但ACK数据包仍将被发送回设备。这可以防止应用程序在主机上接收重复的数据包。
数据包的ID:
从设备传输到主机的任何数据包,都是由包头的两个位包ID字段和包的16位循环冗余校验(CRC)唯一标识的。如果它具有相同的负载,那么这个包ID用于区分一个新的包和以前的包。
管道和寻址:
节点上的每个逻辑地址都称为管道。每个管道映射到在传输或接收数据包时使用的一个空中地址。
除了1字节的前缀地址外,在空中的地址由2-4个字节长的“基本地址”组成。请注意,nRF51电台使用一个交替序列的0和1作为包的序文。因此,对于要正确接收的数据包,基本地址的最重要的字节不应该是0和1的交替序列,也就是说,它不应该是0x55或0xAA。
管道0有它自己唯一的基址地址,也就是基址0,而管道1-7使用相同的基址,也就是基址1。
这8个管道中的每一个都有一个独特的字节前缀地址。
在空中,每个地址字节中最重要的位将首先被传送。4字节长的基本地址中最重要的字节是第一个传输地址字节,而前缀字节是最后传输的。
FIFOs:
设备和主机上的所有8个管道都有两个先入先出(FIFO)缓冲区,可以容纳包。每个管道都有一个TX FIFO和一个RX FIFO。ficfo的总包数是6,而每一个单独的TX或RX FIFO(8个管道x 2=16)可以存储3个包。
从机FIFO处理:
当在设备模式下启用Gazell时,上传到TX FIFO上的任何数据包将在下一次机会中传输。如果有几个TX的FIFOs包含了包,那么各种TX的FIFOs将以轮询的方式提供服务,这意味着即使是在其他TX的FIFOs中不断添加包,也不会有TX的FIFOs体验到饥饿。
当成功地从主机接收到ACK时,它意味着有效负载被成功接收并添加到主机的RX FIFO中,成功传输的数据包将从TX FIFO中删除,以便在FIFO中传输下一个包。
如果一个设备收到的ACK包含一个负载,这个负载将被添加到管道的RX FIFO中。
如果设备上的特定管道的RX FIFO是满的,并且不能容纳任何新的包,则不会从该管道上的设备发送新的数据包。在这种情况下,我们永远不会结束在ACK中收到的负载必须被丢弃的情况,因为管道的RX FIFO是满的。
主机FIFO处理:
当在主机模式启用了Gazell时,所有激活的管道(地址)都同时被监控以接收传入的数据包。
如果接收到的新数据包未添加到管道的RX FIFO中,并且管道的RX FIFO有可用的数据包空间,那么该包将被添加到RX FIFO中,并将发送一个ACK以返回到该设备。如果管道的TX FIFO包含任何数据包,那么TX FIFO中的下一个可用的包将作为负载在ACK包中附加。为了让一个TX包被附加到ACK上,这个TX信息包必须在接收到数据包之前被上传到TX FIFO。
由于ACK不会总是被设备成功地接收,所以在ACK中添加的数据有效载荷不会立即从TX FIFO中移除。当在同一管道上收到一个新的包(新的包ID或CRC)时,将从TX FIFO中删除这个TX包。在这种情况下,从设备发送的新数据包作为对主机先前发送的ACK的确认。发送回转发尝试的ack包含相同的TX有效负载。
当主机在多个管道上处理数据包时,需要注意确保在不再使用的管道上的TX FIFOs中的ACK负载不会占用内存池中的空间,从而阻塞其他管道上的通信。为了避免这种堵塞,在主机上的应用可以将TX的FIFOs冲洗掉,而不再使用。
回调排队:
Gazell包含一个内部回调队列,用于排队等待回调函数,当Gazell尝试调用一个新的回调函数时,应用程序已经为以前调用的回调函数提供服务。
例如,如果当应用程序正在调用nrf_gzll_host_rx_data_ready()函数处理上一个包,而主机接受了一个新的包。用于处理最新包的nrf_gzll_host_rx_data_ready()回调函数将加入到回调队列并在下一次机会中使用。在这种情况下,每个接受到的包将调用一次nrf_gzll_host_rx_data_ready(),并且应用程序不需要处理可能的竞态条件场景,在应用程序即将退出nrf_gzll_host_rx_data_ready()函数之前,接收一个新的数据包。
类似德,设备每次接收到一个ACK都会调用一次nrf_gzll_device_tx_success()回调函数,即使在应用程序为nrf_gzll_device_tx_success()回调之前传输的数据包时,也会收到一个ACK。
回调队列的大小由NRF_GZLL_CONST_CALLBACK_QUEUE_LENGTH定义。
时隙:
Gazell的一个核心参数是timeslot。timeslot可以被看作是内部的Gazell“心跳”。
在一个设备中,任何包传输(新的包和重传输包)将在一个timeslot的开始处启动,并且只有一个包事务(包括ACK)可以在一个timeslot中发生。
同样,在主机端,广播在timeslot开始时启动一个广播启动,以开始监听。此外,它还可以随意更改它监听的射频通道。
心跳周期是使用nrf_gzll_set_timeslot_period()函数设置的。
跳频:
为了确保与其他无线产品的良好共存性能,如wi - fi或蓝牙,Gazell将在不同的射频通道之间实现跳频机制。
在启用时,Gazell将从预定义的通道表中选择通道。
该通道表的内容和大小可以由应用程序重新配置,但是设备和主机必须配置为具有完全相同的通道表。
当指定通道表时,应用程序总计可以从80个通道组中挑选。通常,3 - 7通道的通道表显示在大多数环境中提供了令人满意的共存性能。
使用过大的通道表可能会增加传输延迟和功耗,而使用过小的通道表可能会降低共存性能。
决定通道跳跃行为的核心参数为:
1、timeslots_per_channel(适用于主机和“同步”设备,由nrf_gzll_set_timeslots_per_channel()设置。
2、timeslots_per_channel_when_device_out_of_sync(只适用于“不同步”设备,由nrf_gzll_set_timeslots_per_channel_when_device_out_sync()设置。
3、channel_selection_policy(只适用于“同步”设备,由nrf_gzll_set_device_channel_selection_policy()设置)。
使用哪一个取决于Gazell是“同步”还是“不同步”(这些术语在同步部分中被描述)。因此,我们将不会对这两个术语进行区分,而是使用timeslots_per_channel。
timeslots_per_channel参数决定在通道更改之前,Gazell在一个通道上驻留的时隙的数量。当下一个时点在执行一个通道转换时,Gazell将从预定义的通道表中选择下一个通道,如果需要,则返回到channel表的开始位置。
注意:主机通道切换与设备通道切换相同。
在设备模式中,timeslots_per_channel也可以被看作是在切换通道之前在每个通道上花费的传输次数。这是因为在大多数情况下,每次都有一个传输尝试。
一个同步的设备使用channel_selection_policy参数来决定向主机发送新包时要使用的初始通道(也就是说,新包第一次被发送,而不是重新传输尝试)。
一旦与主机同步,设备就可以在当前信道上发送它认为主机在或在最后一个成功的通道上。这可以使用nrf_gzll_set_device_channel_selection_policy()来配置。
这个channel_selection_policy参数可以采用以下两个值:
- NRF_GZLL_DEVICE_CHANNEL_SELECTION_POLICY_USE_SUCCESSFUL
- NRF_GZLL_DEVICE_CHANNEL_SELECTION_POLICY_USE_CURRENT
通过选择NRF_GZLL_DEVICE_CHANNEL_SELECTION_POLICY_USE_SUCCESSFUL策略,该设备将开始在其上一个已成功确认传输的通道上发送数据包。这个策略是对静态干扰最有效的,就像设备找到一个安静的通道一样,它应该能够继续使用这个安静的通道。
通过选择NRF_GZLL_DEVICE_CHANNEL_SELECTION_POLICY_USE_CURRENT策略,该设备发送到它认为主机正在监听的通道上。这将达到两个策略的最低延迟和最高吞吐量,因为设备不需要等待主机在特定的通道上侦听。这项政策是跳频。该策略的缺点是,如果在某个特定的通道上存在静态干扰,则该设备将会浪费尝试发送此通道的数据包。注意,应用程序可以在运行时重新配置通道表以克服这个问题。
如前所述,通道选择策略只适用于初始传输包。如果此初始包的传输失败,则将始终在设备认为主机正在监视的通道上发送以下重新传输尝试。
如果Gazell“不同步”,Gazell将总是使用之前成功的传输通道立即启动数据包传输。如果Gazell从未发送过一个成功的数据包,因此没有“以前成功的通道”,那么Gazell将开始使用通道表中的第一个通道。
同步:
Gazell的内部timeslot或“heartbeat”机制用于获得同步通信,同时仍然支持高效的通道切换。当一个设备需要切换到一个新的通道时,这个机制是有用的,因为当无线电干扰在当前信道上有经验时。
每个Gazell设备有两个同步状态:“同步”和“不同步”。
在主机上,在启用Gazell时,内部的“heartbeat”计时器将一直运行,独立于设备的同步状态。
在设备上,“心跳”计时器只会在设备“同步”时运行,或只要有发送的数据包。如果计时器停止,并且将数据包添加到TX FIFO,计时器将立即启动。
在任何数据包被成功接收和确认之前,设备是不同步的。在这个状态中,设备转换通道由timeslots_per_channel_when_device_out_sync决定。设备切换通道的速度低于主机(由timeslots_per_channel确定),以便设备最终在主机所在的同一频道上传输数据包。
当设备成功地传输数据包时,当从主机接收到一个ACK数据包时,设备将进入“同步”状态,因为它现在有继续“猜测”主机将要监听的下列通道所需的信息。
为了知道何时更改通道,Gazell有一个内部的timeslot_counter来计算驻留在单个通道上的时间量。当这个计数器到达timeslots_per_channel时,timeslot_counter被重置,channel_index增加(循环)。当接收到ACK时,设备知道主机使用的当前信道,但它不知道主机上的timeslot_counter状态。因此,只有在timeslot_counter = 0的时候,设备才可以确信它“猜测”主机正在监视的正确通道。因此,当收到ACK时,当前timeslot的timeslot_counter被重置为0,当设备上的timeslot_index计数器为0时,将启动一个新的设备传输。然而,重新传输的尝试总是被发送到所有的时间点上
一旦设备同步,它将保持内部计时器运行,以便保持内部心跳,以便与主机保持同步。设备在同步状态中保持同步的时间是sync_lifetime,并在timeslots中测量。当接收到数据包时,sync_lifetime将被重置。一旦在设备上使用了sync_lifetime,就会停止内部计时器,然后设备返回到不同步行为。
注意,当一个“同步”的设备发送一个包但没有收到ACK时,它将继续发送最大的传输次数。
对于需要发送数据包的频率,以及由于时钟漂移和无线电干扰而只能维持有限时间的事实,应该选择sync_lifetime。同步使用寿命是使用nrf_gzll_set_sync_lifetime()配置的。
当重新传输的数量接近于零的时候,设备就可以知道同步已经实现了。nrf_gzll_device_tx_info_t结构传递给设备回调函数,并包含当前数据包所需的传输次数。另外,nrf_gzll_device_tx_info_t包含num_channel_交换机参数,该参数可用于应用程序,以确定RF通道是否可靠。这将使应用程序能够跟踪不良通道并在需要时更新主机和设备上的通道表。
向下兼容:
Gazell链接层示例并没有完全“跳出框”,与nRF24Lxx设备的nRFgo SDK中提供的遗留Gazell示例兼容。默认的timeslot周期和通道表需要进行调整,也需要一些设置来模拟Gazell模式。注意,Gazell“低功率主机模式”(主机模式1)在nRF51中不受支持。
频道表:
默认的通道表需要调整。
改变这些值:编辑gzll_params。h文件在nRF24Lxx项目中使用,或在nRF51项目中使用nrf_gzll_set_channel_table()函数。
时隙周期:
Gazell链接层支持以下最小时间周期:600us时隙周期用于nRF51Gazell设备到nRF51Gazell主机;504us时隙周期用于nRF51Gazell设备到nRF24LXX Gazell主机;
在使用504us时隙周期,适用以下限制:最大有效载荷大小为17字节;最大载荷大小是10个字节;
此外,设备与主机定时参数之间的关系应如下:
主机在GZLL_RX_PERIOD数的微秒中监听每个通道,其中GZLL_RX_PERIOD是nRF24Lxx设备的心跳间隔。
这个主机GZLL_RX_PERIOD必须大于所需的时间(包括ACK等待时间)。
改变这些值:
编辑gzll_params.h文件在nRF24Lxx项目中使用,或在nRF51项目中使用nrf_gzll_set_timeslot_period()函数(即。,nRF51的时间lot = 0.5* GZLL_RX_PERIOD)。
模拟遗留Gazell模式:
nRF51系列的Gazell链路层协议与nRF24Lxx设备的Gazell链路层最有用的模式相兼容。
模拟遗留nRF24Lxx Gazell设备模式2和nRF24Lxx主机模式0。
遗留的“设备模式2”可以模拟如下:
1、通道选择策略与NRF_GZLL_DEVICE_CHANNEL_SELECTION_POLICY_USE_SUCCESSFUL等效
2、当Gazell“不同步”时,在通道切换之前,每个通道可能会出现大量的尝试。
3、当Gazell是“同步”时,在通道被转换之前,在每个通道上都允许有少量的传输尝试,通常是2次。
遗留的“主机模式0”有以下行为:
1、在启用时,主机总是打开的。
2、当启用时,主机将通过通道表不断循环。
可以使用下面的代码片段获得此行为。在这里,我们假设有一个包含3个通道的channel表my_channel_table[]。这可以通过以下代码片段实现: