Realtek:ble profile架构概览

Realtek:ble profile架构概览

1 bluetooth 蓝牙协议

什么是蓝牙呢?蓝牙是频率为2.4GHz的特高频无线通讯标准;按协议制定的时间将其分为两种类型;

1.1 经典蓝牙BT:以点对点方式创建一对一通信;使用蓝牙3.0标准协议;

1.2 低功耗蓝牙BLE :以广播(一对多)和网格(多对多)等通信; 使用蓝牙4.0标准协议,兼容3.0;

2 ( C/S client server )架构

c/s架构通信的双方总是由client客户端的主机发起,server服务端的从机响应;gatt协议使用c/s架构;

2.1 client客户端: 使用数据服务的设备,即主机设备;

2.2 server服务端:提供数据服务的设备,即从机设备; 数据服务:即service服务;

2.3 gatt协议传输

attribute指令在发送出去之后,会一直等待直到收到回复的ack包,直到超时或断开ble;杜绝了空中丢包的可能性;

attribute指令可以选择request/ack的模式,也可以选择request/respond的模式;

3 蓝牙协议栈架构

蓝牙协议栈主要分为三层;

其中GAP协议后文介绍了其广播包部分,其中GATT协议后文介绍了其attribute table部分;application层等价于profile协议部分;

3.1 controller层

由芯片厂编写的硬件层,包括射频电路,将HCI数据按空中包的格式调制解调的链路层,以及规范通信的hci接口;

3.2 host层

由各公司配置的驱动层,用于连接硬件与应用层的通信,需要配置的主要是配置gatt和gap;

3.2.1 GAP generic access profile 通用连接协议

当从机还未建立连接的时候,通过GAP协议单向向外广播数据,该协议通常由芯片产商写好;

从机广播间隔约20ms-10s,主机扫描间隔约2.5ms-10s;广播间隔越长越省电,同时也不容易被扫描到;

3.2.2 GATT generic attribute profile 通用属性协议

当从机和主机建立一对一连接,通过GATT协议进行通信,该协议结构即后文的attribute profile;

GATT连接属于一对一连接,从机和主机建立连接之后,就会停止向外广播使得对其他设备不可见;当设备断开后设备又开始广播;

3.3 application层

由各公司编写的的应用层,每个代码功能都可视为profile;蓝牙联盟也定义了相关功能的标准profiles,看情况使用;

4 广播数据

4.1 广播包 adv_data[ ]

广播包的主要作用是用来广播蓝牙设备名称,以及广播部分需要广播的类型数据;

广播包的数据单元可以切分为[ length、type、data ]的格式进行广播,广播包的数据长度最大31bytes;

4.1.1 type类型

//gap_le_types.h 广播包type类型

#define GAP_ADTYPE_FLAGS 0x01 //!< The Flags data type contains one bit Boolean flags. Please reference @ref ADV_TYPE_FLAGS for details.

#define GAP_ADTYPE_16BIT_MORE 0x02 //!< Service: More 16-bit UUIDs available

#define GAP_ADTYPE_16BIT_COMPLETE 0x03 //!< Service: Complete list of 16-bit UUIDs

#define GAP_ADTYPE_32BIT_MORE 0x04 //!< Service: More 32-bit UUIDs available

#define GAP_ADTYPE_32BIT_COMPLETE 0x05 //!< Service: Complete list of 32-bit UUIDs

#define GAP_ADTYPE_128BIT_MORE 0x06 //!< Service: More 128-bit UUIDs available

#define GAP_ADTYPE_128BIT_COMPLETE 0x07 //!< Service: Complete list of 128-bit UUIDs

#define GAP_ADTYPE_LOCAL_NAME_SHORT 0x08 //!< Shortened local name

#define GAP_ADTYPE_LOCAL_NAME_COMPLETE 0x09 //!< Complete local name

#define GAP_ADTYPE_POWER_LEVEL 0x0A //!< TX Power Level: 0xXX: -127 to +127 dBm

#define GAP_ADTYPE_OOB_CLASS_OF_DEVICE 0x0D //!< Simple Pairing OOB Tag: Class of device (3 octets)

#define GAP_ADTYPE_OOB_SIMPLE_PAIRING_HASHC 0x0E //!< Simple Pairing OOB Tag: Simple Pairing Hash C (16 octets)

