nrf52832学习笔记(1)蓝牙心电例程分析

对于我这种之前完全不懂蓝牙,接触学习nordic的蓝牙感觉学起来有困难,他那api讲解文档竟然网页版的,而且链接一层又一层,网速又慢,协议栈版本又多= 。= 但还是要学啊,就拿着他的例字代码看吧。。。第一个例子是蓝牙心电。
先看下主函数

int main(void)
{
    uint32_t err_code;
    bool erase_bonds;

    // Initialize.
    app_trace_init();//不懂,看字面是追逐意思
    uart_init();     //串口初始化
    timers_init();   //定时器初始化
    buttons_leds_init(&erase_bonds);
    ble_stack_init();//ble协议栈初始化
    device_manager_init(erase_bonds);//看字面是设备管理初始化
    gap_params_init(); //gap初始化
    advertising_init();//广播初始化
    services_init();   //服务初始化
    sensor_simulator_init();//模拟传感器初始化
    conn_params_init();     //链接参数初始化

    // Start execution.
    application_timers_start();//开启定时器
    err_code = ble_advertising_start(BLE_ADV_MODE_FAST);//开启广播
    APP_ERROR_CHECK(err_code);

    // Enter main loop.
    for (;;)
    {
        power_manage();//电源管理
    }
}

按照顺序看
void uart_init(void)

static void uart_init(void)
{
    uint32_t                     err_code;
    const app_uart_comm_params_t comm_params = 
    {
        RX_PIN_NUMBER, //接收pin脚
        TX_PIN_NUMBER, //发送pin脚
        RTS_PIN_NUMBER,//流控
        CTS_PIN_NUMBER,
        APP_UART_FLOW_CONTROL_DISABLED,//不使用流控
        false,//不进行奇偶校验
        UART_BAUDRATE_BAUDRATE_Baud115200//波特率115200
    };//初始化串口参数结构图
    //初始化串口宏
    APP_UART_FIFO_INIT( &comm_params,
                       256,//RX_BUF_SIZE
                       256,//TX_BUF_SIZE
                       uart_event_handle,//串口的callback
                       APP_IRQ_PRIORITY_LOW,//看字面是优先级,追踪下看下面
                       err_code); 
    APP_ERROR_CHECK(err_code);
}
typedef enum
{   ...前面略
    APP_IRQ_PRIORITY_LOW     = _PRIO_APP_LOW, //这里
    APP_IRQ_PRIORITY_LOWEST  = _PRIO_APP_LOWEST,
    APP_IRQ_PRIORITY_THREAD  = _PRIO_THREAD     /**< "Interrupt level" when running in Thread Mode. */
} app_irq_priority_t;
#elif defined(NRF52)
#define _PRIO_SD_HIGH       0
#define _PRIO_SD_MID        1
#define _PRIO_APP_HIGH      2
#define _PRIO_APP_MID       3
#define _PRIO_SD_LOW        4
#define _PRIO_SD_LOWEST     5
#define _PRIO_APP_LOW       6  //这里
#define _PRIO_APP_LOWEST    7
#define _PRIO_THREAD        15
#else

==================================================================
timers_init(); //定时器初始化

static void timers_init(void)
{
    uint32_t err_code;

    //这是给软件定时器分配内存并初始化
    //APP_TIMER_PRESCALER :这是RTC1分频系数,是0
    //APP_TIMER_OP_QUEUE_SIZE:定时器操作队列个数,就是你创建了几个定时器,是4,他下面创建了4个定时器
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false);

    //创建电池电量定时器,用于定时更新电量
    //m_battery_timer_id 我的理解类似操作句柄
    //APP_TIMER_MODE_REPEATED 定时模式,重复定时,还有个是单次定时APP_TIMER_MODE_SINGLE_SHOT
    //battery_level_meas_timeout_handler 定时时间到的callback
    err_code = app_timer_create(&m_battery_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                battery_level_meas_timeout_handler);
    APP_ERROR_CHECK(err_code);

    err_code = app_timer_create(&m_heart_rate_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                heart_rate_meas_timeout_handler);
    APP_ERROR_CHECK(err_code);

    err_code = app_timer_create(&m_rr_interval_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                rr_interval_timeout_handler);
    APP_ERROR_CHECK(err_code);

    err_code = app_timer_create(&m_sensor_contact_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                sensor_contact_detected_timeout_handler);
    APP_ERROR_CHECK(err_code);
}

看下怎么定义这些句柄,

APP_TIMER_DEF(m_battery_timer_id); /**< Battery timer. */
APP_TIMER_DEF(m_heart_rate_timer_id); /**< Heart rate measurement timer. */
APP_TIMER_DEF(m_rr_interval_timer_id);  /**< RR interval timer. */                                                             
APP_TIMER_DEF(m_sensor_contact_timer_id); /**< Sensor contact detected timer. */

