蓝牙BLE详解,有这一篇就够了


什么是蓝牙?编程


    蓝牙是一种短距的无线通信技术,可实现固定设备、移动设备之间的数据交换。通常将蓝牙3.0以前的BR/EDR蓝牙称为传统蓝牙,而将蓝牙4.0规范下的LE蓝牙称为低功耗蓝牙。缓存

    不少人对蓝牙的认识还很局限于手机领域,其实蓝牙的应用已经远远不止于此。过去几年里,蓝牙的增加量就达到了80%,固然,低功耗蓝牙的出现也起到关键的做用,相信将来蓝牙会开创一个可交互的物联世界。安全

    标准分类
微信

    蓝牙4.0标准包括传统蓝牙模块部分低功耗蓝牙模块部分,是一个双模标准。低功耗蓝牙也是创建在传统蓝牙基础之上发展起来的,并区别于传统模块,最大的特色就是成本和功耗下降,应用于实时性要求比较高。网络

    BLE(Bluetooh Low Energy)蓝牙低能耗技术是短距离、低成本、可互操做性的无线技术,它利用许多智能手段最大限度地下降功耗。并发

    BLE技术的工做模式很是适合用于从微型无线传感器(每半秒交换一次数据)或使用彻底异步通讯的遥控器等其它外设传送数据。这些设备发送的数据量很是少(一般几个字节),并且发送次数也不多(例如每秒几回到每分钟一次,甚至更少)。app


BLE协议栈的结构和配置异步

一、协议有两个部分组成:Controller和Host编辑器

二、Profiles和应用老是基于GAP和GATT之上函数

三、在单芯片方案中,Controller和Host,profiles,和应用层都在同一片芯片中

四、在网络控制器模式中,Host和Controller是在一块儿运行的,可是应用和profiles在另一个器件上,好比PC或者其余微控制器,能够经过UART,USB进行操做

五、在双芯片模式中,Controller运行在一个控制器,而应用层,profiles和Host是运行在另一个控制器上


BLE设备链接状态流程图


BLE协议栈各层功能机制


低功耗蓝牙体系结构

 如上图所述,要实现一个BLE应用,首先须要一个支持BLE射频的芯片,而后还须要提供一个与此芯片配套的BLE协议栈,最后在协议栈上开发本身的应用。能够看出BLE协议栈是链接芯片和应用的桥梁,是实现整个BLE应用的关键。那BLE协议栈具体包含哪些功能呢?简单来讲,BLE协议栈主要用来对你的应用数据进行层层封包,以生成一个知足BLE协议的空中数据包,也就是说,把应用数据包裹在一系列的帧头(header)和帧尾(tail)中。具体来讲,BLE协议栈主要由以下几部分组成:

  • PHY(Physical layer物理层)。PHY层用来指定BLE所用的无线频段,调制解调方式和方法等。PHY层作得好很差,直接决定整个BLE芯片的功耗,灵敏度以及selectivity等射频指标。

  • LL(Link Layer链路层)。LL层是整个BLE协议栈的核心,也是BLE协议栈的难点和重点。像Nordic的BLE协议栈能同时支持20个link(链接),就是LL层的功劳。LL层要作的事情很是多,好比具体选择哪一个射频通道进行通讯,怎么识别空中数据包,具体在哪一个时间点把数据包发送出去,怎么保证数据的完整性,ACK如何接收,如何进行重传,以及如何对链路进行管理和控制等等。LL层只负责把数据发出去或者收回来,对数据进行怎样的解析则交给上面的GAP或者ATT。

  • HCI(Host controller interface)。HCI是可选的,HCI主要用于2颗芯片实现BLE协议栈的场合,用来规范二者之间的通讯协议和通讯命令等。

  • GAP(Generic access profile)。GAP是对LL层payload(有效数据包)如何进行解析的两种方式中的一种,并且是最简单的那一种。GAP简单的对LL payload进行一些规范和定义,所以GAP能实现的功能极其有限。GAP目前主要用来进行广播,扫描和发起链接等。

  • L2CAP(Logic link control and adaptation protocol)。L2CAP对LL进行了一次简单封装,LL只关心传输的数据自己,L2CAP就要区分是加密通道仍是普统统道,同时还要对链接间隔进行管理。

  • SMP(Secure manager protocol)。SMP用来管理BLE链接的加密和安全的,如何保证链接的安全性,同时不影响用户的体验,这些都是SMP要考虑的工做。

  • ATT(Attribute protocol)。简单来讲,ATT层用来定义用户命令及命令操做的数据,好比读取某个数据或者写某个数据。BLE协议栈中,开发者接触最多的就是ATT。BLE引入了attribute概念,用来描述一条一条的数据。Attribute除了定义数据,同时定义该数据可使用的ATT命令,所以这一层被称为ATT层。

  • GATT(Generic attribute profile )。GATT用来规范attribute中的数据内容,并运用group(分组)的概念对attribute进行分类管理。没有GATT,BLE协议栈也能跑,但互联互通就会出问题,也正是由于有了GATT和各类各样的应用profile,BLE摆脱了ZigBee等无线协议的兼容性困境,成了出货量最大的2.4G无线通讯产品。