#define GAP_ADTYPE_OOB_SIMPLE_PAIRING_RANDR 0x0F //!< Simple Pairing OOB Tag: Simple Pairing Randomizer R (16 octets)

#define GAP_ADTYPE_SM_TK 0x10 //!< Security Manager TK Value

#define GAP_ADTYPE_SM_OOB_FLAG 0x11 //!< Secutiry Manager OOB Flags

#define GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE 0x12 //!< Min and Max values of the connection interval (2 octets Min, 2 octets Max)

// (0xFFFF indicates no conn interval min or max)

#define GAP_ADTYPE_SIGNED_DATA 0x13 //!< Signed Data field

#define GAP_ADTYPE_SERVICES_LIST_16BIT 0x14 //!< Service Solicitation: list of 16-bit Service UUIDs

#define GAP_ADTYPE_SERVICES_LIST_128BIT 0x15 //!< Service Solicitation: list of 128-bit Service UUIDs

#define GAP_ADTYPE_SERVICE_DATA 0x16 //!< Service Data

#define GAP_ADTYPE_PUBLIC_TGT_ADDR 0x17 //!< Public Target Address

#define GAP_ADTYPE_RANDOM_TGT_ADDR 0x18 //!< Random Target Address

#define GAP_ADTYPE_APPEARANCE 0x19 //!< Appearance

#define GAP_ADTYPE_ADV_INTERVAL 0x1A //!< Advertising Interval

#define GAP_ADTYPE_LE_BT_ADDR 0x1B //!< LE Bluetooth Device Address

#define GAP_ADTYPE_LE_ROLE 0x1C //!< LE Role

#define GAP_ADTYPE_SP_HASH_C256 0x1D //!< Simple Pairing Hash C-256

#define GAP_ADTYPE_SP_RAND_R256 0x1E //!< Simple Pairing Randomizer R-256

#define GAP_ADTYPE_LIST_32BIT_SILI 0x1F //!< List of 32-bit Service Solicitation UUIDs

#define GAP_ADTYPE_SERVICE_DATA_32BIT 0x20 //!< Service Data - 32-bit UUID

#define GAP_ADTYPE_SERVICE_DATA_128BIT 0x21 //!< Service Data - 128-bit UUID

#define GAP_ADTYPE_SC_CONF_VALUE 0x22 //!< LE Secure Connections Confirmation Value

#define GAP_ADTYPE_SC_RAND_VALUE 0x23 //!< LE Secure Connections Random Value

#define GAP_ADTYPE_URI 0x24 //!< URI

#define GAP_ADTYPE_INDOOR_POSITION 0x25 //!< Indoor Positioning

#define GAP_ADTYPE_TRANSPORT_DISCOVERY_DATA 0x26 //!< Transport Discovery Data

#define GAP_ADTYPE_LE_SUPPORTED_FEATURES 0x27 //!< LE Supported Features

#define GAP_ADTYPE_CHAN_MAP_UPDATE_IND 0x28 //!< Channel Map Update Indication

#define GAP_ADTYPE_MESH_PB_ADV 0x29 //!< Mesh Pb-Adv

#define GAP_ADTYPE_MESH_PACKET 0x2A //!< Mesh Packet

#define GAP_ADTYPE_MESH_BEACON 0x2B //!< Mesh Beacon

#define GAP_ADTYPE_3D_INFO_DATA 0x3D //!< 3D Information Data

#define GAP_ADTYPE_MANUFACTURER_SPECIFIC 0xFF //!< Manufacturer Specific Data: first 2 octets contain the Company Identifier Code

// followed by the additional manufacturer specific data

// type=ADV_TYPE_FLAGS,data如下:

#define GAP_ADTYPE_FLAGS_LIMITED 0x01 //!< Discovery Mode: LE Limited Discoverable Mode

#define GAP_ADTYPE_FLAGS_GENERAL 0x02 //!< Discovery Mode: LE General Discoverable Mode

#define GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED 0x04 //!< Discovery Mode: BR/EDR Not Supported

#define GAP_ADTYPE_FLAGS_SIMULTANEOUS_LE_BREDR_CONTROLLER 0x08 //!< Discovery Mode: Simultaneous LE and BR/EDR Controller Supported

#define GAP_ADTYPE_FLAGS_SIMULTANEOUS_LE_BREDR_HOST 0x10 //!< Discovery Mode: Simultaneous LE and BR/EDR Host Supported

4.1.2 广播包