==================================================================
static void ble_stack_init(void) //协议栈初始化

static void ble_stack_init(void)
{
    uint32_t err_code;
    //NRF_CLOCK_LFCLKSRC 是一个宏,选择协议栈时钟源和校准参数、ppm
    nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;
    
    // 初始化协议栈处理模块
    SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);
    //---------------------------------------------------
    ble_enable_params_t ble_enable_params;
    err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
                                                    PERIPHERAL_LINK_COUNT,
                                                    &ble_enable_params);
    APP_ERROR_CHECK(err_code);

#ifdef BLE_DFU_APP_SUPPORT
    ble_enable_params.gatts_enable_params.service_changed = 1;
#endif // BLE_DFU_APP_SUPPORT
    //Check the ram settings against the used number of links
    CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT,PERIPHERAL_LINK_COUNT);

    // Enable BLE stack.
    err_code = softdevice_enable(&ble_enable_params);
    APP_ERROR_CHECK(err_code);

    // 注册BLE的事件派发函数
    err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
    APP_ERROR_CHECK(err_code);

    //注册sys的事件派发函数
    err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
    APP_ERROR_CHECK(err_code);
}

==================================================================
static void gap_params_init(void)//GAP层初始化
GAP:通用访问配置文件,定义了设备如何彼此发现,建立连接、绑定,同时描述了设备如何成为广播者和观察者,实现无需连接的传输
GAP初始化包括

  • 设备名
  • 外观特性
  • 首选连接参数
static void gap_params_init(void)
{
    uint32_t                err_code;
    ble_gap_conn_params_t   gap_conn_params;
    ble_gap_conn_sec_mode_t sec_mode;
    //这里设置的是设备名的特性的写权限,这里无安全性
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
    //设置设备名
    err_code = sd_ble_gap_device_name_set(&sec_mode,
                                          (const uint8_t *)DEVICE_NAME,
                                          strlen(DEVICE_NAME));
    APP_ERROR_CHECK(err_code);
    //设置外观特征
    err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_HEART_RATE_SENSOR_HEART_RATE_BELT);
    APP_ERROR_CHECK(err_code);
    //设置首选连接参数
    memset(&gap_conn_params, 0, sizeof(gap_conn_params));

    gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;//最小连接间隔,单位是1.25ms
    gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;//最大连接参数,单位是1.25ms
    gap_conn_params.slave_latency     = SLAVE_LATENCY;//从机延迟
    gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;//监督超时,单位是10ms
    //写入外设首选连接参数 Peripheral Preferred Connection Parameters
    err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
    APP_ERROR_CHECK(err_code);
}

==================================================================
static void advertising_init(void) //广播初始化

static void advertising_init(void)
{
    uint32_t      err_code;
    ble_advdata_t advdata;

    // Build advertising data struct to pass into @ref ble_advertising_init.
    memset(&advdata, 0, sizeof(advdata));
    //广播数据复制
    advdata.name_type               = BLE_ADVDATA_FULL_NAME;//名字用全称
    advdata.include_appearance      = true;//包含外观特征
    advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;//一般可发现、不支持BR/EDR
    advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);//uuid表个数
    advdata.uuids_complete.p_uuids  = m_adv_uuids;//指向uuid表数组
    //ble_adv_modes_config_t 是对不同广播模式参数的设置
    ble_adv_modes_config_t options = {0};
    options.ble_adv_fast_enabled  = BLE_ADV_FAST_ENABLED;//使能快速广播
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;//快速广播间隔,单位是0.625ms
    options.ble_adv_fast_timeout  = APP_ADV_TIMEOUT_IN_SECONDS;//快速广播超时,单位是1s
    //on_adv_evt 广播事件的callback
    err_code = ble_advertising_init(&advdata, NULL, &options, on_adv_evt, NULL);
    APP_ERROR_CHECK(err_code);
}

m_adv_uuids 是服务的uuid,广播报文包含这个uuid列表,这个列表是设备空开的首要服务,对于这里就是3个GATT首要服务

  • 心率测量
  • 电池电量
  • 设备信息
static ble_uuid_t m_adv_uuids[] = \
{{BLE_UUID_HEART_RATE_SERVICE,         BLE_UUID_TYPE_BLE},\
{BLE_UUID_BATTERY_SERVICE,            BLE_UUID_TYPE_BLE},\
{BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}}; /**< Universally unique service identifiers. */

ble_uuid_t 结构体

typedef struct
{
  uint16_t    uuid; /**< 16-bit UUID value or octets 12-13 of 128-bit UUID. */
  uint8_t     type; /**< UUID type, see @ref BLE_UUID_TYPES. If type is @ref BLE_UUID_TYPE_UNKNOWN, the value of uuid is undefined. */
} ble_uuid_t;

================================================================
static void services_init(void) //添加服务和特征