BLE蓝牙模块主要应用领域


一、移动扩展设备

二、汽车电子设备

三、健康医疗用品:心跳带、血压计等

四、定位应用:室内定位、井下定位等

五、近距离数据采集:无线抄表、无线遥测等

六、数据传输:智能家居室内控制、蓝牙调光、打印机


BLE协议栈详解


协议概述

所谓协议,即将指定的字节按照必定的顺序排列起来,以便他人使用本身的设备时,能经过该协议同其余设备进行通讯。协议一特色,就是有固定的帧格式,经过该格式发送,接收者经过解读帧格式,进而获得新息内容;


BLE链接过程

通常通讯协议,一类通讯是直接发生数据,当设备接送到数据时,直接对数据进行解析,当接受到的数据合法时,即为有效数据,该类型的通讯协议,主要用在有线通讯协议中,好比Modbus,Can一般采用的即为该类型的通讯方式。
另外一类通讯协议,则须要新创建链接,当双方链接创建成功了方可通讯,例如TCP、BLE;BLE协议在须要进行通讯时,即须要向外发送广播信号,告诉接收者,即将和它进行通讯,接受者接收到广播内容后,确认是与本身通讯,因而向广播者发送一响应信息,这样当广播者和接受者都有了对方的身份信息时,即表示双方链接成功。
所以,在链接过程当中,一定有相应的广播帧格式。在BLE通讯过程当中,假设设备A须要连其余设备假设为B,则A须要不断地发送广播信号(此过程通常有一个时间间隔,在没发送广播数据时间内,芯片处于低功耗状态),每发送一次广播包,称之为一次广播事件。


广播帧格式

前导
是一个8比特的交替序列
接入地址的第一个比特为0:01010101
接入地址的第一个比特为1:10101010
接入地址:广播帧为固定地址:0x8E89BED6(低字节在前)
广播报文的报头
包含4bit广播报文类型、2bit保留位、1bit发送地址类型和1bit接收地址类型。
广播报文类型:

发送地址类型
    0: 公共地址
    1:随机地址
长度:广播报文的长度域包含8个比特,有效值的范围是6~37
数据 广播者地址(6个字节)+广播数据(31个字节)
校验 3个字节,为CRC校验。
广播数据 分为有效数据和无效数据


有效数据部分
包含N个AD Structure,每一个AD Structure由Length,AD Type和AD Data组成。其中:
Length AD Type和AD Data的长度。
AD Type 指示AD Data数据的含义。详见https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/


BLE链接创建过程


1. BLE广播与扫描

      设备B不断发送广播信号给手机(Observer),若是手机不开启扫描窗口,手机是收不到设备B的广播的,以下图所示,不只手机要开启射频接收窗口,并且只有手机的射频接收窗口跟广播发送的发射窗口匹配成功,并且广播射频通道和手机扫描射频通道是同一个通道,手机才能收到设备B的广播信号。也就是说,若是设备B在37通道发送广播包,而手机在扫描38通道,那么即便他们俩的射频窗口匹配,二者也是没法进行通讯的。因为这种匹配成功是一个几率事件,所以手机扫到设备B也是一个几率事件,也就是说,手机有时会很快扫到设备B,好比只须要一个广播事件,手机有时又会很慢才能扫到设备B,好比须要10个广播事件甚至更多。

