Nordic-Thingy52-FW DFU

GATT(Generic Attribute Profile):

GATT是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范,这些很短的数据段被称为属性(Attribute)。

GATT连接是独占的。也就是一个BLE外设同时只能被一个中心设备连接。一旦外设被连接,它就会马上停止广播,这样它就是一个BLE外设同时只能被一个中心设备连接。一旦外设被连接,它就会马上停止广播,这样它就对其他设备不可见了。当设备断开,它又开始广播。中心设备和外设需要双向通信的话,唯一的方式就是建立GATT连接。

GAP(Generic Access Profile):

它在用来控制设备和广播。GAP使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。例如Beacon设备就只是向外广播,不支持连接,小米手环就等设备就可以与中心设备连接。

GAP给设备定义了若干角色,其中主要的两个是:外围设备(Peripheral)和中心设备(Central)。

外围设备:这一般就是非常小或者简单的低功耗设备,用来提供数据,并连接到一个更加相对强大的中心设备。例如小米手环。

中心设备:中心设备相对比较强大,用来连接其他外围设备。例如手机等。

在GAP中外围设备通过两种方式向外广播数据:Advertising Data Payload(广播数据)和Scan Response Data(扫描回复),这里包含一些设备额外的信息,例如设备的名字。

CCCD (Client Characteristic Configuration

Descriptor),这个描述符是给任何支持通知或指示功能的特性额外增加的。在CCCD中写入“1”使能通知功能,写入“2”使能指示功能,写入“0”同时禁止通知和指示功能。这个描述符是服务器来配置的,通过写事件来向其中写入值。

DFU(Device Firmware Update),设备固件升级,而OTA(Over The Air)是实现DFU的一种方式,OTA的全称应该是OTA DFU,即通过空中无线方式实现设备固件升级。为了方便起见,直接用OTA来指代固件空中升级。只要是通过无线通信方式实现DFU的,都可以叫OTA,比如2G/3G/4G/WiFi/蓝牙/NFC/Zigbee,他们都支持OTA。DFU除了可以通过无线方式(OTA)进行升级,也可以通过有线方式进行升级,比如通过UART,USB或者SPI通信接口来升级设备固件。不管采用OTA方式还是有线通信方式,DFU包括后台式(background)和非后台式两种模式。

后台式DFU,又称静默式DFU(Silent DFU),在升级的时候,新固件在后台悄悄下载,即新固件下载属于应用程序功能的一部分,在新固件下载过程中,应用可以正常使用,也就是说整个下载过程对用户来说是无感的,下载完成后,系统再跳到BootLoader模式,由BootLoader完成新固件覆盖老固件的操作,至此整个升级过程结束。

非后台式DFU,在升级的时候,系统需要先从应用模式跳入到BootLoader模式,由BootLoader进行新固件下载工作,下载完成后BootLoader继续完成新固件覆盖老固件的操作,至此升级结束。

双区DFU(dual bank)和单区DFU(single

bank),双区或者单区DFU是新固件和老固件覆盖的两种方式。后台式DFU必须采用双区模式进行升级,即老系统(老固件)和新系统(新固件)各占一块bank(存储区),假设老固件放在bank0中,新固件放在bank1中,升级的时候,应用程序先把新固件下载到bank1中,只有当新固件下载完成并校验成功后,系统才会跳入BootLoader模式,然后擦除老固件所在的bank0区,并把新固件拷贝到bank0中。非后台式DFU可以采用双区也可以采用单区模式,与后台式DFU相似,双区模式下新老固件各占一块bank(老固件为bank0,新固件为bank1),升级时,系统先跳入BootLoader模式,然后BootLoader程序把新固件下载到bank1中,只有新固件下载完成并校验成功后,才会去擦除老固件所在的bank0区,并把新固件拷贝到bank0区。单区模式的非后台式DFU只有一个bank0,老固件和新固件分享这一个bank0,升级的时候,进入bootloader模式后立马擦除老固件,然后直接把新固件下载到同一个bank中,下载完成后校验新固件的有效性,新固件有效升级完成,否则要求重来。跟非后台式DFU双区模式相比,单区模式节省了一个bank的Flash空间,在系统资源比较紧张的时候,单区模式是一个不错的选择。不管是双区模式还是单区模式,升级过程出现问题后,都可以进行二次升级,都不会出现“变砖”情况。不过双区模式有一个好处,如果升级过程中出现问题或者新固件有问题,它还可以选择之前的老固件老系统继续执行而不受其影响。而单区模式碰到这种情况就只能一直待在bootloader中,然后等待二次或者多次升级尝试,此时设备的正常功能已无法使用,从用户使用这个角度来说,你的确可以说此时设备已经“变砖”了。所以说,虽然双区模式牺牲了很多存储空间,但是换来了更好的升级体验。