static void services_init(void)
{
    uint32_t       err_code;
    ble_hrs_init_t hrs_init;//心率测量服务变量结构体
    ble_bas_init_t bas_init;//电池电量服务变量结构体
    ble_dis_init_t dis_init;//设备信息服务变量结构体
    uint8_t        body_sensor_location;//保存传感器部位信息

    // finger 手部.
    body_sensor_location = BLE_HRS_BODY_SENSOR_LOCATION_FINGER;

    memset(&hrs_init, 0, sizeof(hrs_init));

    hrs_init.evt_handler                 = NULL;//心率测量callback
    hrs_init.is_sensor_contact_supported = true;//接触判断支持
    hrs_init.p_body_sensor_location      = &body_sensor_location;//传感器佩戴部位

    // Here the sec level for the Heart Rate Service can be changed/increased.
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&hrs_init.hrs_hrm_attr_md.cccd_write_perm);//hrm cccd 写权限
	
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_hrm_attr_md.read_perm);//心率测量数据的读权限
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_hrm_attr_md.write_perm);//heart rate service measurement 心率测量数据的写权限

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&hrs_init.hrs_bsl_attr_md.read_perm);//传感器部位属性读权限
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_bsl_attr_md.write_perm);//body sensor location //传感器部位属性写权限

		
    err_code = ble_hrs_init(&m_hrs, &hrs_init);//添加hrs 服务
    APP_ERROR_CHECK(err_code);

    // Initialize Battery Service.
    memset(&bas_init, 0, sizeof(bas_init));

    // Here the sec level for the Battery Service can be changed/increased.
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.cccd_write_perm);//电池电量cccd 写权限
		
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.read_perm);//电池电量数据属性读权限
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&bas_init.battery_level_char_attr_md.write_perm);//电池电量数据属性写权限

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_report_read_perm);//字面是电池电量报告,不知道干什么用的???

    bas_init.evt_handler          = NULL;//电池电量callback
    bas_init.support_notification = true;//电池电量特性支持notify
    bas_init.p_report_ref         = NULL;
    bas_init.initial_batt_level   = 100;//初始电量100

    err_code = ble_bas_init(&m_bas, &bas_init);//添加电池电量服务
    APP_ERROR_CHECK(err_code);

    // Initialize Device Information Service.
    memset(&dis_init, 0, sizeof(dis_init));

    ble_srv_ascii_to_utf8(&dis_init.manufact_name_str, (char *)MANUFACTURER_NAME);//把MANUFACTURER_NAME的长度和地址放到manufact_name_str结构体
    //设备信息属性的读写权限
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&dis_init.dis_attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&dis_init.dis_attr_md.write_perm);

    err_code = ble_dis_init(&dis_init);//添加设备信息服务
    APP_ERROR_CHECK(err_code);
}

分别看下添加的3个服务
1.心率测量服务

uint32_t ble_hrs_init(ble_hrs_t * p_hrs, const ble_hrs_init_t * p_hrs_init)
{
    uint32_t   err_code;
    ble_uuid_t ble_uuid;

    // Initialize service structure 初始化服务结构体
    p_hrs->evt_handler                 = p_hrs_init->evt_handler;//心率测量服务处理函数
    p_hrs->is_sensor_contact_supported = p_hrs_init->is_sensor_contact_supported;//是否支持接触检测
    p_hrs->conn_handle                 = BLE_CONN_HANDLE_INVALID;//初始化连接句柄,因为现在并未与手机连接所以先赋值无效。
    p_hrs->is_sensor_contact_detected  = false;
    p_hrs->rr_interval_count           = 0;

    // Add service 添加uuid
    BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_HEART_RATE_SERVICE);//这里是SIG定义的uuid
    //GATT服务添加,主要是添加服务UUID   p_lbs->service_handle是主服务句柄
    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
                                        &ble_uuid,
                                        &p_hrs->service_handle);

    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Add heart rate measurement characteristic
    err_code = heart_rate_measurement_char_add(p_hrs, p_hrs_init);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    if (p_hrs_init->p_body_sensor_location != NULL)
    {
        // Add body sensor location characteristic
        err_code = body_sensor_location_char_add(p_hrs, p_hrs_init);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }

    return NRF_SUCCESS;
}

