[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)

转载自:

http://www.cnblogs.com/zjutlitao/p/5051166.html

 

开机初始化Log


 

Log编号                       函数名                             所在文件名

复制代码
 1 000001:                                          main                                                        ..\main.c
 2 000002:                                   timers_init                                                        ..\main.c
 3 000003:                                   gpiote_init                                                        ..\main.c
 4 000004:                                  buttons_init                                                        ..\main.c
 5 000005:                                ble_stack_init                                                        ..\main.c
 6 000006:                             bond_manager_init                                                        ..\main.c
 7 000007:                               gap_params_init                                                        ..\main.c
 8 000008:                              advertising_init                                                        ..\main.c
 9 000009:                                 services_init                                                        ..\main.c
10 000010:                                  ble_hrs_init                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
11 000011:               heart_rate_measurement_char_add                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
12 000012:                                    hrm_encode                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
13 000013:                 body_sensor_location_char_add                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
14 000014:                                  ble_bas_init                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
15 000015:                        battery_level_char_add                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
16 000016:                                  ble_dis_init                 ..\..\..\..\..\Source\ble\ble_services\ble_dis.c
17 000017:                                      char_add                 ..\..\..\..\..\Source\ble\ble_services\ble_dis.c
18 000018:                              conn_params_init                                                        ..\main.c
19 000019:                               sec_params_init                                                        ..\main.c
20 000020:                             advertising_start                                                        ..\main.c
21 000021:                                     led_start                                                         ..\led.c
22 000022:                                      ppi_init                                                         ..\led.c
23 000023:                                   timer1_init                                                         ..\led.c
24 000024:                                   gpiote_init                                                         ..\led.c
复制代码

下面是main函数对应的初始化函数:

[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第1张图片

 

 上面Log中第九行server_init中会跳转到hrs,bas,dis中进行相关操作,我们重点分析——

【第一段黄色地带讲解】

复制代码
 1 uint32_t ble_hrs_init(ble_hrs_t * p_hrs, const ble_hrs_init_t * p_hrs_init)
 2 {
 3     LOG(__FUNCTION__);
 4     uint32_t   err_code;
 5     ble_uuid_t ble_uuid;
 6 
 7     // Initialize service structure
 8     p_hrs->evt_handler                 = p_hrs_init->evt_handler;
 9     p_hrs->is_sensor_contact_supported = p_hrs_init->is_sensor_contact_supported;
10     p_hrs->conn_handle                 = BLE_CONN_HANDLE_INVALID;
11     p_hrs->is_sensor_contact_detected  = false;
12     p_hrs->rr_interval_count           = 0;

这一部分主要是初始化p_hrs结构体,p_hrs_init的内容在server_init赋值的,如下:
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第2张图片

其中第433行设置hrs的事件句柄,这样可以在其中监听hrs的相关事件,如下:
这个和ble_evt_dispatch传送BLE协议栈事件给hrs模块的ble_hrs_on_ble_evt函数稍有不同
在on_xxxx中主要处理on_connect、disconnect和write事件
注:其中case的两个事件类型是自定义的枚举型
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第3张图片

上面437~443是设置hrs的attribute的可读可写等属性的
其中hrs_hrm_attr_md是Initial security level for heart rate service measurement attribute
其中hrs_bsl_attr_md是Initial security level for body sensor location attribute
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第4张图片

上面89、90行的红色部分的结构体为:
可见,cccd比无cccd的多了个cccd_write_perm
这里的cccd为:客户端特性配置描述符(Client Characteristic Configuration Descriptor,CCCD)
这个带cccd的结构体在SDK描述如下:
Security settings structure.
This structure contains the security options needed during initialization of the service. It can be used when the charecteristics contains cccd. 
我的理解是attribute中的属性有两种:属性值或描述符。如果想支持通知或指示notify,那么就得选用cccd的这种~
注:关于BLE的一些名词[1] profile\service\characteristic\uuid关系[2]
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第5张图片

综上:HRS服务配置了两个characteristic,他们分别是heart rate measurement characteristic(简称HRMC)和body sensor location characteristic。其中HRMC具有notify功能,不具有读写功能,即:他的值不能被其他蓝牙设备请求读写,只能主动发送给设备(称为notify,类似于web中的推送~)

14     // Add service
15     BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_HEART_RATE_SERVICE);
16
17
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_hrs->service_handle);
18 if (err_code != NRF_SUCCESS)
19 {
20 return err_code;
21 }

