对于我这种之前完全不懂蓝牙,接触学习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,
®_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;
}
连接参数更新另外开一片再讲,至此蓝牙心电工程基本分析完。