// GAP Advertisement data (max size = 31 bytes,best kept short to conserve power while advertisting)

SHM_DATA_SECTION uint8_t adv_data[] ={

/* Core spec. Vol. 3, Part C, Chapter 18 */

/* Flags */

/* place holder for Local Name, filled by BT stack. if not present */

/* BT stack appends Local Name. */

0x02, //length

GAP_ADTYPE_FLAGS, //type

GAP_ADTYPE_FLAGS_GENERAL , //data

// 0x03, //length

// GAP_ADTYPE_16BIT_MORE, //type:16bit uuid

// 0x59, //data

// 0xD4,

0x0F //length

0x09, //type:Complete local name

'C', 'M', 'B', '5', '9', '0', '9', '6', '8', '-', 'X', 'X', 'X', 'X',//data

0x08, //length

0xFF, //type:Manufacture specified data

'L','B', //data:company identifier

0x20,0x15,0x09,0x14,0x14, //data:mac addr

};

4.2 广播回复包

//main.c GAP - SCAN RSP data (max size = 31 bytes) 数据格式同广播包一样

SHM_DATA_SECTION static uint8_t scan_rsp_data[] ={

0x0F, //length

0x09, //type

'C', 'M', 'B', '5', '9', '0', '9', '6', '8', '-', 'X', 'X', 'X', 'X',//data

};

4.3 广播包初始化

//main.c

void app_le_gap_init(void){

//... 广播包adv_data[] 和广播回复包scan_rsp_data[]的初始化

le_adv_set_param(GAP_PARAM_ADV_DATA, sizeof(adv_data), (void *)adv_data);

le_adv_set_param(GAP_PARAM_SCAN_RSP_DATA, sizeof(scan_rsp_data), (void *)scan_rsp_data);

}

//main.c

static void app_bt_gap_init(void){

//...

app_le_gap_init();

}

//src\app\watch\watch_app\main.c

int main(void){

//...

app_bt_gap_init();

}

5 ATT attribute 协议

5.1 attribute 数据段

attribute数据段格式如下,是后续提供给gatt使用的服务数据源;gatt将其归纳为gatt的格式同应用层交互;

attribute handle:由蓝牙底层生成,是ATT PDU(protocol data unit)的句柄,不是代码中的attrib_index;

属性句柄虽然也起到标识和管理作用,但不是通道索引序号,这属性句柄体现在什么代码中呢?先放着;

5.2 attribute 结构体

5.2.1 attribute 结构体

//gatt.h

//brief GATT attribute definition. profile中通过 type_value 来区分通道的,具体逻辑边界先放着;

typedef struct

{

uint16_t flags; // flags

uint8_t type_value[2 + 14]; // UUID charecteristic 特征值属性

uint16_t value_len; // Length of value

void *p_value_context; // service data addr

uint32_t permissions; // 属性权限,可以组合属性;

} T_ATTRIB_APPL;

5.2.2 flags 属性

// flags:标识service的其他属性,比如UUID长度、支持广播、CCCD使能、指示等;不同蓝牙厂商的功能应该会有所不同;

#define ATTRIB_FLAG_VOID 0x0000 /**< Attribute value neither supplied by application

nor included following 16bit UUID. Attribute value is pointed by p_value_context

and value_len shall be set to the length of attribute value. */

#define ATTRIB_FLAG_UUID_128BIT 0x0001 /**< Attribute uses 128 bit UUID */

#define ATTRIB_FLAG_VALUE_INCL 0x0002 /**< Attribute value is included following 16 bit UUID */

#define ATTRIB_FLAG_VALUE_APPL 0x0004 /**< Application has to supply write value */

#define ATTRIB_FLAG_ASCII_Z 0x0008 /**< Attribute value is ASCII_Z string */

#define ATTRIB_FLAG_CCCD_APPL 0x0010 /**< Application will be informed about CCCD value is changed */

#define ATTRIB_FLAG_CCCD_NO_FILTER 0x0020 /**< Application will be informed about CCCD value

when CCCD is write by client, no matter it is changed or not */

#define ATTRIB_FLAG_INCLUDE_MULTI 0x0040

#define ATTRIB_FLAG_LE 0x0800 /**< Used only for primary service declaration attributes if GATT over BLE is supported */

#define ATTRIB_FLAG_BREDR 0x0400 /**< Used only for primary service declaration attributes if GATT over BR/EDR is supported */

5.2.3 permissions 属性