//心率测量数据特性
static uint32_t heart_rate_measurement_char_add(ble_hrs_t            * p_hrs,
                                                const ble_hrs_init_t * p_hrs_init)
{
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;
    uint8_t             encoded_initial_hrm[MAX_HRM_LEN];
   
	  //配置cccd权限=========================================
    memset(&cccd_md, 0, sizeof(cccd_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    cccd_md.write_perm = p_hrs_init->hrs_hrm_attr_md.cccd_write_perm;//配置cccd读写权限
    cccd_md.vloc       = BLE_GATTS_VLOC_STACK;//属性值放在协议栈内存里

	  //特性描述符配置
    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.notify = 1;
    char_md.p_char_user_desc  = NULL;
    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = &cccd_md;
    char_md.p_sccd_md         = NULL;

    //心率特性的uuid
    BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_HEART_RATE_MEASUREMENT_CHAR);

    //配置属性值性质(就是心率测量数据的性质)
    memset(&attr_md, 0, sizeof(attr_md));

    attr_md.read_perm  = p_hrs_init->hrs_hrm_attr_md.read_perm;
    attr_md.write_perm = p_hrs_init->hrs_hrm_attr_md.write_perm;//属性值权限
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;//属性值放在协议栈内存里
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 1;//长度可变
    
		//属性值
    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid    = &ble_uuid;//uuid
    attr_char_value.p_attr_md = &attr_md;//权限
    attr_char_value.init_len  = hrm_encode(p_hrs, INITIAL_VALUE_HRM, encoded_initial_hrm);//对要发送数据进行编码,返回要发送长度
    attr_char_value.init_offs = 0;
    attr_char_value.max_len   = MAX_HRM_LEN;
    attr_char_value.p_value   = encoded_initial_hrm;

    return sd_ble_gatts_characteristic_add(p_hrs->service_handle,
                                           &char_md,
                                           &attr_char_value,
                                           &p_hrs->hrm_handles);
}
//传感器部位特性
static uint32_t body_sensor_location_char_add(ble_hrs_t * p_hrs, const ble_hrs_init_t * p_hrs_init)
{
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

	  //特性描述符配置
    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read  = 1;
    char_md.p_char_user_desc = NULL;
    char_md.p_char_pf        = NULL;
    char_md.p_user_desc_md   = NULL;
    char_md.p_cccd_md        = NULL;
    char_md.p_sccd_md        = NULL;

	  //传感器部位特性 uuid
    BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_BODY_SENSOR_LOCATION_CHAR);
    //特性值属性
    memset(&attr_md, 0, sizeof(attr_md));

    attr_md.read_perm  = p_hrs_init->hrs_bsl_attr_md.read_perm;
    attr_md.write_perm = p_hrs_init->hrs_bsl_attr_md.write_perm;//读写属性
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;//属性值放在协议栈内存里
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;//读写不需要认证
    attr_md.vlen       = 0;//不可变长度属性 
    //属性值
    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid    = &ble_uuid;
    attr_char_value.p_attr_md = &attr_md;
    attr_char_value.init_len  = sizeof (uint8_t);
    attr_char_value.init_offs = 0;
    attr_char_value.max_len   = sizeof (uint8_t);
    attr_char_value.p_value   = p_hrs_init->p_body_sensor_location;

    return sd_ble_gatts_characteristic_add(p_hrs->service_handle,
                                           &char_md,
                                           &attr_char_value,
                                           &p_hrs->bsl_handles);
}

2.电池电量服务