其中
BLE_UUID_BLE_ASSIGN是一个给uuid初始赋值的宏定义:

而sd_ble_gatts_service_add负责:Add a service declaration to the local server ATT table.
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第6张图片

谈到sd_ble_gatts_service_add,就必须谈一下Function packet format:
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第7张图片

综上:上面14~21行代码就实现了将BLE_UUID_HEART_RATE_SERVICE加入到蓝牙协议栈中~

23
// Add heart rate measurement characteristic 24 err_code = heart_rate_measurement_char_add(p_hrs, p_hrs_init); 25 if (err_code != NRF_SUCCESS) 26 { 27 return err_code; 28 }

这里heart_rate_measurement_char_add是向上面添加的服务中添加characteristic的,非API函数,具体拆分讲解如下:
该函数前面都是一些赋值操作,最后调用了一个API函数:

如下,该API实现将一个属性声明,一个属性值声明和一个可选属性描述符声明添加进ATT表。
这个添加的属性会添加进最近添加的服务中,当然perminssions需要统一等情况要注意~
该函数的参数情况如下:
· 第一个service_handle指向该属性所在的服务
· 第二个p_char_md是属性的原数据
· 第三个p_attr_char_value是指向attribute结构体所对应的属性值
· 第四个p_handles是指向指定句柄所存储的结构体
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第8张图片

在调用上面API之前,首先对p_char_md属性原数据进行配置attribute
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第9张图片

接着配置p_attr_char_value进行配置:
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第10张图片

其实经过前面的这些配置之后,再调用API函数sd_ble_gatts_characteristic_add就会形成一个下面格式的包:
注:sd_ble_gatts_characteristic_add 功能的包格式[3]
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第11张图片

30
if (p_hrs_init->p_body_sensor_location != NULL) 31 { 32 // Add body sensor location characteristic 33 err_code = body_sensor_location_char_add(p_hrs, p_hrs_init); 34 if (err_code != NRF_SUCCESS) 35 { 36 return err_code; 37 } 38 }
最后判断是否要把body sensor location characteristic加入到hrs服务中~
40 return NRF_SUCCESS; 41 }
复制代码

 

连接蓝牙后的效果


 

复制代码
 1 000025:                              ble_evt_dispatch                                                        ..\main.c
 2 000026:                            ble_hrs_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
 3 000027:                                    on_connect                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
 4 000028:                            ble_bas_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
 5 000029:                                    on_connect                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
 6 000030:                                    on_ble_evt                                                        ..\main.c
 7 000031:                                      led_stop                                                         ..\led.c
 8 000032:                      application_timers_start                                                        ..\main.c
 9 000033:               heart_rate_meas_timeout_handler                                                        ..\main.c
10 000034:           ble_hrs_heart_rate_measurement_send                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
11 000035:                                    hrm_encode                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
12 000036:               heart_rate_meas_timeout_handler                                                        ..\main.c
13 000037:           ble_hrs_heart_rate_measurement_send                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
14 000038:                                    hrm_encode                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
15 000039:            battery_level_meas_timeout_handler                                                        ..\main.c
16 000040:                                 battery_start                                                     ..\battery.c
17 000041:                                ADC_IRQHandler                                                     ..\battery.c
18 000042:                  ble_bas_battery_level_update                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
19 000043:               heart_rate_meas_timeout_handler                                                        ..\main.c
20 000044:           ble_hrs_heart_rate_measurement_send                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
21 000045:                                    hrm_encode                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
22 000046:               heart_rate_meas_timeout_handler                                                        ..\main.c
23 000047:           ble_hrs_heart_rate_measurement_send                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
the same...
复制代码

 

