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属性: 该从机可以被写入
//read属性: 该从机可以被读取
//notify属性: 该从机可以主动发送通知消息;
//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 小节
本文结构由上至下为循序渐进,从下往上查阅则方便追踪;