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

 

 

位于:<KEIL path> \ARM\Device\Nordic\nrf51822\Board\pca10001\s110\ble_app_hrs 

Heart Rate Example

The Heart Rate Application is a firmware example that implements the Heart Rate profile using the hardware delivered in the nRF51822 Development Kit.

The source code and project file can be found in the <InstallFolder>\Nordic\nrf51822\Board\nrf6310\s110\ble_app_hrs folder.

The application includes the two services in the Heart Rate profile:

In addition, use of the Battery Service is also demonstrated.

When the application starts, three timers are started which control generation of various parts of the Heart Rate Measurement characteristic value:

  • Heart Rate
  • RR Interval
  • Sensor Contact Detected

Also, a timer for generating battery measurements is started.

The sensor measurements are simulated the following way:

When notification of Heart Rate Measurement characteristic is enabled, the Heart Rate Measurement, containing the current value for all the components of the Heart Rate Measurement characteristic, is notified each time the Heart Rate measurement timer expires. When notification of Battery Level characteristic is enabled, the Battery Level is notified each time the Battery Level measurement timer expires.

Note
This application is not power optimised!
The application will stop advertising after 3 minutes and go to system-off mode. Push the button 0 restart advertising.

 

Setup

Instructions on how to set up the nRFgo Motherboard: nRFgo Motherboard Setup (nRF6310).

LED assignments:

  • LED 0: Advertising
  • LED 1: Connected
  • LED 7: Asserted (i.e. an assert check in the application has failed)

Buttons assignments:

  • Button 0: Wake-up from system-off and restart advertising.
  • Button 1: Wake-up erase all bonds and restart advertising.

 

Testing

The Heart Rate Application can be tested using the nRF Utility app for iOS and Android. The app will be listed as "nRFready Utility" on Apple Store and as "nRF Utility" on Google Play.

It can also be tested using the Master Control Panel as follows:

  1. Compile and program the application. Observe that the Advertising LED is lit.
  2. Connect to the device from Master Control Panel (the device will be advertising as 'Nordic_HRM'), then perform service discovery. Observe that the Connected LED is lit, and the Advertising LED is off.
  3. Click the 'Enable services' button on the Master Control Panel. Observe that Heart Rate notifications are received every second, and Battery Level notifications are received every two seconds. 

 

main函数

 1 int main(void)
 2 {
 3     uint32_t err_code;
 4 
 5     timers_init();//@details Initializes the timer module. This creates and starts application timers.
 6     gpiote_init();
 7     buttons_init();
 8     ble_stack_init();
 9     bond_manager_init();
10 
11     // Initialize Bluetooth Stack parameters
12     gap_params_init();
13     advertising_init();
14     services_init();
15     conn_params_init();
16     sec_params_init();
17 
18     // Start advertising
19     advertising_start();
20 
21     // Enter main loop
22     for (;;)
23     {
24         // Switch to a low power state until an event is available for the application
25         err_code = sd_app_evt_wait();
26         APP_ERROR_CHECK(err_code);
27     }
28 }

初始化->start ad->for loop


 

Timer初始化

 1 /**@brief Function for the Timer initialization.
 2  *
 3 * @details Initializes the timer module. This creates and starts application timers.
 4 */
 5 static void timers_init(void)
 6 {
 7     uint32_t err_code;
 8 
 9     // Initialize timer module
10     APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false);
11 
12     // Create timers
13     err_code = app_timer_create(&m_battery_timer_id,
14                                 APP_TIMER_MODE_REPEATED,
15                                 battery_level_meas_timeout_handler);
16     APP_ERROR_CHECK(err_code);
17 
18     err_code = app_timer_create(&m_heart_rate_timer_id,
19                                 APP_TIMER_MODE_REPEATED,
20                                 heart_rate_meas_timeout_handler);
21     APP_ERROR_CHECK(err_code);
22 }

使用app_timer_create创建了两个时钟,处理函数分别是battery_level_meas_timeout_handler和heart_rate_meas_timeout_handler。

 