如Log第25行手机连接上nRF51822之后,BLE协议栈把事件通过ble_evt_dispatch分配到每个模

复制代码
 1 /**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler.
 2  *
 3  * @details This function is called from the BLE Stack event interrupt handler after a BLE stack
 4  *          event has been received.
 5  *
 6  * @param[in]   p_ble_evt   Bluetooth stack event.
 7  */
 8 static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
 9 {
10     LOG(__FUNCTION__);
11     ble_bondmngr_on_ble_evt(p_ble_evt);
12     ble_hrs_on_ble_evt(&m_hrs, p_ble_evt);

先进入ble_hrs_on_ble_evt,进而解析事件执行on_connect,如下:eto
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第12张图片

13     ble_bas_on_ble_evt(&bas, p_ble_evt);
14     ble_conn_params_on_ble_evt(p_ble_evt);
15     on_ble_evt(p_ble_evt);

在on_ble_evt中当检测到connected事件,
则关闭标志广播进行中的灯的定时器,
start在timer初始化中设置的hrs和bas定时器,也因此在Log中接着会轮流周期性触发hrs和bas的timeout句柄
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第13张图片

16 }
复制代码

 

 

Heart Rate Measurement启动或终止notify监听时的Log 


 

复制代码
1 002044:                              ble_evt_dispatch                                                        ..\main.c
2 002045:                            ble_hrs_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
3 002046:                                      on_write                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
4 002047:                             on_hrm_cccd_write                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
5 002048:                             hrs_event_handler                                                        ..\main.c
6 002049:                            ble_bas_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
7 002050:                                      on_write                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
复制代码

当上位机(手机端)点击start notify时,其实有一个交互过程的!

如上面的Log:

1、首先BLE协议栈把该事件通过dispatch分派给模块ble_hrs_on_ble_evt 

2、ble_hrs_on_ble_evt 对事件解析发现是BLE_GATTS_EVT_WRITE,并调用on_write

3、从on_write又进入on_hrm_cccd_write,在其中判断并发送相应的evt_handler事件消息