//permissions:标识service的访问属性,比如读写,加密读写、认证读写、签名读写

#define GATT_PERM_NONE 0x00

#define GATT_PERM_ALL 0x01 /**< bits 0..1 (rd), 4..5 (wr), 8..9 (notif/ind) */

#define GATT_PERM_AUTHEN_REQ 0x02

#define GATT_PERM_AUTHEN_MITM_REQ 0x03

#define GATT_PERM_AUTHOR_REQ 0x04 /**< bits 2 (rd), 6 (wr), 10 (notif/ind) */

#define GATT_PERM_ENCRYPTED_REQ 0x08 /**< bits 3 (rd), 7 (wr), 11 (notif/ind) */

#define GATT_PERM_AUTHEN_SC_REQ 0x00010000

#define GATT_PERM_READ GATT_PERM_ALL

#define GATT_PERM_READ_AUTHEN_GET(x) (x & 0x03)

#define GATT_PERM_WRITE (GATT_PERM_ALL << 4)

#define GATT_PERM_WRITE_AUTHEN_GET(x) ((x >> 4) & 0x03)

#define GATT_PERM_NOTIF_IND (GATT_PERM_ALL << 8)

#define GATT_PERM_NOTIF_IND_AUTHEN_GET(x) ((x >> 8) & 0x03)

//...

//flags和permissions具有关联性,没有因果性;比如ATTRIB_FLAG_VALUE_APPL flag会有notify permission;

//上面这句话真是总结的太准确了,我真是小聪明;

5.2.4 characteristic属性

//这里还有一个相关概念老是在网文中出现的,那就是characteristic特征值属性;

//是用于描述characteristic的访问权限;说句大白话就是用于描述特征值的permissions的;为什么要费这劲再定义呢?先放着;

//write属性: 该从机可以被写入 / client>>server执行写操作

//read属性: 该从机可以被读取 / client>>server执行读操作

//notify属性: 该从机可以主动发送通知消息; / server>>client发送通知

//authenticated属性: 该从机需要验证才能读写

//虽然不是蓝牙规范的标准用法,但是notify通知也可以用来实现service指令传输;

5.2.5 uuid 通用唯一标识码

UUID: universally unique identifier 通用唯一标识码,通过不同的uuid来区分不同的service服务;

蓝牙技术联盟定义基本的UUID标识码如右:0000xxxx-0000-1000-8000-00805F9B34FB;

蓝牙技术联盟定义基本的UUID标识码中xxxx为属性的uuid;使用uuid标识service的characteristic属性;

蓝牙技术联盟定义基本的UUID标识码只是官方提供的,实际上多数公司都会重新定义自己的UUID特征值通道;

蓝牙广播中的uuid格式:xxxx,0000xxxx,0000xxxx-0000-1000-8000-00805F9B34FB;2bytes、4bytes、16bytes;

5.3 attribute table

//wristband_private_service.c

const T_ATTRIB_APPL bwps_service_tbl[] ={

{ // attrib_index 0

(ATTRIB_FLAG_VOID | ATTRIB_FLAG_LE), //flag:0x0800, 仅用于primary service;

{ LO_WORD(GATT_UUID_PRIMARY_SERVICE), //uuid:0x2800

HI_WORD(GATT_UUID_PRIMARY_SERVICE),

},

UUID_16BIT_SIZE, //valueLength

(void *)GATT_UUID16_BWPS, //pvalue:&[0x59,0xd4]

//这个是service的uuid,后面的几个都是这个service的characteristic;

//添加service就是重新添加一个当前attrib_index和后面几个特征值的attrib_index;

GATT_PERM_READ //permission

},

{ // attrib_index 1

ATTRIB_FLAG_VALUE_INCL, //flag:0x0002, 使用16bit uuid

{ LO_WORD(GATT_UUID_CHARACTERISTIC), //uuid:0x2803

HI_WORD(GATT_UUID_CHARACTERISTIC),

GATT_CHAR_PROP_WRITE|GATT_CHAR_PROP_WRITE_NO_RSP, //???

},

1, //valueLength

NULL, //pvalue

GATT_PERM_READ //permission

},

{ // attrib_index 2

ATTRIB_FLAG_VALUE_APPL, //flag:0x0004, 支持写数据

{ LO_WORD(BLE_UUID_CM_TX_CHARACTERISTIC), //uuid:0x0013

HI_WORD(BLE_UUID_CM_TX_CHARACTERISTIC),

},

0, //valueLength

NULL, //pvalue

GATT_PERM_WRITE //permission

},

// attrib_index 3 ...

{ // attrib_index 4

ATTRIB_FLAG_VALUE_APPL, //flag:0x0004, 支持写数据

{ LO_WORD(BLE_UUID_CM_RXN_CHARACTERISTIC), //uuid:0x0014

HI_WORD(BLE_UUID_CM_RXN_CHARACTERISTIC),

},

0, //valueLength

NULL, //pvalue

GATT_PERM_NOTIF_IND //permission

},

{ // attrib_index 5

(ATTRIB_FLAG_VALUE_INCL | ATTRIB_FLAG_CCCD_APPL),

{

LO_WORD(GATT_UUID_CHAR_CLIENT_CONFIG), //uuid:0x2902

HI_WORD(GATT_UUID_CHAR_CLIENT_CONFIG),

LO_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT),

HI_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT)

},