如果采用双区模式DFU,那么Bank0放的是应用程序,即老固件,Bank1放的是新固件。平时,Bank1为空或者忽略,系统只跑Bank0里面的应用程序;升级的时候,先跳到BootLoader,然后接收新固件并把它放在Bank1中,最后把Bank1里面的固件拷贝到Bank0中。

如果采用单区模式,则没有Bank1这个区。平时,系统只跑Bank0里面的代码;升级的时候,跳到BootLoader,先擦除Bank0里面的老程序,并把新固件直接放在Bank0中。

根据升级时如何跳转到Bootloader,Nordic SDK又将DFU分为按键式DFU和非按键式(Buttonless)DFU:

按键式DFU:就是上电时长按某个按键以进入bootloader模式。

非按键式DFU:就是整个DFU过程中设备端无任何人工干预,通过BLE/UART/USB接口给应用程序发送一条指令,应用程序收到指令后再自动跳入bootloader模式。

不管是按键式DFU还是非按键式DFU,两者只是进入BootLoader的方式不一样,其余基本一样,尤其是BootLoader工作过程基本上是一模一样的。

程序跳到BootLoader后,根据BootLoader需不需要对新固件进行验签,Nordic SDK又把DFU分为开放式DFU和安全式DFU(又称签名DFU)。

Legacy DFU:BootLoader不做任何验证,直接把新固件接收下来。

Security DFU:BootLoader存有一把公钥,BootLoader会先用这把公钥验证新固件的签名,只有验签通过,才允许后续的工作:比如把新固件接收下来;如果验签失败,BootLoader将拒绝升级,重新跳回应用程序。

BootLoader可以通过不同的通信接口来接收新的固件,Nordic

SDK支持BLE,UART和USB三种接口。


nRF52的启动流程:

上电后,系统先执行softdevice,softdevice通过读取UICR一个寄存器的值,来判断目前系统是否有BootLoader,如果没有BootLoader,系统直接跳到application;如果有BootLoader,系统先跳到BootLoader,BootLoader再根据目前的情况来决定是进入升级模式还是跳往application,BootLoader主要判断如下几种情况:

按键是否按下

保持寄存器GPREGRET1是否为0xB1

上次DFU过程是否还在进行中

应用程序校验是否通过

如果按键没有按下,GPREGRET1不为0xB1,本次复位不是上次DFU的继续,并且应用程序校验通过,那么BootLoader就会直接跳到application,去执行application应用程序。那怎么去校验应用程序的有效性呢?为此BootLoader引入了一个放在Flash的结构体参数:m_dfu_settings_buffer(数据类型:nrf_dfu_settings_t),这个结构体参数虽然只有896字节,但由于Flash只能按页擦除,所以这个参数实际占用了一个Flash page,这个page称为settings page。

Bootloader Setting Page是Bootloader工程开辟的一段Flash空间,其中保存了固件镜像的信息和DFU进度信息,这些信息称为Bootloader Settings。nRF52832芯片的Flash大小为512 kB(0x0008 0000),Settings位于最顶端(0x0007 F000 – 0x0008 0000),大小为1 Page(4 kB)。在Settings的下方是MBR

Param Storage,它的长度也是4 kB,地址为:0x0007 E000 – 0x0007 F000。这块区域用于存放Settings的备份。Bootloader启动时候,会检查Settings中以下信息:

bank0_bank_code

bank0_img_crc

如果二者都正确,则执行跳转进入Application,否则驻留在Bootloader中执行DFU。

nRF52系列芯片,Settings的位置如图:

settings page放在Flash的最后一个页面,settings page目前有2个版本:版本1(SDK15.2及以前版本)和版本2(SDK15.3及以后版本),版本2可以兼容版本1,前面所述的896字节是指settings page版本2的大小。

Settings page包含的信息比较多,用得比较多的是:

各种版本信息

DFU升级过程信息

Application image的CRC值和大小

应用程序的bonding信息

Init command内容

application/softdevice的启动校验信息(版本2才有)

版本1的settings page只校验application image的CRC值,如果CRC匹配,则认为application有效。

版本2的settings page不仅可以校验application image的CRC值,还可以校验application/softdevice的CRC值或者hash值或者签名,你可以选择你自己想要的校验方式,只有CRC值或者hash值或者签名校验通过(三选其一),应用程序才算有效,这时BootLoader才会跳到application去执行。