uint32_t ble_bas_init(ble_bas_t * p_bas, const ble_bas_init_t * p_bas_init)
{
    if (p_bas == NULL || p_bas_init == NULL)
    {
        return NRF_ERROR_NULL;
    }
    
    uint32_t   err_code;
    ble_uuid_t ble_uuid;

    // Initialize service structure
    p_bas->evt_handler               = p_bas_init->evt_handler;
    p_bas->conn_handle               = BLE_CONN_HANDLE_INVALID;
    p_bas->is_notification_supported = p_bas_init->support_notification;
    p_bas->battery_level_last        = INVALID_BATTERY_LEVEL;

    // Add service
    BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_BATTERY_SERVICE);

    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_bas->service_handle);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Add battery level characteristic
    return battery_level_char_add(p_bas, p_bas_init);
}
//电量特性添加,和之前类似,就是通过外面传来参数判断下是否要notify 
static uint32_t battery_level_char_add(ble_bas_t * p_bas, const ble_bas_init_t * p_bas_init)
{
    uint32_t            err_code;
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;
    uint8_t             initial_battery_level;
    uint8_t             encoded_report_ref[BLE_SRV_ENCODED_REPORT_REF_LEN];
    uint8_t             init_len;

    // Add Battery Level characteristic
    if (p_bas->is_notification_supported)
    {
        memset(&cccd_md, 0, sizeof(cccd_md));

        // According to BAS_SPEC_V10, the read operation on cccd should be possible without
        // authentication.
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
        cccd_md.write_perm = p_bas_init->battery_level_char_attr_md.cccd_write_perm;
        cccd_md.vloc       = BLE_GATTS_VLOC_STACK;
    }

    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read   = 1;
    char_md.char_props.notify = (p_bas->is_notification_supported) ? 1 : 0;
    char_md.p_char_user_desc  = NULL;
    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = (p_bas->is_notification_supported) ? &cccd_md : NULL;
    char_md.p_sccd_md         = NULL;

    BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_BATTERY_LEVEL_CHAR);

    memset(&attr_md, 0, sizeof(attr_md));

    attr_md.read_perm  = p_bas_init->battery_level_char_attr_md.read_perm;
    attr_md.write_perm = p_bas_init->battery_level_char_attr_md.write_perm;
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;

    initial_battery_level = p_bas_init->initial_batt_level;

    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid    = &ble_uuid;
    attr_char_value.p_attr_md = &attr_md;
    attr_char_value.init_len  = sizeof(uint8_t);
    attr_char_value.init_offs = 0;
    attr_char_value.max_len   = sizeof(uint8_t);
    attr_char_value.p_value   = &initial_battery_level;

    err_code = sd_ble_gatts_characteristic_add(p_bas->service_handle, &char_md,
                                               &attr_char_value,
                                               &p_bas->battery_level_handles);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    //p_report_ref 不知道是干什么的,应该是某个描述符
    if (p_bas_init->p_report_ref != NULL)
    {
        // Add Report Reference descriptor
        BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_REPORT_REF_DESCR);

        memset(&attr_md, 0, sizeof(attr_md));

        attr_md.read_perm = p_bas_init->battery_level_report_read_perm;
        BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.write_perm);

        attr_md.vloc    = BLE_GATTS_VLOC_STACK;
        attr_md.rd_auth = 0;
        attr_md.wr_auth = 0;
        attr_md.vlen    = 0;
        
        init_len = ble_srv_report_ref_encode(encoded_report_ref, p_bas_init->p_report_ref);
        
        memset(&attr_char_value, 0, sizeof(attr_char_value));

        attr_char_value.p_uuid    = &ble_uuid;
        attr_char_value.p_attr_md = &attr_md;
        attr_char_value.init_len  = init_len;
        attr_char_value.init_offs = 0;
        attr_char_value.max_len   = attr_char_value.init_len;
        attr_char_value.p_value   = encoded_report_ref;

        err_code = sd_ble_gatts_descriptor_add(p_bas->battery_level_handles.value_handle,
                                               &attr_char_value,
                                               &p_bas->report_ref_handle);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }
    else
    {
        p_bas->report_ref_handle = BLE_GATT_HANDLE_INVALID;
    }

    return NRF_SUCCESS;
}

3.设备信息服务

uint32_t ble_dis_init(const ble_dis_init_t * p_dis_init)
{
    uint32_t   err_code;
    ble_uuid_t ble_uuid;

    // 设备信息uuid Add service
    BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_DEVICE_INFORMATION_SERVICE);

    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &service_handle);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Add characteristics
    if (p_dis_init->manufact_name_str.length > 0)
    {
        err_code = char_add(BLE_UUID_MANUFACTURER_NAME_STRING_CHAR,
                            p_dis_init->manufact_name_str.p_str,
                            p_dis_init->manufact_name_str.length,
                            &p_dis_init->dis_attr_md,
                            &manufact_name_handles);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }
    if (p_dis_init->model_num_str.length > 0)
    {
        err_code = char_add(BLE_UUID_MODEL_NUMBER_STRING_CHAR,
                            p_dis_init->model_num_str.p_str,
                            p_dis_init->model_num_str.length,
                            &p_dis_init->dis_attr_md,
                            &model_num_handles);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }
    if (p_dis_init->serial_num_str.length > 0)
    {
        err_code = char_add(BLE_UUID_SERIAL_NUMBER_STRING_CHAR,
                            p_dis_init->serial_num_str.p_str,
                            p_dis_init->serial_num_str.length,
                            &p_dis_init->dis_attr_md,
                            &serial_num_handles);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }
    if (p_dis_init->hw_rev_str.length > 0)
    {
        err_code = char_add(BLE_UUID_HARDWARE_REVISION_STRING_CHAR,
                            p_dis_init->hw_rev_str.p_str,
                            p_dis_init->hw_rev_str.length,
                            &p_dis_init->dis_attr_md,
                            &hw_rev_handles);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }
    if (p_dis_init->fw_rev_str.length > 0)
    {
        err_code = char_add(BLE_UUID_FIRMWARE_REVISION_STRING_CHAR,
                            p_dis_init->fw_rev_str.p_str,
                            p_dis_init->fw_rev_str.length,
                            &p_dis_init->dis_attr_md,
                            &fw_rev_handles);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }
    if (p_dis_init->sw_rev_str.length > 0)
    {
        err_code = char_add(BLE_UUID_SOFTWARE_REVISION_STRING_CHAR,
                            p_dis_init->sw_rev_str.p_str,
                            p_dis_init->sw_rev_str.length,
                            &p_dis_init->dis_attr_md,
                            &sw_rev_handles);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }
    if (p_dis_init->p_sys_id != NULL)
    {
        uint8_t encoded_sys_id[BLE_DIS_SYS_ID_LEN];

        sys_id_encode(encoded_sys_id, p_dis_init->p_sys_id);
        err_code = char_add(BLE_UUID_SYSTEM_ID_CHAR,
                            encoded_sys_id,
                            BLE_DIS_SYS_ID_LEN,
                            &p_dis_init->dis_attr_md,
                            &sys_id_handles);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }
    if (p_dis_init->p_reg_cert_data_list != NULL)
    {
        err_code = char_add(BLE_UUID_IEEE_REGULATORY_CERTIFICATION_DATA_LIST_CHAR,
                            p_dis_init->p_reg_cert_data_list->p_list,
                            p_dis_init->p_reg_cert_data_list->list_len,
                            &p_dis_init->dis_attr_md,
                            &reg_cert_data_list_handles);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }
    if (p_dis_init->p_pnp_id != NULL)
    {
        uint8_t encoded_pnp_id[BLE_DIS_PNP_ID_LEN];

        pnp_id_encode(encoded_pnp_id, p_dis_init->p_pnp_id);
        err_code = char_add(BLE_UUID_PNP_ID_CHAR,
                            encoded_pnp_id,
                            BLE_DIS_PNP_ID_LEN,
                            &p_dis_init->dis_attr_md,
                            &pnp_id_handles);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }

    return NRF_SUCCESS;
}