2. 创建链接(connection establishment)

     根据蓝牙spec规定,advertiser发送完一个广播包以后150us(T_IFS),advertiser必须开启一段时间的射频Rx窗口,以接收来自observer的数据包。Observer就能够在这段时间里给advertiser发送链接请求。以下图所示,手机在第三个广播事件的时候扫到了设备B,并发出了链接请求CONN_REQ(CONN_REQ又称为CONNECT_IND)




注:图中M表明手机,S表明设备B,M->S表示手机将数据包发给设备B,即手机开启Tx窗口,设备B开启Rx窗口;S->M正好相反,表示设备B将数据包发给手机,即设备B开启Tx窗口,手机开启Rx窗口。

如图所示,手机在收到A1广播包ADV_IND后,以此为初始锚点(这个锚点不是链接的锚点),T_IFS时间后给Advertiser发送一个connection request命令,即A2数据包,告诉advertiser我将要过来连你,请作好准备。Advertiser根据connect_req命令信息作好接收准备,connect_req包含以下关键信息:

  • Transmit window offset,定义如上图示

  • Transmit window size,定义如上图所示

  • connect_req数据包完整定义以下所示  


     connect_req实际上是在告诉advertiser,手机将在Transmit Window期间发送第一个同步包(P1)给你,请在这段时间里把你的射频接收窗口打开。设备B收到P1后,T_IFS时间后将给手机回复数据包P2(ACK包)。一旦手机收到数据包P2,链接便可认为创建成功。固然,实际状况会比较复杂,手机有可能收不到P2,这个时候手机将持续发送同步包直到超时时间(supervision timeout)到,在此期间只要设备B回过一次ACK包,链接即算成功。因此一旦P1包发出,主机(手机)即认为链接成功,而无论有没有收到设备的ACK包。这也是为何在Android或者iOS系统中,应用常常收到链接成功的回调事件( 该回调事件就是基于 P1包有没有发出,只要P1包发出,手机即认为链接成功,而无论有没有收到设备的ACK包 ),但实际上手机和设备并无成功创建链接。后续手机将以 P1为锚点(原点),Connection Interval为周期,周期性地 给设备B发送数据包(Packet),Packet除了充当数据传送功能,它还有以下两个很是重要的功能:
  1. 同步手机和设备的时钟,也就是说,设备每收到手机发来的一个包,都会把本身的时序原点从新设置,以跟手机同步。

  2. 告诉设备你如今能够传数据给我了。链接成功后,BLE通讯将变成主从模式,所以把链接发起者(手机)称为Master或者Central,把被链接者(以前的Advertiser)称为Slave或者Peripheral。BLE通讯之因此为主从模式,是由于Slave不能“随性”给Master发信息,它只有等到Master给它发了一个packet后,而后才能在规定的时间把本身的数据回传给Master。

3. 链接失败

有以下几种典型的链接失败状况:


  1. 如步骤2图所示,若是slave在transmit window期间没有收到master发过来的P1,那么链接将会失败。此时应该排查master那边的问题,看看master为何没有在约定的时间把P1发出来。

  2. 若是master在transmit window期间把P1发出来了,也就是说master按照connect_req约定的时序把P1发出来了,但slave没有把P2回过去或者没有在超时时间内把P2回过去,那么链接也会失败。此时应该排查slave这边的问题,看一看slave为何没有把P2回过去

  3. 若是master把P1发出来了,slave也把P2回过去了,此时主机或者从机仍是报链接失败,这种状况有多是软件有问题,须要仔细排查master或者slave的软件。

  4. 还有一种比较常见的链接失败状况:空中射频干扰太大。此时应该找一个干净的环境,好比屏蔽室,排除干扰后再去测试链接是否正常。


数据帧格式


链接成功后,双方将能够互相发送数据,那么将涉及到其数据帧格式:

字段释义:
LLID:表示此包数据是 LL Date PDU 仍是 LL Control PDU
    00b: Reserved
    01b: LL Date PDU:Continuation fragment of L2CAP             message, or an Empty PDU.
    10b: LL Date PDU:Start of an L2CAP message or a             complete L2CAP message with no fragmentation.
    11b: LL Control PDU

MIC( Message Integrity Check):信息完整性检测。涉及到加密操做,上图中是用虚线表示的,并非必定要有此项。
MD:这个标志位是用来通知对方设备本身还有其余数据准备发送。0 表示没有更多数据发送, 1 表示有更多数据准备发送。这样,只要还有数据须要发送,链接事件会自动扩展。一旦再也不有数据发送,链接事件当即关闭。


Note:如何区分是肯定包、新包仍是重发包?
SN:只有一个 bit 位,因此值是在 0 和 1 之间进行切换。若是序列号与以前的同样,则为重传报文,若是序列号和之间的不一样,则为新报文。
NESN:预期序列号,它是接收方但愿接到的下一包的序列号,也就是数据包的确认标志。当设备接收到序列(SN)为 0 的报文后,在发送给对方的数据包中,应将 NESN 设为 1,这样对方接收到这个包后,会发送一个新的数据包过来,不然就会重发上一次序列号为 0 的包。这个标志能够用来判断数据包是否被正确接收仍是须要重传。


代码示例

   

    本例以OSAL下BLE代码为例作讲解。

什么是OSAL? 

    OSAL为:Operating System Abstraction Layer,即操做系统抽象层,支持多任务运行,它并非一个传统意义上的操做系统,可是实现了部分相似操做系统的功能。

      OSAL概念是由TI公司在ZIGBEE协议栈引入,他的意思是”模拟操做系统”,此OS,并不是一个真正的OS,而是模拟OS的一些方法为广大编程者提供一种写MCU程序的方法。当有一个事件发生的时候,OSAL负责将此事件分配给可以处理此事件的任务,而后此任务判断事件的类型,调用相应的事件处理程序进行处理。

   

实验平台

    一、蓝牙协议栈:1.3.2

    二、软件平台:IAR For 8051 8.10.3

    三、硬件平台:Smart RF开发板(从机),Android_Lightblue(主机)


代码解析

一、int main(void)

int main(void)

{

  /* Initialize hardware */

  HAL_BOARD_INIT();//初始化时钟和使能缓存预取模式

 

  // Initialize board I/O

  InitBoard( OB_COLD );//冷启动,关闭了led灯与中断,避免接下来的各类初始化受干扰

 

  /* Initialze the HAL driver */

  HalDriverInit();//各类驱动的初始化、如按键、lcd、adc、usb、uart等8

 

  /* Initialize NV system */

  osal_snv_init();//snv 内部用于保存配对数据或你的用户自定义数据的一段flash,4kB空间

 

  /* Initialize LL */  

 

  /* Initialize the operating system */

  osal_init_system();//oasl 操做系统初始化, 包含内存分配、消息队列、定时器、电源管理和任务等

 

  /* Enable interrupts */

  HAL_ENABLE_INTERRUPTS();// 开启全局中断

  // Final board initialization

  InitBoard( OB_READY );  //设置标志标示系统初始化完毕

}

二、osal_init_system()

uint8 osal_init_system( void )

{

  // Initialize the Memory Allocation System

  osal_mem_init();//初始化内存分配系统

 

  // Initialize the message queue

  osal_qHead = NULL;//初始化消息队列

 

  // Initialize the timers

  osalTimerInit();//初始化定时器

 

  // Initialize the Power Management System

  osal_pwrmgr_init();//初始化电源管理系统

 

  // Initialize the system tasks.

  osalInitTasks();//初始化系统任务, 这一个任务初始花很是关键

 

  // Setup efficient search for the first free block of heap.

  osal_mem_kick();

 

  return ( SUCCESS );

}

三、osalInitTasks()

void osalInitTasks( void )