无按键式BLE OTA的工作流程:

1)     正常启动后,系统运行在应用程序中,此时手机通过app发送一条开始DFU的指令给设备,设备收到指令后,将GPREGRET1赋值0xB1,并触发软复位

#defineBOOTLOADER_DFU_GPREGRET                    (0xB0)    

#defineBOOTLOADER_DFU_START_BIT_MASK          (0x01)    

#defineBOOTLOADER_DFU_START   (BOOTLOADER_DFU_GPREGRET | BOOTLOADER_DFU_START_BIT_MASK)

sd_power_gpregret_set(0,BOOTLOADER_DFU_START);

//手机Android端log

D/BluetoothGatt:setCharacteristicNotification() - uuid: 8ec90004-f315-4f60-9fb8-838830daea50enable: true

D/BluetoothGatt:writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb

D/BluetoothGatt:onDescriptorWrite() - Device=C7:77:73:**:**:** handle=17

I/DfuImpl: SendingEnter Bootloader (Op Code = 1)

2)     复位后,系统再次进入BootLoader,因为GPREGRET1等于0xB1,BootLoader进入DFU模式,等待新固件接收

static booldfu_enter_check(void)

{

    if (NRF_BL_DFU_ENTER_METHOD_PINRESET&&

       (NRF_POWER->RESETREAS &POWER_RESETREAS_RESETPIN_Msk))

    {

        NRF_LOG_DEBUG("DFU mode requestedvia pin-reset.");

        return true;

}

}

3)     手机先将init packet发送给设备,设备先做前期检验prevalidation,主要是版本校验以及签名验签,校验通过后,更新settings

 page并准备开始数据接收。

nrf_dfu_validation_prevalidate();

//手机Android端log

I/DfuImpl: Sending 141 bytes of init

packet...

I/DfuImpl: Sendinginit packet (Value =12-8A-01-0A-44-08-01-12-40-08-03-10-34-1A-02-CB-01-20-00-28-00-30-00-38-D4-87-05-42-24-08-03-12-20-A0-AA-67-6B-51-58-81-96-0A-D5-01-21-EB-6E-4F-0E-A1-B6-36-1F-0C-2E-87-DA-F4-17-81-D2-DE-3F-8F-45-48-00-52-04-08-01-12-00-10-00-1A-40-31-B2-5C-54-CF-41-FB-68-05-68-8E-FD-19-FB-45-AD-C0-F7-08-C9-25-84-A0-CC-E4-E0-4E-CE-AF-44-A7-EE-E7-36-C7-67-A1-DC-A7-F6-B5-82-B3-AC-17-56-82-FE-81-6E-BB-45-48-2B-F2-E8-03-A3-73-4A-E5-EA-9A-14)

D/BluetoothGatt:writeCharacteristic() - uuid: 8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

I/DfuImpl: Sending Calculate Checksumcommand (Op Code = 3)

D/BluetoothGatt:writeCharacteristic() - uuid: 8ec90001-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onNotify() - Device=C7:77:73:**:**:** handle=18

    onCharacteristicWrite() -Device=C7:77:73:**:**:** handle=18 Status=0

I/DfuImpl: Checksumreceived (Offset = 141, CRC = 03F169BE)

    Executing init packet (Op Code = 4)

D/BluetoothGatt:writeCharacteristic() - uuid: 8ec90001-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=18 Status=0

D/BluetoothGatt:onNotify() - Device=C7:77:73:**:**:** handle=18

4)     接收新固件。每接收4kB数据,回复一次CRC校验值,直至整个新固件image接收完毕,如果新固件校验通过(版本1校验CRC值,版本2校验hash值),就会去invalidate bank0里面的老固件,更新settings page,并再次触发软复位。

//手机Android端log

I/DfuImpl: Uploading firmware...

D/BluetoothGatt:writeCharacteristic() - uuid: 8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

D/BluetoothGatt:writeCharacteristic() - uuid: 8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

D/BluetoothGatt:writeCharacteristic() - uuid: 8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

D/BluetoothGatt:writeCharacteristic() - uuid: 8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid:8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

    writeCharacteristic() - uuid: 8ec90002-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onCharacteristicWrite() - Device=C7:77:73:**:**:** handle=16 Status=0

I/DfuImpl: Sending Calculate Checksumcommand (Op Code = 3)

D/BluetoothGatt:writeCharacteristic() - uuid: 8ec90001-f315-4f60-9fb8-838830daea50