// Initialize timer module
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false);
1、参数宏APP_TIMER_INIT()
这个宏用于初始化app_timer模块,这是一个参数宏,接口定义如下:
APP_TIMER_INIT(PRESCALER, MAX_TIMERS, OP_QUEUES_SIZE, USE_SCHEDULER)
其中PRESCALE     分频比例,填入0的话,每秒就产生32768次tick,定时最大长度为0xFFFFFF次tick,也就是说500多秒定时。
PS3:与ucos提供的时基tick不同,本SDK的主要在定时到达的时候进入RTC中断,而不是每个TICK都进入。因此就算每秒就产生32768次tick,也不会拖慢系统性能。
MAX_TIMERS       必须大于等于工程中创建的timer数量。
OP_QUEUES_SIZE   操作队列的大小,具体意思看第三节。如果不作死,选择等于MAX_TIMERS就行了。
USE_SCHEDULER    是否使用任务调度器,当前不使用

#define APP_TIMER_PRESCALER                  0                                         /**< Value of the RTC1 PRESCALER register. */
#define APP_TIMER_MAX_TIMERS                 4                                         /**< Maximum number of simultaneously created timers. */
#define APP_TIMER_OP_QUEUE_SIZE              5                                         /**< Size of timer operation queues. */
 
 // Create timers
    err_code = app_timer_create(&m_battery_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                battery_level_meas_timeout_handler);
2、函数app_timer_create()
用于创建一个timer,并获取生成timer的控制句柄。接口定义如下:
uint32_t app_timer_create(app_timer_id_t *            p_timer_id,
                          app_timer_mode_t            mode,
                          app_timer_timeout_handler_t timeout_handler)
p_timer_id         读取到创建的timer的句柄
mode             timer的类型,其中
                  APP_TIMER_MODE_SINGLE_SHOT是单次执行
    APP_TIMER_MODE_REPEATED是循环执行
timeout_handler    被注册到内核的回调函数,当timer超时后就会执行。
static app_timer_id_t                        m_battery_timer_id;                       /**< Battery timer. */
 
 
在处理函数中分别测电量和心率。这些函数被周期性执行~
 1 /**@brief Function for handling the Battery measurement timer timeout.
 2  *
 3  * @details This function will be called each time the battery level measurement timer expires.
 4  *          This function will start the ADC.
 5  *
 6  * @param[in]   p_context   Pointer used for passing some arbitrary information (context) from the
 7  *                          app_start_timer() call to the timeout handler.
 8  */
 9 static void battery_level_meas_timeout_handler(void * p_context)
10 {
11     UNUSED_PARAMETER(p_context);
12     battery_start();
13 }
14 
15 
16 /**@brief Function for handling the Heart rate measurement timer timeout.
17  *
18  * @details This function will be called each time the heart rate measurement timer expires.
19  *          It will exclude RR Interval data from every third measurement.
20  *
21  * @param[in]   p_context   Pointer used for passing some arbitrary information (context) from the
22  *                          app_start_timer() call to the timeout handler.
23  */
24 static void heart_rate_meas_timeout_handler(void * p_context)
25 {
26     uint32_t err_code;
27 
28     UNUSED_PARAMETER(p_context);
29 
30     err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, m_cur_heart_rate);
31 
32     if (
33         (err_code != NRF_SUCCESS)
34         &&
35         (err_code != NRF_ERROR_INVALID_STATE)
36         &&
37         (err_code != BLE_ERROR_NO_TX_BUFFERS)
38         &&
39         (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
40     )
41     {
42         APP_ERROR_HANDLER(err_code);
43     }
44 }

 