2, //valueLength

NULL, //pvalue

(GATT_PERM_READ | GATT_PERM_WRITE) //permission

},

// attrib_index 6 ...

{ // attrib_index 7

ATTRIB_FLAG_VALUE_APPL, //flag

{ LO_WORD(BLE_UUID_CM_RXC_CHARACTERISTIC), //uuid:0x0015

HI_WORD(BLE_UUID_CM_RXC_CHARACTERISTIC),

},

0, //valueLength

NULL, //pvalue

GATT_PERM_NOTIF_IND //permission

},

}

6 profile

蓝牙规范冗余繁杂,所以蓝牙设备通常只实现所需协议;这部分协议好比蓝牙规范的剪影,所以称为profile;

每个attribute_table可视为一个profile协议,每个profile协议中包含多个service服务;每个service服务中包含多个characteristic特征值服务;

这些 service 和 characteristic 体现在bwps_service_tbl 中都是一条service服务,都有自己的attrib_index id;是并列存在而非包含的关系;

6.1 私有协议 profile 举例

对于当前sdk而言,其中包含了LB私有profile、CM私有profile,两个私有协议的profile;

此处以LB私有profile为例来分析,将实现代码与profile定义对应上;

私有profile协议的特征值service为ffb0,ffb0 service的特征值有ffb1,ffb2,ffb3,ffb4,ffb6;

6.2 私有协议 profile 流程

profile是如何通过service和characteristics来接收数据服务的呢?

通信由提前约定好service和characteristics,在bwps_service_tbl中为其添加对应的 T_ATTRIB_APPL attrib_index数据服务;

设备接收到数据之后,characteristic会调用对应permissions的回调函数;

回调函数中通过判断attrib_index来区分如何传递服务数据的何去何从;

根据代码推断出来的逻辑大致如上;那么是否可以根据特征值来反推attrib_index呢?它们通过permissions联系起来没法反推;

6.3 常见 service

除去传输数据的service attribute之外,还要一些数据服务由协议栈实现,概述部分如下,具体先放着;

6.3.1 uuid 1800 service attribute

GAP Generic Access服务,为必须service,包含characteristic:2a00、2a01、2a04、2a06;

包含device name;appearance;PPCP;CAR等characteristic;由GAP协议层实现的;

6.3.2 uuid 1801 service attribute

GATT Generic attribute,为必须service,包含characteristic:2a05、2902、2b3a;

包含service change等characteristic;由GAP协议层实现的;

6.3.3 uuid 2902 characteristic attribute;

CCCD使能:server允许发送notify或indicate类型的characteristic给client;

CCCD禁能:server禁止发送notify或indicate类型的characteristic给client;

所以如果蓝牙有notify或者indicate特征值的功能,那么就应该添加CCCD 特征值attribute

7 realtek sdk ble service初始化

以下是瑞昱sdk中对gatt协议的初始化逻辑流程,本文则是对如下代码进行的内容扩展;

//main.c

int main(void){

//...

app_ble_service_init();

}

//app_ble_service.c

//注册4个gatt service服务,然后返回注册id,通过service id来标识不同的服务;

//这个需要看数据手册和sdk文档,相关资料我都没有也不知道怎么找,先放着;

//先以sdk处理的profile数据源的service为例继续往下走;