D/BluetoothGatt:onNotify() - Device=C7:77:73:**:**:** handle=18

    onCharacteristicWrite() -Device=C7:77:73:**:**:** handle=18 Status=0

I/DfuImpl: Checksum received (Offset =4096, CRC = 825DA3DF)

5)     BootLoader启动后发现有新固件需要activate(激活),此时会去擦掉bank0里面的固件,并把bank1里面的固件拷贝到bank0,然后更新settings page,并再次触发软复位。

注:上面讲的是dual bank的流程,single bank与之相似,只不过在第3)步的时候就会去擦除老固件。

6)     BootLoader再次启动后,检查新image的有效性,校验通过后,跳到新的application去执行代码

从上面流程可以看出,DFU过程中,系统需要跑两段完全独立的代码:Application和BootLoader,Application和BootLoader都支持蓝牙功能,也就是说,两者都有自己的蓝牙广播和蓝牙连接。这里面有一个问题:当系统从Application跳到BootLoader后,手机怎么辨别两者为同一个设备?很多人会说,可以让BootLoader和Application两者的广播名字一样,根据广播名字的一致性,来判断二者来自同一个设备。这种方法存在两个问题:

一:大部分手机都支持GATT cache(缓存)功能,当application跟手机相连后,手机会把application的GATT数据缓存下来以加快下次连接的速度(这个现象在苹果手机最明显),之后如果系统跳到BootLoader,然后再跟手机相连,如果两者的蓝牙设备地址一样,手机会认为是同一个设备,从而跳过服务发现的过程而直接使用之前缓存下来的GATT数据,这样会导致BootLoader的服务无法被手机发现,从而出现升级失败。

二:如果多个设备同时在升级,而我们仅仅依靠广播名字来决定两者属不属于同一个设备,这会导致设备A application有可能跟设备B的BootLoader进行错配。

为了解决这个问题,Nordic提出了两套方案:

方案一:假设application的蓝牙设备地址为x,跳到BootLoader后蓝牙设备地址会变成x+1,这样手机就可以通过这种地址+1的方式来辨别两者属不属于同一个设备,由于application和BootLoader使用不同的蓝牙设备地址,前面的GATT缓存问题也就不存在。关于方案一,有一个问题需要特别注意:如果你想修改例子默认的蓝牙设备地址(比如使用IEEE的public蓝牙MAC地址),此时一定要记得同时更改application和BootLoader的蓝牙设备地址,使他们满足+1的条件,否则Nordic手机DFU库无法辨别两者是否属于同一个设备,以致于无法完成OTA过程。

方案二:application和BootLoader的蓝牙设备地址一模一样,但设备跟手机执行配对和bonding操作,设备跟手机bonding后,就可以支持service changed indicate操作,这样跳到BootLoader后可以让手机主动再执行一次服务发现过程,从而解决GATT缓存问题。

签名验签的工作原理:首先,你需要一对公私钥,其中私钥用来生成新固件的签名,公钥用来验证签名的有效性,大家可以用nrfutil来生成自己需要的公私钥对,公私钥制作成功后,私钥一定要妥善保管。公钥可以变成一个.c文件,并覆盖DFU工程下的同名文件:dfu_public_key.c。其次,BootLoader要支持签名验签密码算法,这个DFU代码已经有了,并且有四种后端可选:micro-ecc,cc310_bl,Oberon和mbedtls,四选其一即可(这4种后端,只有cc310是硬件实现,其余都是软件实现),nRF52840推荐选择cc310作为算法后端,其他nRF52芯片推荐选择micro-ecc作为算法后端。micro-ecc效率高,占用的代码空间最小,但它的版权是CPOL,只要你能接受CPOL,那么推荐使用micro-ecc;反之,如果接受不了CPOL版权,而且硬件又不支持cc310,那么推荐使用Oberon,不过Oberon占用的代码空间比micro-ecc要大一些。再次,手机端要生成新固件的签名,并把新固件的签名传给设备端。大家还是可以用nrfutil去生成新固件的签名。最后,BootLoader接收到新固件hash值和签名,并使用自己的公钥对该签名进行验签。由于nrfutil是PC端应用程序,所以它可以集成各种加密算法库,并完成上面提及的公私钥对,hash和签名的生成工作。