时钟创建后并不会自动运行,当调用application_timers_start后时钟开始运行:

 1 /**@brief Function for starting the application timers.
 2  */
 3 static void application_timers_start(void)
 4 {
 5     uint32_t err_code;
 6 
 7     // Start application timers
 8     err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
 9     APP_ERROR_CHECK(err_code);
10 
11     err_code = app_timer_start(m_heart_rate_timer_id, HEART_RATE_MEAS_INTERVAL, NULL);
12     APP_ERROR_CHECK(err_code);
13

 

services_init()初始化程序中的三个服务:ble_dis.c, ble_bas.c, ble_hrs.c

 1 /**@brief Function for initializing the services that will be used by the application.
 2  *
 3  * @details Initialize the Heart Rate, Battery and Device Information services.
 4  */
 5 static void services_init(void)
 6 {
 7     uint32_t       err_code;
 8     ble_hrs_init_t hrs_init;
 9     ble_bas_init_t bas_init;
10     ble_dis_init_t dis_init;
11     uint8_t        body_sensor_location;
12 
13     // Initialize Heart Rate Service
14     body_sensor_location = BLE_HRS_BODY_SENSOR_LOCATION_FINGER;
15 
16     memset(&hrs_init, 0, sizeof(hrs_init));
17 
18     hrs_init.evt_handler = hrs_event_handler;
19     hrs_init.is_sensor_contact_supported = false;
20     hrs_init.p_body_sensor_location      = &body_sensor_location;
21 
22     // Here the sec level for the Heart Rate Service can be changed/increased.
23     BLE_GAP_CONN_SEC_MODE_SET_OPEN(&hrs_init.hrs_hrm_attr_md.cccd_write_perm);
24     BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_hrm_attr_md.read_perm);
25     BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_hrm_attr_md.write_perm);
26 
27     BLE_GAP_CONN_SEC_MODE_SET_OPEN(&hrs_init.hrs_bsl_attr_md.read_perm);
28     BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_bsl_attr_md.write_perm);
29 
30     err_code = ble_hrs_init(&m_hrs, &hrs_init);
31     APP_ERROR_CHECK(err_code);
32 
33     // Initialize Battery Service
34     memset(&bas_init, 0, sizeof(bas_init));
35 
36     // Here the sec level for the Battery Service can be changed/increased.
37     BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.cccd_write_perm);
38     BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.read_perm);
39     BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&bas_init.battery_level_char_attr_md.write_perm);
40 
41     BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_report_read_perm);
42 
43     bas_init.evt_handler          = NULL;
44     bas_init.support_notification = true;
45     bas_init.p_report_ref         = NULL;
46     bas_init.initial_batt_level   = 100;
47 
48     err_code = ble_bas_init(&bas, &bas_init);
49     APP_ERROR_CHECK(err_code);
50 
51     // Initialize Device Information Service
52     memset(&dis_init, 0, sizeof(dis_init));
53 
54     ble_srv_ascii_to_utf8(&dis_init.manufact_name_str, MANUFACTURER_NAME);
55 
56     BLE_GAP_CONN_SEC_MODE_SET_OPEN(&dis_init.dis_attr_md.read_perm);
57     BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&dis_init.dis_attr_md.write_perm);
58 
59     err_code = ble_dis_init(&dis_init);
60     APP_ERROR_CHECK(err_code);
61 }

 

static ble_hrs_t的结构定义:

 1 /**@brief Heart Rate Service structure. This contains various status information for the service. */  
 2 typedef struct ble_hrs_s  
 3 {  
 4     ble_hrs_evt_handler_t        evt_handler;                                          /**< Event handler to be called for handling events in the Heart Rate Service. */  
 5     bool                         is_expended_energy_supported;                         /**< TRUE if Expended Energy measurement is supported. */  
 6     bool                         is_sensor_contact_supported;                          /**< TRUE if sensor contact detection is supported. */  
 7     uint16_t                     service_handle;                                       /**< Handle of Heart Rate Service (as provided by the BLE stack). */  
 8     ble_gatts_char_handles_t     hrm_handles;                                          /**< Handles related to the Heart Rate Measurement characteristic. */  
 9     ble_gatts_char_handles_t     bsl_handles;                                          /**< Handles related to the Body Sensor Location characteristic. */  
10     ble_gatts_char_handles_t     hrcp_handles;                                         /**< Handles related to the Heart Rate Control Point characteristic. */  
11     uint16_t                     conn_handle;                                          /**< Handle of the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection). */  
12     bool                         is_sensor_contact_detected;                           /**< TRUE if sensor contact has been detected. */  
13     uint16_t                     rr_interval[BLE_HRS_MAX_BUFFERED_RR_INTERVALS];       /**< Set of RR Interval measurements since the last Heart Rate Measurement transmission. */  
14     uint16_t                     rr_interval_count;                                    /**< Number of RR Interval measurements since the last Heart Rate Measurement transmission. */  
15 } ble_hrs_t; 

 