void app_ble_service_init(void){

server_init(4);

wristband_ser_id = bwps_service_add_service((void *)app_profile_callback);//sdk data src profile service;

ota_ser_id = ota_add_service((void *)app_profile_callback); //xx? profile service

dfu_ser_id = dfu_add_service((void *)app_profile_callback); //xx? profile service

hid_kb_ser_id = hids_add_service((void *)app_profile_callback); //xx? profile serveice

//理论上这里应该给每个service都注册自己的回调函数的,但是下面用同一个回调函数来注册;所以所有service都在这个模块中处理了;

server_register_app_cb(app_profile_callback);

client_init(2);

ancs_init(1);

ams_init(1);

}

//wristband_private_service.c

//seivice的attribute table >> bwps_service_tbl[]

const T_ATTRIB_APPL bwps_service_tbl[] ={

//...前文4.3小节attribute table

}

//seivice的回调函数 >> bwps_service_cbs

const T_FUN_GATT_SERVICE_CBS bwps_service_cbs =

{

bwps_service_attr_read_cb, // Read callback function pointer

bwps_service_attr_write_cb, // Write callback function pointer

bwps_service_cccd_update_cb // CCCD update callback function pointer

//*** 重写这三个函数作为应用层profile数据的处理开始;***

};

//wristband_private_service.c

//service map>> bwps_service_tbl[]、bwps_service_cbs;

//所以这里的回调函数应该是由蓝牙底层驱动中断来触发的回调处理;

uint8_t bwps_service_add_service(void *app_profile_callback){

uint8_t bwps_service_id;

server_add_service(&bwps_service_id,

(uint8_t *)bwps_service_tbl,

sizeof(bwps_service_tbl),

bwps_service_cbs);

pfn_wristband_service_cb = (P_FUN_SERVER_GENERAL_CB)app_profile_callback;

return bwps_service_id;

}

//wristband_private_service.c

//回调系列之 write callback sample;

T_APP_RESULT bwps_service_attr_write_cb(uint8_t conn_id,

T_SERVER_ID service_id,

uint16_t attrib_index,

T_WRITE_TYPE write_type,

uint16_t length,

uint8_t *p_value,

P_FUN_WRITE_IND_POST_PROC *p_write_ind_post_proc)

{

//这个attrib_index是attrib_table中的序号,没有直接对应上,推导可知;

switch(attrib_index){

case GATT_ATTRIB_INDEX_BG_DATA:

memcpy(receive_temp + 1, p_value, length);

//...大数据接收

break;

case GATT_ATTRIB_INDEX_AT_DATA:

memcpy(receive_at_temp + 1, p_value, length);

//...at指令接收,at指令设置完毕之后直接往通道回复,没有发送msg;

break;

}

}

//数据接收完毕后,send cmd msg queue to l1send_queue_handle;

//另起一应用层task,阻塞等待 recv msg queue from l1send_queue_handle;

//因为使用的是阻塞等待,所以没收到msg的话task就会一直被挂起,这样就不会占用资源,这个构思挺巧的;

7.1 接收数据后续的应用层task处理逻辑补充,即profile具体代码;

//sdk\LW107-10701\src\app\watch\watch_app\main.c

int main(void){

//...

task_init();

}

static void task_init(void){

//...

communicate_task_init();

}

//sdk\LW107-10701\src\app\watch\communicate\communicate_task.c

//l1send_task线程创建;

void communicate_task_init(){

os_task_create(&l1send_task_handle, "l1send", l1send_task, 0, L1SEND_TASK_STACK_SIZE, L1SEND_TASK_PRIORITY);

}

//l1send_task线程:创建了消息队列 l1send_queue_handle;

void l1send_task(void *pvParameters){

//参数名称:os_msg_queue_create(pp_handle, p_name, msg_num, msg_size);

os_msg_queue_create(&l1send_queue_handle, "l1sendQ", 0x10, sizeof(uint16_t));

while (true){

//while(true)搭配永久阻塞等待,觉得哪里怪怪的,但是又十分自洽。。。==!

if (os_msg_recv(l1send_queue_handle, &event, 0xFFFFFFFF) == true){

switch(event){

case CMD_xx:

cp_xx();

//cp_xx()处理完数据后,调用sendRespondPacket(NB_RET_CMD rs);

break;

//...

}

}

}

}

8 小节

本文结构由上至下为循序渐进,从下往上查阅则方便追踪;

参考博文先放着:深入浅出低功耗蓝牙(BLE)协议栈 - iini - 博客园 (cnblogs.com)