连接参数初始化

static void conn_params_init(void)
{
    uint32_t               err_code;
    ble_conn_params_init_t cp_init;

    memset(&cp_init, 0, sizeof(cp_init));

    cp_init.p_conn_params                  = NULL;//从主机获得连接参数
    cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;//连接或者启动通知到第一次更新连接参数之间的时间
    cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;//下一次更新连接参数时间
    cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;//更新连接参数最大协商次数
    cp_init.start_on_notify_cccd_handle    = m_hrs.hrm_handles.cccd_handle;//如果是启动通知则是对于cccd句柄,如果从连接开始则设置成BLE_GATT_HANDLE_INVALID
    cp_init.disconnect_on_fail             = false;//设置为true时,如果连接参数更新失败会断开连接
    cp_init.evt_handler                    = on_conn_params_evt;//处理连接协商事件
    cp_init.error_handler                  = conn_params_error_handler;//错误处理函数

    err_code = ble_conn_params_init(&cp_init);
    APP_ERROR_CHECK(err_code);
}

static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
{
    uint32_t err_code;
    //这里如果协商失败,断开连接
    if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
    {
        err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
        APP_ERROR_CHECK(err_code);
    }
}


/**@brief Function for handling a Connection Parameters error.
 *
 * @param[in] nrf_error  Error code containing information about what went wrong.
 */
static void conn_params_error_handler(uint32_t nrf_error)
{
    APP_ERROR_HANDLER(nrf_error);
}

开启定时器

static void application_timers_start(void)
{
    uint32_t err_code;

    // Start application timers.
    err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
    APP_ERROR_CHECK(err_code);

    err_code = app_timer_start(m_heart_rate_timer_id, HEART_RATE_MEAS_INTERVAL, NULL);
    APP_ERROR_CHECK(err_code);

    err_code = app_timer_start(m_rr_interval_timer_id, RR_INTERVAL_INTERVAL, NULL);
    APP_ERROR_CHECK(err_code);

    err_code = app_timer_start(m_sensor_contact_timer_id, SENSOR_CONTACT_DETECTED_INTERVAL, NULL);
    APP_ERROR_CHECK(err_code);
}

接下来是开始广播ble_advertising_start(BLE_ADV_MODE_FAST);
广播的另外开篇写,这里先放着
下面是让52832进入低功耗,等待唤醒

static void power_manage(void)
{
    uint32_t err_code = sd_app_evt_wait();
    APP_ERROR_CHECK(err_code);
}

当唤醒后,协议栈就会指向派发函数里面的函数。派发函数里面的函数只关心自己关心的事件(通过下面这个结构体里的事件header来区分事件)

/**@brief Common BLE Event type, wrapping the module specific event reports. */
typedef struct
{
  ble_evt_hdr_t header;           /**< Event header. */
  union
  {
    ble_common_evt_t  common_evt; /**< Common Event, evt_id in BLE_EVT_* series. */
    ble_gap_evt_t     gap_evt;    /**< GAP originated event, evt_id in BLE_GAP_EVT_* series. */
    ble_l2cap_evt_t   l2cap_evt;  /**< L2CAP originated event, evt_id in BLE_L2CAP_EVT* series. */
    ble_gattc_evt_t   gattc_evt;  /**< GATT client originated event, evt_id in BLE_GATTC_EVT* series. */
    ble_gatts_evt_t   gatts_evt;  /**< GATT server originated event, evt_id in BLE_GATTS_EVT* series. */
  } evt;
} ble_evt_t;

下面看下派发函数里有哪些函数