[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第14张图片

4、该消息会发送到其接受函数hrs_event_handler中,并根据情况设置notify

[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第15张图片

 

Heart Rate Measurement启动监听中的Log


 

复制代码
 1 000895:               heart_rate_meas_timeout_handler                                                        ..\main.c
 2 000896:           ble_hrs_heart_rate_measurement_send                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
 3 000897:                                    hrm_encode                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
 4 000898:                              ble_evt_dispatch                                                        ..\main.c
 5 000899:                            ble_hrs_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
 6 000900:                            ble_bas_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
 7 000901:                                    on_ble_evt                                                        ..\main.c
 8 000902:               heart_rate_meas_timeout_handler                                                        ..\main.c
 9 000903:           ble_hrs_heart_rate_measurement_send                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
10 000904:                                    hrm_encode                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
11 000905:            battery_level_meas_timeout_handler                                                        ..\main.c
12 000906:                                 battery_start                                                     ..\battery.c
13 000907:                                ADC_IRQHandler                                                     ..\battery.c
14 000908:                  ble_bas_battery_level_update                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
15 000909:                              ble_evt_dispatch                                                        ..\main.c
16 000910:                            ble_hrs_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
17 000911:                            ble_bas_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
18 000912:                                    on_ble_evt                                                        ..\main.c
复制代码

和连接蓝牙后的Log的12-18行对比,由于这里开启了notify,所以出现了些许不一样的地方:

即:红色字体的部分!

根据Log猜测(暂时没有器材,无法调试,到早上试,再给出最终结果~):

1、可能是因为上位机使能了notification,执行了红线那句话

2、之前由于没有执行这句话使ble_hrs_heart_rate_measurement_send和ble_bas_battery_level_update中的sd_ble_gatts_hvx函数没有被执行,而一旦红线那句话执行后sd_ble_gatts_hvx将被周期性地执行:

[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第16张图片

3、而每次sd_ble_gatts_hvx函数都会触发ble协议栈产生相应的消息,通过ble_evt_dispatch派送到各个模块,最终被on_ble_evt解析到BLE_GAP_EVT_SEC_PARAMS_REQUEST消息并执行相关操作,把消息发送出去:

 

 

蓝牙断开时的Log


 

复制代码
1 008913:                              ble_evt_dispatch                                                        ..\main.c
2 008914:                            ble_hrs_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
3 008915:                                 on_disconnect                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
4 008916:                            ble_bas_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
5 008917:                                 on_disconnect                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
6 008918:                                    on_ble_evt                                                        ..\main.c
7 008919:                         system_off_mode_enter                                                        ..\main.c
复制代码

断开蓝牙比较简单,消息从协议栈经dispatch分发到hrs、bas和on_ble_evt分别做相应处理! 

 

 

注:


[1]、关于BLE的一些名词:

每个attribute属性被UUID(通用唯一标识符)唯一标识 ,UUID是标准128-bit格式的ID用来唯一标识信息。attributes 被 ATT 格式化characteristics和services形式进行传送。 
· 特征(Characteristics)— 一个characteristics包含一个单独的value值和0 –n个用来描述characteristic 值(value)的descriptors。一个characteristics可以被认为是一种类型的,类似于一个类。 
· 描述符(descriptor)   descriptor是被定义的attributes,用来描述一个characteristic的值。例如,一个descriptor可以指定一个人类可读的描述中,在可接受的范围里characteristic值,或者是测量单位,用来明确characteristic的值。 
· 服务(service)    —service是characteristic的集合。例如,你可以有一个所谓的“Heart RateMonitor”service,其中包括characteristic,如“heart rate measurement ”。你可以在 bluetooth.org找到关于一系列基于GATT的profile和service。

  [蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第17张图片

他们关系为:蓝牙设备可以包括多个Profile,一个Profile中有多个Service,一个Service中有多个Characteristic,一个Characteristic中包括一个value和多个Descriptor。

[2]、profile\service\characteristic\uuid关系:

1、profile  
  profile可以理解为一种规范,一个标准的通信协议,它存在于从机中。蓝牙组织规定了一些标准的profile,例如 HID OVER GATT ,防丢器 ,心率计等。每个profile中会包含多个service,每个service代表从机的一种能力。
2、service
  service可以理解为一个服务,在ble从机中,通过有多个服务,例如电量信息服务、系统信息服务等,每个service中又包含多个characteristic特征值。每个具体的characteristic特征值才是ble通信的主题。比如当前的电量是80%,所以会通过电量的characteristic特征值存在从机的profile里,这样主机就可以通过这个characteristic来读取80%这个数据
3、characteristic
  characteristic特征值,ble主从机的通信均是通过characteristic来实现,可以理解为一个标签,通过这个标签可以获取或者写入想要的内容。
4、UUID
  UUID,统一识别码,我们刚才提到的service和characteristic,都需要一个唯一的uuid来标识
 
每个从机都会有一个叫做profile的东西存在,不管是上面的自定义的simpleprofile,还是标准的防丢器profile,他们都是由一些列service组成,然后每个service又包含了多个characteristic,主机和从机之间的通信,均是通过characteristic来实现。
实际产品中,每个蓝牙4.0的设备都是通过服务和特征来展示自己的,服务和特征都是用UUID来唯一标识的。一个设备必然包含一个或多个服务,每个服务下面又包含若干个特征。特征是与外界交互的最小单位。蓝牙设备硬件厂商通常都会提供他们的设备里面各个服务(service)和特征(characteristics)的功能,比如哪些是用来交互(读写),哪些可获取模块信息(只读)等。比如说,一台蓝牙4.0设备,用特征A来描述自己的出厂信息,用特征B来与收发数据等。
 
[3]、sd_ble_gatts_characteristic_add 功能的包格式:
 
1、Frame format encoding of the sd_ble_gatts_characteristic_add packet.

2、Operation Code = 0xA2 (162) for sd_ble_gatts_characteristic_add, see BLE_GATTS_SVCS.

3、The parameters provided as input to sd_ble_gatts_characteristic_add are encoded in the following order

- 1 byte:  

Operation Code, Value = 0xA2 (162)

- 2 bytes:  

Service Handle

- 1 byte:   Metadata Present
    0x00 Field not present
    0x01 Field present and follows immediately in the packet
- 11..539 bytes: ble_gatts_char_md_t

Conditional: Characteristic Metadata

- 1 byte:   Characteristic Attribute Present
    0x00 Field not present
    0x01 Field present and follows immediately in the packet
- 9..527 bytes: ble_gatts_attr_t

Conditional: Characteristic Attribute

- 1 byte:   Handles Present
    0x00 Field not present
    0x01 Field present on Application Chip 

 



 

 

 

 

 

 

 

 

 

 

 

 

其包格式图像表示为:(其中characteristic metadata和char attributes展开太多,请参看SDK)

[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第18张图片

 

 

注:

本篇讲了nrf51822蓝牙ble工程的消息流 
至此整个蓝牙心率计工程分析得差不多了 
本工程链接:http://pan.baidu.com/s/1dEalb6h

 

More:

[蓝牙] 1、蓝牙核心技术了解(蓝牙协议、架构、硬件和软件笔记)

[蓝牙] 2、蓝牙BLE协议及架构浅析&&基于广播超时待机说广播事件

[蓝牙] 3、 剖析BLE心率检测工程

[蓝牙] 4、Heart Rate Service module

[蓝牙] 5、Battery Service module

@beautifulzzzz 2015-12-17 continue~ 

 

分类:  硬件_通信组网
好文要顶  关注我  收藏该文   
beautifulzzzz
关注 - 43
粉丝 - 487
荣誉: 推荐博客
+加关注
5
0
(请您对文章做出评价)
关注  |   顶部  |   评论
« 上一篇: [蓝牙] 5、Battery Service module
» 下一篇: [异常解决] ubuntu上安装虚拟机遇到的问题(vmware坑了,virtual-box简单安装,在virtual-box中安装精简版win7)
posted @  2015-12-17 01:50  beautifulzzzz 阅读( 278) 评论( 2)  编辑  收藏

  
#1楼 [ 楼主2015-12-17 11:39 |  beautifulzzzz   
引用Heart Rate Measurement启动监听中的Log 上面存在猜测部分,我现在测了下:
1
2
3
4
5
6
7
8
9
10
11
static  void  ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
     LOG(__FUNCTION__);
     ble_bondmngr_on_ble_evt(p_ble_evt);
     ble_hrs_on_ble_evt(&m_hrs, p_ble_evt);
     ble_bas_on_ble_evt(&bas, p_ble_evt);
     ble_conn_params_on_ble_evt(p_ble_evt);
     on_ble_evt(p_ble_evt);
     
     printf ( "%d\n" ,p_ble_evt->header.evt_id);
}

输出Log的printf为1
[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)_第19张图片
支持(0) 反对(0)
  
#2楼 [ 楼主2015-12-17 11:44 |  beautifulzzzz   
而header.evt_id为1对应的事件为:

即:Transmission Complete事件,表示此次事件到来已经完成了数据传输,也就是说上面第三点是有误的:
引用3、而每次sd_ble_gatts_hvx函数都会触发ble协议栈产生相应的消息,通过ble_evt_dispatch派送到各个模块,最终被on_ble_evt解析到BLE_GAP_EVT_SEC_PARAMS_REQUEST消息并执行相关操作,把消息发送出去:
正确的解释应该是:2、之前由于没有执行这句话使ble_hrs_heart_rate_measurement_send和ble_bas_battery_level_update中的sd_ble_gatts_hvx函数没有被执行,而一旦红线那句话执行后sd_ble_gatts_hvx将被周期性地执行;
3、sd_ble_gatts_hvx一旦执行便会将数据通过BLE协议栈底层将数据立刻发送出去,等发送完毕会产生Transmission Complete事件
因此:才会出现Log中所示在ble_evt_dispatch分派该事件给各个模块时,各个模块都没有做相应的处理~


你可能感兴趣的:(单片机,c语言,硬件,蓝牙)