{

  /* L2CAP Task */

  L2CAP_Init( taskID++ );

 

  /* GAP Task */

  GAP_Init( taskID++ );

 

  /* GATT Task */

  GATT_Init( taskID++ );

 

  /* SM Task */

  SM_Init( taskID++ );

 

  /* Profiles */

  GAPRole_Init( taskID++ );    //链路角色初始化

  GAPBondMgr_Init( taskID++ );  //链路绑定初始化

 

  GATTServApp_Init( taskID++ );

 

  /* Application */

  SimpleBLEPeripheral_Init( taskID );

}

四、GAPRole_Init( taskID++ )

void GAPRole_Init( uint8 task_id )

{

  gapRole_TaskID = task_id;  //定义任务地址

 

  gapRole_state = GAPROLE_INIT;  //链路状态设置为GAPROLE_INIT

  gapRole_ConnectionHandle = INVALID_CONNHANDLE;  //设置链路链接句柄为0xFFFF

 

  GAP_RegisterForHCIMsgs( gapRole_TaskID );//注册控制接口的任务ID

 

  // Initialize the Profile Advertising and Connection Parameters

  gapRole_profileRole = GAP_PROFILE_PERIPHERAL;  //链路配置角色为从机

  VOID osal_memset( gapRole_IRK, 0, KEYLEN );    //密钥缓冲器清零

  VOID osal_memset( gapRole_SRK, 0, KEYLEN );

  gapRole_signCounter = 0;                       //密钥计数标志位清零

  gapRole_AdvEventType = GAP_ADTYPE_ADV_IND; //广播类型为可链接无定向广播

  gapRole_AdvDirectType = ADDRTYPE_PUBLIC; //广播方式为经过广播(可被发现扫描链接)

  gapRole_AdvChanMap = GAP_ADVCHAN_ALL ;  //广播全部通道3七、3八、39

  gapRole_AdvFilterPolicy = GAP_FILTER_POLICY_ALL;  //容许扫描,容许链接

 

  // Restore Items from NV

  VOID osal_snv_read( BLE_NVID_IRK, KEYLEN, gapRole_IRK ); //读出存储的密钥和密钥计数标志位

  VOID osal_snv_read( BLE_NVID_CSRK, KEYLEN, gapRole_SRK );

  VOID osal_snv_read( BLE_NVID_SIGNCOUNTER, sizeof( uint32 ), &gapRole_signCounter );

}

//初始化完成后

五、

void SimpleBLEPeripheral_Init( uint8 task_id )

{

osal_set_event( simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT );              //启动设备开始事件

}

六、

uint16 SimpleBLEPeripheral_ProcessEvent( uint8 task_id, uint16 events )

{     

  if ( events & SBP_START_DEVICE_EVT )// 初始化后就执行这个啦

      {              

            // Start the Device

            VOID GAPRole_StartDevice( &simpleBLEPeripheral_PeripheralCBs ); //配置链路事件通知回调函数

        

            // Start Bond Manager

            VOID GAPBondMgr_Register( &simpleBLEPeripheral_BondMgrCBs ); //配置配对消息回调函数

            

            // Set timer for first periodic event

            osal_start_timerEx( simpleBLEPeripheral_TaskID, POWER_DETECT_EVT, DetectPowerPeriod );   

            

            return ( events ^ SBP_START_DEVICE_EVT );

      }

}

七、

Status_t GAPRole_StartDevice( gapRolesCBs_t *pAppCallbacks )

{

  if ( gapRole_state == GAPROLE_INIT ) //若是链路状态是初始化状态

  {

    // Clear all of the Application callbacks

    if ( pAppCallbacks )

    {

      pGapRoles_AppCGs = pAppCallbacks;//设置回调函数

    }

    // Start the GAP

    gapRole_SetupGAP();//开始创建链路

    return ( SUCCESS );

  }

  else     //不然返回已经在请求模式状态

  {

    return ( bleAlreadyInRequestedMode );

  }

}

八、

static void gapRole_SetupGAP( void )

{

  VOID GAP_DeviceInit( gapRole_TaskID,

          gapRole_profileRole, 0,

          gapRole_IRK, gapRole_SRK,

          &gapRole_signCounter );

}

九、

bStatus_t GAP_DeviceInit(  uint8 taskID,
                           uint8 profileRole,
                           uint8 maxScanResponses,
                           uint8 *pIRK,
                           uint8 *pSRK,
                           uint32 *pSignCounter )