/**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler.
 *
 * @details This function is called from the BLE Stack event interrupt handler after a BLE stack
 *          event has been received.
 *
 * @param[in] p_ble_evt  Bluetooth stack event.
 */
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
    dm_ble_evt_handler(p_ble_evt);
    ble_hrs_on_ble_evt(&m_hrs, p_ble_evt);//心率事件处理
    ble_bas_on_ble_evt(&m_bas, p_ble_evt);//电量事件处理
    ble_conn_params_on_ble_evt(p_ble_evt);//连接参数更新处理
    bsp_btn_ble_on_ble_evt(p_ble_evt);//按键处理
    on_ble_evt(p_ble_evt);//ble协议栈事件处理
    ble_advertising_on_ble_evt(p_ble_evt);//广播相关事件处理
}

心率事件处理

void ble_hrs_on_ble_evt(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
{
    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED://连接建立
            on_connect(p_hrs, p_ble_evt);
            break;

        case BLE_GAP_EVT_DISCONNECTED://连接断开
            on_disconnect(p_hrs, p_ble_evt);
            break;

        case BLE_GATTS_EVT_WRITE://向GATTS写
            on_write(p_hrs, p_ble_evt);
            break;

        default:
            // No implementation needed.
            break;
    }
}
static void on_connect(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
{   //保存连接句柄
    p_hrs->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}

static void on_disconnect(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
{   //清除连接句柄
    UNUSED_PARAMETER(p_ble_evt);
    p_hrs->conn_handle = BLE_CONN_HANDLE_INVALID;
}

static void on_write(ble_hrs_t * p_hrs, ble_evt_t * p_ble_evt)
{
    ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
    //如果写的write的handle 是cccd_handle ,表示主机使能了notify
    /*p_hrs->hrm_handles是通过 添加心率测量特性      
    heart_rate_measurement_char_add中的
    sd_ble_gatts_characteristic_add(p_hrs->service_handle,
                                           &char_md,
                                           &attr_char_value,
                                           &p_hrs->hrm_handles);
    输出参数得到
    */
    if (p_evt_write->handle == p_hrs->hrm_handles.cccd_handle)
    {
        on_hrm_cccd_write(p_hrs, p_evt_write);
    }
}

static void on_hrm_cccd_write(ble_hrs_t * p_hrs, ble_gatts_evt_write_t * p_evt_write)
{
    if (p_evt_write->len == 2)//如果写的长度时2字节(notify是2字节)
    {
        // CCCD written, update notification state
        if (p_hrs->evt_handler != NULL)//这是之前注册的callback
        {   //这个例子是没有执行这里,因为初始化是赋值NULL
            ble_hrs_evt_t evt;

            if (ble_srv_is_notification_enabled(p_evt_write->data))
            {
                evt.evt_type = BLE_HRS_EVT_NOTIFICATION_ENABLED;
            }
            else
            {
                evt.evt_type = BLE_HRS_EVT_NOTIFICATION_DISABLED;
            }
            
            p_hrs->evt_handler(p_hrs, &evt);//执行callback
        }
    }
}

上面数据传输方向是APP—>ble
ble—>APP方向是通过之前的心率测量定时器中断发送
下面是心率测量定时器中断函数

static void heart_rate_meas_timeout_handler(void * p_context)
{
    static uint32_t cnt = 0;
    uint32_t        err_code;
    uint16_t        heart_rate;

    UNUSED_PARAMETER(p_context);
    //得到心率值
    heart_rate = (uint16_t)sensorsim_measure(&m_heart_rate_sim_state, &m_heart_rate_sim_cfg);

	  cnt++;     //发送心率值
    err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, heart_rate);
    if ((err_code != NRF_SUCCESS) &&
        (err_code != NRF_ERROR_INVALID_STATE) &&
        (err_code != BLE_ERROR_NO_TX_PACKETS) &&
        (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
        )
    {
        APP_ERROR_HANDLER(err_code);
    }

    // Disable RR Interval recording every third heart rate measurement.
    // NOTE: An application will normally not do this. It is done here just for testing generation
    //       of messages without RR Interval measurements.
    m_rr_interval_enabled = ((cnt % 3) != 0);
}

uint32_t ble_hrs_heart_rate_measurement_send(ble_hrs_t * p_hrs, uint16_t heart_rate)
{
    uint32_t err_code;

    // Send value if connected and notifying
    if (p_hrs->conn_handle != BLE_CONN_HANDLE_INVALID)//判断是否是连接的
    {
        uint8_t                encoded_hrm[MAX_HRM_LEN];
        uint16_t               len;
        uint16_t               hvx_len;
        ble_gatts_hvx_params_t hvx_params;
			   //把要发送的数据编码下放到数组里
        len     = hrm_encode(p_hrs, heart_rate, encoded_hrm);
        hvx_len = len;  //搞不懂为什么要个len

        memset(&hvx_params, 0, sizeof(hvx_params));
        //这里的handle是添加心率特性那个函数输出结构体变量中的value_handle
        hvx_params.handle = p_hrs->hrm_handles.value_handle;
	    //类型是notify
        hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
		//offset 不懂
        hvx_params.offset = 0;
		//发送长度
        hvx_params.p_len  = &hvx_len;
		//待发送buffer 地址
        hvx_params.p_data = encoded_hrm;
		//发送 注意这里的handle是表示连接关系的handle
        err_code = sd_ble_gatts_hvx(p_hrs->conn_handle, &hvx_params);

        if ((err_code == NRF_SUCCESS) && (hvx_len != len))
        {
            err_code = NRF_ERROR_DATA_SIZE;
        }
    }
    else
    {
        err_code = NRF_ERROR_INVALID_STATE;
    }
    return err_code;
}

下面是电量事件处理

void ble_bas_on_ble_evt(ble_bas_t * p_bas, ble_evt_t * p_ble_evt)
{
    if (p_bas == NULL || p_ble_evt == NULL)
    {
        return;
    }
    
    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            on_connect(p_bas, p_ble_evt);
            break;

        case BLE_GAP_EVT_DISCONNECTED:
            on_disconnect(p_bas, p_ble_evt);
            break;

        case BLE_GATTS_EVT_WRITE:
            on_write(p_bas, p_ble_evt);
            break;

        default:
            // No implementation needed.
            break;
    }
}
static void on_write(ble_bas_t * p_bas, ble_evt_t * p_ble_evt)
{
    if (p_bas->is_notification_supported)
    {
        ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;

        if (
            (p_evt_write->handle == p_bas->battery_level_handles.cccd_handle)
            &&
            (p_evt_write->len == 2)
           )
        {
            // CCCD written, call application event handler
            if (p_bas->evt_handler != NULL)
            {
                ble_bas_evt_t evt;

                if (ble_srv_is_notification_enabled(p_evt_write->data))
                {
                    evt.evt_type = BLE_BAS_EVT_NOTIFICATION_ENABLED;
                }
                else
                {
                    evt.evt_type = BLE_BAS_EVT_NOTIFICATION_DISABLED;
                }

                p_bas->evt_handler(p_bas, &evt);
            }
        }
    }
}

可以看到和心率测量事件原理一样
这是APP—>ble数据流
ble—>APP方向是通过之前的电量测量定时器中断发送
下面是电量测量定时器中断函数

static void battery_level_update(void)
{
    uint32_t err_code;
    uint8_t  battery_level;

    battery_level = (uint8_t)sensorsim_measure(&m_battery_sim_state, &m_battery_sim_cfg);

    err_code = ble_bas_battery_level_update(&m_bas, battery_level);
    if ((err_code != NRF_SUCCESS) &&
        (err_code != NRF_ERROR_INVALID_STATE) &&
        (err_code != BLE_ERROR_NO_TX_PACKETS) &&
        (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
        )
    {
        APP_ERROR_HANDLER(err_code);
    }
}
uint32_t ble_bas_battery_level_update(ble_bas_t * p_bas, uint8_t battery_level)
{
    if (p_bas == NULL)
    {
        return NRF_ERROR_NULL;
    }
    
    uint32_t err_code = NRF_SUCCESS;
    ble_gatts_value_t gatts_value;
    //当前电量不等于上次记录电量
    if (battery_level != p_bas->battery_level_last)
    {
        // Initialize value struct.  
        //这里不懂为什么定义一个ble_gatts_value_t 结构体,
		//下面把这个结构体内容赋值给hvx_params 然后发送,为什么不直接赋值给hvx_params ?
        memset(&gatts_value, 0, sizeof(gatts_value));
      
        gatts_value.len     = sizeof(uint8_t);
        gatts_value.offset  = 0;
        gatts_value.p_value = &battery_level;

        // Update database.
        err_code = sd_ble_gatts_value_set(p_bas->conn_handle,
                                          p_bas->battery_level_handles.value_handle,
                                          &gatts_value);
        if (err_code == NRF_SUCCESS)
        {
            // Save new battery value.
            p_bas->battery_level_last = battery_level;
        }
        else
        {
            return err_code;
        }

        // Send value if connected and notifying.
        if ((p_bas->conn_handle != BLE_CONN_HANDLE_INVALID) && p_bas->is_notification_supported)
        {
            ble_gatts_hvx_params_t hvx_params;

            memset(&hvx_params, 0, sizeof(hvx_params));

            hvx_params.handle = p_bas->battery_level_handles.value_handle;
            hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
            hvx_params.offset = gatts_value.offset;
            hvx_params.p_len  = &gatts_value.len;
            hvx_params.p_data = gatts_value.p_value;

            err_code = sd_ble_gatts_hvx(p_bas->conn_handle, &hvx_params);
        }
        else
        {
            err_code = NRF_ERROR_INVALID_STATE;
        }
    }

    return err_code;
}

连接参数更新另外开一片再讲,至此蓝牙心电工程基本分析完。

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值