NRF_SDH_BLE_OBSERVER用来为本地文件注册一个BLE回调函数(此处为ble_evt_handler),NRF_SDH_BLE_OBSERVER这个宏执行成功后,所有的BLE事件都会被ble_evt_handler捕获。进入ble_evt_handler,你会发现BLE有上百个回调事件,你不需要每个都处理,你只需要处理你关心的事件即可,比如连接成功事件BLE_GAP_EVT_CONNECTED或者连接断开事件BLE_GAP_EVT_DISCONNECTED。NRF_SDH_BLE_OBSERVER有一个很大的好处:某个文件如果需要捕获BLE事件,那么它只需在本文件中某处(可以在函数内也可以在函数外)调用NRF_SDH_BLE_OBSERVER这个宏去注册一个回调函数即可,而不再需要在其它文件中去注册这个回调函数,将模块的耦合性降到最低,符合模块化编程思想。

//APP固件主函数

int main(void)

{   

#ifdefBUTTONLESS_ENABLED

    // Initialize the async SVCI interface tobootloader before any interrupts are enabled.

    ret_code_t err_code =ble_dfu_buttonless_async_svci_init();

    APP_ERROR_CHECK(err_code);

#endif

    // Initialize.

    system_init();

    scheduler_init();

    log_init(); //打印日志初始化

    timers_init(); //APP timer初始化

#ifdef DEV_BSP

    buttons_leds_init();

#endif

    power_management_init(); //低功耗管理初始化

    ble_stack_init();  //蓝牙协议栈初始化

    mac_address_get();

#ifdefBOND_ENABLE   

    peer_manager_init();

#endif

    gap_params_init(); //修改广播名字和连接间隔的

    gatt_init(); //用来修改底层数据包长度的

    advertising_init(); //用来修改广播包内容,广播间隔以及广播超时时间

    services_init(); //蓝牙服务,characteristic,NUS, buttonless,

    advertising_init(); //为啥调用两次?

    conn_params_init(); //用来请求更新连接间隔

    fs_init();

    application_timers_start();

    // Start execution.

    NRF_LOG_INFO("Debug logging for UARTover RTT started.");

    ctl_advertising(); //启动广播         

    twi_master_init(); //I2C初始化

    nfc_init(); //NFC初始化

    wdt_init(); //看门狗初始化

    // Enter main loop.

    for (;;)

    {

        main_loop();

        app_sched_execute();

        //先把需要打印的日志打印完,然后让系统进入idle状态(SystemON),

        //在idle状态下,蓝牙连接或者广播可以正常进行而不受影响

        idle_state_handle();

    }

}


#defineNUS_BASE_UUID{{0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00,0x00, 0x40, 0x6E}}

uint32_tble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)

{

    //添加自定义UUID

    sd_ble_uuid_vs_add(&nus_base_uuid,&p_nus->uuid_type);

    //添加服务,UUID,返回服务的句柄

    sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,&ble_uuid,&p_nus->service_handle);

    //添加RX特征值

characteristic_add(p_nus->service_handle,&add_char_params, &p_nus->rx_handles);

//添加TX特征值

characteristic_add(p_nus->service_handle,&add_char_params, &p_nus->tx_handles);

}

ble_nus_init同时注册了nus_data_handler回调函数,当设备收到手机发过来的数据时,就会触发nus_data_handler,用户可以在nus_data_handler中对接收到的数据进行处理。


蓝牙数据传输吞吐率一个连接间隔传输的数据连接间隔一个数据包长度包数连接间隔


一个数据包的长度取决于下面两个参数:

#defineNRF_SDH_BLE_GAP_DATA_LENGTH  251

#defineNRF_SDH_BLE_GATT_MAX_MTU_SIZE        247

NRF_SDH_BLE_GAP_DATA_LENGT≥(NRF_SDH_BLE_GATT_MAX_MTU_SIZE+4) ,

NRF_SDH_BLE_GAP_DATA_LENGTH最大值为251,MTU设为247,相当于把数据包应用数据最大长度设为247-3=244字节


连接间隔由下面两个参数建议:

#defineMIN_CONN_INTERVAL              MSEC_TO_UNITS(15, UNIT_1_25_MS)

#defineMAX_CONN_INTERVAL              MSEC_TO_UNITS(30, UNIT_1_25_MS)

终连接间隔设为多少只能由蓝牙主设备决定,蓝牙从设备只有建议权。所以上面就是对主机提出建议:希望主机把连接间隔设为30ms,主机可以接受也可以拒绝这个请求。


一个连接间隔可以发多个包由下面参数决定:

#defineNRF_SDH_BLE_GAP_EVENT_LENGTH 6

你可能感兴趣的:(Nordic-Thingy52-FW DFU)