{

    // Setup the device configuration parameters

stat = GAP_ParamsInit( taskID, profileRole );  //设置设备配置参数

 

    #if ( HOST_CONFIG & ( CENTRAL_CFG | PERIPHERAL_CFG ) )

    {

      GAP_SecParamsInit( pIRK, pSRK, pSignCounter );

    }

#endif

 

#if ( HOST_CONFIG & ( PERIPHERAL_CFG | BROADCASTER_CFG ) )

{

   // Initialize GAP Peripheral Device Manager

   VOID GAP_PeriDevMgrInit();  //初始化从机设备管理

   #if ( HOST_CONFIG & PERIPHERAL_CFG )

   {

     // Initialize SM Responder

     VOID SM_ResponderInit();  //回应者初始化

   }

   #endif

 }

 #endif

}

十、当GAP_DeviceInit初始化完成后,将产生GAP_DEVICE_INIT_DONE_EVENT事件;

uint16 GAPRole_ProcessEvent( uint8 task_id, uint16 events )  //链路处理事件

十一、static void gapRole_ProcessOSALMsg( osal_event_hdr_t *pMsg ) //链路系统消息事件

十二、

static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //链路处理链接消息

{

  uint8 notify = FALSE;   // State changed notify the app? (default no)

  switch ( pMsg->opcode )

  {

      case GAP_DEVICE_INIT_DONE_EVENT: //当GAP_DeviceInit初始化完成后,将产生此事件

      {

        gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;

        bStatus_t stat = pPkt->hdr.status;

 

        if ( stat == SUCCESS )

        {

          // Save off the generated keys

          VOID osal_snv_write( BLE_NVID_IRK, KEYLEN, gapRole_IRK );//保存生成的密钥

          VOID osal_snv_write( BLE_NVID_CSRK, KEYLEN, gapRole_SRK );

 

          // Save off the information

          VOID osal_memcpy( gapRole_bdAddr, pPkt->devAddr, B_ADDR_LEN );//保存设备地址

 

          gapRole_state = GAPROLE_STARTED;  //链路开始

 

          // Update the advertising data

          stat = GAP_UpdateAdvertisingData( gapRole_TaskID,//更新广播数据

                              TRUE, gapRole_AdvertDataLen, gapRole_AdvertData );

        }

            notify = TRUE;  //通知回调函数链路的状态

      }

      break;

      if ( notify == TRUE )

      {

          // Notify the application with the new state change

          if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange ) //判断是否设置了回调函数

          {

               pGapRoles_AppCGs->pfnStateChange( gapRole_state );//调用设置的回调函数,通知gapRole_state当前状态

          }

      }

}

1三、stat=GAP_UpdateAdvertisingData( gapRole_TaskID,TRUE, gapRole_AdvertDataLen, gapRole_AdvertData );//更新广播数据后,将产生GAP_ADV_DATA_UPDATE_DONE_EVENT事件;

1四、

static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //链路处理链接消息