ble_hrs.h/ble_hrs.c是心率计程序服务的代码。

 1 /** @file
 2  *
 3  * @defgroup ble_sdk_srv_hrs Heart Rate Service
 4  * @{
 5  * @ingroup ble_sdk_srv
 6  * @brief Heart Rate Service module.
 7  *
 8  * @details This module implements执行 the Heart Rate Service with the Heart Rate Measurement,
 9  *          Body Sensor Location and Heart Rate Control Point characteristics.
10  *          During initialization it adds the Heart Rate Service and Heart Rate Measurement
11  *          characteristic to the BLE stack database. Optionally it also adds the
12  *          Body Sensor Location and Heart Rate Control Point characteristics.
13  *
14  *          If enabled, notification of the Heart Rate Measurement characteristic is performed
15  *          when the application calls ble_hrs_heart_rate_measurement_send().
16  *
17  *          The Heart Rate Service also provides a set of functions for manipulating the
18  *          various fields in the Heart Rate Measurement characteristic, as well as setting
19  *          the Body Sensor Location characteristic value.
20  *
21  *          If an event handler is supplied by the application, the Heart Rate Service will
22  *          generate Heart Rate Service events to the application.
23  *
24  * @note The application must propagate BLE stack events to the Heart Rate Service module by calling
25  *       ble_hrs_on_ble_evt() from the from the @ref ble_stack_handler callback.
26  *
27  * @note Attention! 
28  *  To maintain compliance with Nordic Semiconductor ASA Bluetooth profile 
29  *  qualification listings, this section of source code must not be modified.
30  */

 

buttons_init(void)初始化两个按钮:HR_INC_BUTTON_PIN_NO和HR_DEC_BUTTON_PIN_NO,分别模拟心率计的加减。

 1 /**@brief Function for initializing the button module. 
 2  */  
 3 static void buttons_init(void)  
 4 {  
 5     // Configure HR_INC_BUTTON_PIN_NO and HR_DEC_BUTTON_PIN_NO as wake up buttons and also configure  
 6     // for 'pull up' because the eval board does not have external pull up resistors connected to  
 7     // the buttons.  
 8     static app_button_cfg_t buttons[] =  
 9     {  
10         {HR_INC_BUTTON_PIN_NO, false, BUTTON_PULL, button_event_handler},  
11         {HR_DEC_BUTTON_PIN_NO, false, BUTTON_PULL, button_event_handler}  // Note: This pin is also BONDMNGR_DELETE_BUTTON_PIN_NO  
12     };  
13       
14     APP_BUTTON_INIT(buttons, sizeof(buttons) / sizeof(buttons[0]), BUTTON_DETECTION_DELAY, false);  
15 }  

 

当按下按钮时,处理程序是button_event_handler(),它处理心率计的加减模拟:

 1 /**@brief Function for handling button events.
 2  *
 3  * @param[in]   pin_no   The pin number of the button pressed.
 4  */
 5 static void button_event_handler(uint8_t pin_no)
 6 {
 7     switch (pin_no)
 8     {
 9         case HR_INC_BUTTON_PIN_NO:
10             // Increase Heart Rate measurement
11             m_cur_heart_rate += HEART_RATE_CHANGE;
12             if (m_cur_heart_rate > MAX_HEART_RATE)
13             {
14                 m_cur_heart_rate = MIN_HEART_RATE; // Loop back
15             }
16             break;
17             
18         case HR_DEC_BUTTON_PIN_NO:
19             // Decrease Heart Rate measurement
20             m_cur_heart_rate -= HEART_RATE_CHANGE;
21             if (m_cur_heart_rate < MIN_HEART_RATE)
22             {
23                 m_cur_heart_rate = MAX_HEART_RATE; // Loop back
24             }
25             break;
26             
27         default:
28             APP_ERROR_HANDLER(pin_no);
29             break;
30     }
31 }

 

注:

本篇讲了整个工程的大致结构
下一篇具体分析几个服务
 
More:

 

 

 

posted @ 2015-12-13 23:59  beautifulzzzz  阅读(4991)  评论(4编辑  收藏  举报