{

  uint8 notify = FALSE;   // State changed notify the app? (default no)

  switch ( pMsg->opcode )

  {

      case GAP_ADV_DATA_UPDATE_DONE_EVENT:

      {

        gapAdvDataUpdateEvent_t *pPkt = (gapAdvDataUpdateEvent_t *)pMsg;

 

        if ( pPkt->hdr.status == SUCCESS )

        {

          if ( pPkt->adType )

          {

            // Setup the Response Data

            pPkt->hdr.status = GAP_UpdateAdvertisingData( gapRole_TaskID,

                              FALSE, gapRole_ScanRspDataLen, gapRole_ScanRspData );//更新扫描回应数据

          }

          else

          {

            // Start advertising

            VOID osal_set_event( gapRole_TaskID, START_ADVERTISING_EVT );  //启动广播事件

          }

        }

        if ( pPkt->hdr.status != SUCCESS ) //若是不成功将通知回调函数,不然不通知

        {

          // Set into Error state

          gapRole_state = GAPROLE_ERROR;

          notify = TRUE;

        }

      }

      break;

1五、

static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //链路处理链接消息

{

  uint8 notify = FALSE;   // State changed notify the app? (default no)

  switch ( pMsg->opcode )

  {

      case GAP_ADV_DATA_UPDATE_DONE_EVENT:

      {

        gapAdvDataUpdateEvent_t *pPkt = (gapAdvDataUpdateEvent_t *)pMsg;

 

        if ( pPkt->hdr.status == SUCCESS )

        {

          if ( pPkt->adType )

          {

            // Setup the Response Data

            pPkt->hdr.status = GAP_UpdateAdvertisingData( gapRole_TaskID,

                              FALSE, gapRole_ScanRspDataLen, gapRole_ScanRspData );//更新扫描回应数据

          }

          else

          {

            // Start advertising

            VOID osal_set_event( gapRole_TaskID, START_ADVERTISING_EVT );  //启动广播事件

          }

        }

        if ( pPkt->hdr.status != SUCCESS ) //若是不成功将通知回调函数,不然不通知

        {

          // Set into Error state

          gapRole_state = GAPROLE_ERROR;

          notify = TRUE;

        }

      }

      break;

1六、执行广播事件

uint16 GAPRole_ProcessEvent( uint8 task_id, uint16 events )

{

  VOID task_id; // OSAL required parameter that isn't used in this function

  if ( events & START_ADVERTISING_EVT )

  {

    if ( gapRole_AdvEnabled )

    {

      gapAdvertisingParams_t params;

 

      // Setup advertisement parameters

      params.eventType = gapRole_AdvEventType; //GAP_ADTYPE_ADV_IND; 广播类型为可链接无定向广播

      params.initiatorAddrType = gapRole_AdvDirectType; //ADDRTYPE_PUBLIC; 广播方式为通用广播

      VOID osal_memcpy( params.initiatorAddr, gapRole_AdvDirectAddr, B_ADDR_LEN ); //发起者地址配置

      params.channelMap = gapRole_AdvChanMap;  //广播通道配置:广播全部通道3七、3八、39

      params.filterPolicy = gapRole_AdvFilterPolicy;//过滤策略GAP_FILTER_POLICY_ALL;容许扫描,容许链接

 

      if ( GAP_MakeDiscoverable( gapRole_TaskID, ¶ms ) != SUCCESS ) //配置广播参数,并产生一个GAP_MakeDiscoverable      消息事件

      {

        gapRole_state = GAPROLE_ERROR;//若是不成功将通知回调函数-链路错误

        

        // Notify the application with the new state change

        if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange )

        {

          pGapRoles_AppCGs->pfnStateChange( gapRole_state );

        }

      }

    }

    return ( events ^ START_ADVERTISING_EVT );

  }

  1七、处理GAP_MakeDiscoverable消息事件

 static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //链路处理链接消息

{

  uint8 notify = FALSE;   // State changed notify the app? (default no)

  switch ( pMsg->opcode )

  {

      case GAP_MAKE_DISCOVERABLE_DONE_EVENT:  //使能可被发现完成事件即开始广播了

      case GAP_END_DISCOVERABLE_DONE_EVENT:   //结束可被发现完成事件即中止广播了

      {

        gapMakeDiscoverableRspEvent_t *pPkt = (gapMakeDiscoverableRspEvent_t *)pMsg;

        if ( pPkt->hdr.status == SUCCESS )

        {

          if ( pMsg->opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT )

          {

            gapRole_state = GAPROLE_ADVERTISING;  //设置当前链路状态

          }

          else // GAP_END_DISCOVERABLE_DONE_EVENT//结束可被发现完成事件即中止广播了

          {

            if ( gapRole_AdvertOffTime != 0 )  //若是gapRole_AdvertOffTime等于0,将再也不广播,不然启动定时广播件

            {

              if ( ( gapRole_AdvEnabled ) )//若是使能广播

              {

            VOID osal_start_timerEx( gapRole_TaskID, START_ADVERTISING_EVT, gapRole_AdvertOffTime );//启动周期广播事件

              }

            }

            else

            {

              // Since gapRole_AdvertOffTime is set to 0, the device should not

              // automatically become discoverable again after a period of time.

              // Set enabler to FALSE; device will become discoverable again when

              // this value gets set to TRUE

              gapRole_AdvEnabled = FALSE;

            }

            // In the Advertising Off period

            gapRole_state = GAPROLE_WAITING;//若是GAP_END_DISCOVERABLE_DONE_EVENT,链路当前状态为等待状态

          }

        }

        else

        {

          gapRole_state = GAPROLE_ERROR;

        }

        notify = TRUE;//通知回调函数

      }

      break;

  if ( notify == TRUE )

  {

      // Notify the application with the new state change

      if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange ) //判断是否设置了回调函数

      {

           pGapRoles_AppCGs->pfnStateChange( gapRole_state );//调用设置的回调函数,通知gapRole_state当前状态

      }

  }

1八、这时候底层已经使能硬件在广播了,要么广播超时产生一个GAP_END_DISCOVERABLE_DONE_EVENT消息,要么被链接事件 GAP_LINK_ESTABLISHED_EVENT;

1九、广播超时产生一个GAP_END_DISCOVERABLE_DONE_EVENT消息

static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //链路处理链接消息

{

  uint8 notify = FALSE;   // State changed notify the app? (default no)

  switch ( pMsg->opcode )

  {  

  case GAP_MAKE_DISCOVERABLE_DONE_EVENT:  //使能可被发现完成事件即开始广播了

      case GAP_END_DISCOVERABLE_DONE_EVENT:   //结束可被发现完成事件即中止广播了

      {

        gapMakeDiscoverableRspEvent_t *pPkt = (gapMakeDiscoverableRspEvent_t *)pMsg;

 

        if ( pPkt->hdr.status == SUCCESS )

        {

          if ( pMsg->opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT )

          {

            gapRole_state = GAPROLE_ADVERTISING;  //设置当前链路状态

          }

          else // GAP_END_DISCOVERABLE_DONE_EVENT//结束可被发现完成事件即中止广播了

          {

            if ( gapRole_AdvertOffTime != 0 )  //若是gapRole_AdvertOffTime不等于0,启动定时广播事件,不然将关闭广播

            {

              if ( ( gapRole_AdvEnabled ) )//若是使能广播

              {

            VOID osal_start_timerEx( gapRole_TaskID, START_ADVERTISING_EVT, gapRole_AdvertOffTime );//启动周期广播事件

              }

            }

            else

            {

              // Since gapRole_AdvertOffTime is set to 0, the device should not

              // automatically become discoverable again after a period of time.

              // Set enabler to FALSE; device will become discoverable again when

              // this value gets set to TRUE

              gapRole_AdvEnabled = FALSE; //关闭广播

            }

            // In the Advertising Off period

             gapRole_state = GAPROLE_WAITING;//若是GAP_END_DISCOVERABLE_DONE_EVENT,链路当前状态为等待状态,或再也不广播或等待周期广播          

            }

        }

        else

        {

          gapRole_state = GAPROLE_ERROR;

        }

        notify = TRUE;//通知回调函数

      }

      break;

  if ( notify == TRUE )

  {

      // Notify the application with the new state change

      if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange ) //判断是否设置了回调函数

      {

           pGapRoles_AppCGs->pfnStateChange( gapRole_state );//调用设置的回调函数,通知gapRole_state当前状态

      }

  }

20、广播时产生一个GAP_LINK_ESTABLISHED_EVENT消息


static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg )  //链路处理链接消息

{

  uint8 notify = FALSE;   // State changed notify the app? (default no)

  switch ( pMsg->opcode )

  {  

    case GAP_LINK_ESTABLISHED_EVENT:

      {

        gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *)pMsg;

        if ( pPkt->hdr.status == SUCCESS )

        {

          VOID osal_memcpy( gapRole_ConnectedDevAddr, pPkt->devAddr, B_ADDR_LEN );//保存主机的地址

          gapRole_ConnectionHandle = pPkt->connectionHandle; //保存主机链接句柄

          gapRole_state = GAPROLE_CONNECTED;  //通知链路状态:链接成功

              notify = TRUE;

        }

      }

 }

本文分享自微信公众号 - 嵌入式云IOT技术圈(gh_d6ff851b4069)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索