nrf51822程序的分析( 二)
接着上篇分析,这次从主函数的协议栈开始分析。
1.3.蓝牙协议栈
1.3.1.协议栈中的变量
蓝牙协议栈开源的部分程序主要放在ble_conn_params.c,softdevice_handler.c等文件中。中间包括一些蓝牙协议栈的变量。
softdevice_handler中会存储一些softdevice相关的变量(整个蓝牙设备)
蓝牙系统主要靠事件驱动进行调度。下面是蓝牙事件的结构体定义:
/**@brief BLE事件头部. */
typedef struct
{
uint16_t evt_id; /**< BLE_<module>_EVT 对应时间编号 */
uint16_t evt_len; /**< 除头部外其他数据的字节长度 */
} ble_evt_hdr_t;
typedef struct
{
ble_evt_hdr_t header; /**< 事件头部*/
union
{
ble_common_evt_t common_evt; /**< 公共时间, evt_id in BLE_EVT_* series. */
ble_gap_evt_t gap_evt; /**< GAP生成事件, evt_id in BLE_GAP_EVT_* series. */
ble_l2cap_evt_t l2cap_evt; /**< L2CAP生成事件, evt_id in BLE_L2CAP_EVT* series. */
ble_gattc_evt_t gattc_evt; /**< GATT客户端生成事件, evt_id in BLE_GATTC_EVT* series.*/
ble_gatts_evt_t gatts_evt; /**< GATT服务器生成时间, evt_id in BLE_GATTS_EVT* series.*/
} evt;
} ble_evt_t;
前一部分是事件的头部,用来标识是具体哪种类型的事件。后面是用共用体表示的具体事件的值。
1.3.2.协议栈的初始化
主函数中调用ble_stack_init对蓝牙进行初始化
static void ble_stack_init(void)
{
uint32_t err_code;
// 初始化softdevice
SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false);
//使能协议栈
err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
APP_ERROR_CHECK(err_code);
// 注册SoftDevice处理模块
err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
APP_ERROR_CHECK(err_code);
}
BLE协议栈的主要工作为:
1>.初始化蓝牙协议栈的事件处理
2>.绑定ble事件调度函数
3>.绑定sys事件调度函数。
先分析初始化代码。EVT_BUFFER作为存储传入事件缓存传入蓝牙的协议栈内。函数内确定一下事件缓冲区的指向和大小,确定一下回调函数softdevice_evt_schedule,最后进行使能和开中断。其中sd_softdevice_enable和sd_nvic_EnableIRQ为协议栈中不开源的代码,只需要传入对应参数即可。
#define SOFTDEVICE_HANDLER_INIT(CLOCK_SOURCE, \
USE_SCHEDULER) \
do \
{ \
static uint32_t EVT_BUFFER[CEIL_DIV(MAX( \
MAX(BLE_STACK_EVT_MSG_BUF_SIZE, \
ANT_STACK_EVT_STRUCT_SIZE), \
SYS_EVT_MSG_BUF_SIZE \
), \
sizeof(uint32_t))]; \
uint32_t ERR_CODE; \
ERR_CODE = softdevice_handler_init((CLOCK_SOURCE), \
EVT_BUFFER, \
sizeof(EVT_BUFFER), \
(USE_SCHEDULER) ? softdevice_evt_schedule : NULL);\
APP_ERROR_CHECK(ERR_CODE); \
} while (0)
uint32_t softdevice_handler_init(nrf_clock_lfclksrc_t clock_source,
void * p_evt_buffer,
uint16_t evt_buffer_size,
softdevice_evt_schedule_func_t evt_schedule_func)
{
uint32_t err_code;
// 保存参数
#if defined (BLE_STACK_SUPPORT_REQD) || defined (ANT_STACK_SUPPORT_REQD)
if (p_evt_buffer == NULL)
{
return NRF_ERROR_INVALID_PARAM;
}
if (!is_word_aligned(p_evt_buffer))
{
return NRF_ERROR_INVALID_PARAM;
}
m_evt_buffer = (uint8_t *)p_evt_buffer; //现在使用蓝牙协议栈
#else
//如果不在蓝牙协议站或者ANT协议栈下p_evt_buffer不是必须的
UNUSED_PARAMETER(p_evt_buffer);
#endif
#if defined (BLE_STACK_SUPPORT_REQD)
m_ble_evt_buffer_size = evt_buffer_size;
#else
UNUSED_PARAMETER(evt_buffer_size);
#endif
m_evt_schedule_func = evt_schedule_func;
// 初始化
err_code = sd_softdevice_enable(clock_source, softdevice_assertion_handler);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
m_softdevice_enabled = true;
// 使能蓝牙中断(优先级早就被栈设置好).
return sd_nvic_EnableIRQ(SWI2_IRQn);
}
softdevice_evt_schedule回调函数只有在SWI2_IRQHandler中断发生才被使用。SWI2_IRQHandler中断函数和在开启调度后发送事件的回调函数softdevice_evt_get最终调用的都是intern_softdevice_events_execute函数。
void intern_softdevice_events_execute(void)
{
if (!m_softdevice_enabled)
return;
bool no_more_soc_evts = (m_sys_evt_handler == NULL); //ble_evt_dispatch
#ifdef BLE_STACK_SUPPORT_REQD
bool no_more_ble_evts = (m_ble_evt_handler == NULL); //sys_evt_dispatch
#endif
#ifdef ANT_STACK_SUPPORT_REQD
bool no_more_ant_evts = (m_ant_evt_handler == NULL);
#endif
for (;;)
{
uint32_t err_code;
if (!no_more_soc_evts) //初始化中绑定了sys_evt_dispatch,因此这必进
{
uint32_t evt_id;
//从SOC上得到事件
err_code = sd_evt_get(&evt_id);
if (err_code == NRF_ERROR_NOT_FOUND)
{
no_more_soc_evts = true;
}
else if (err_code != NRF_SUCCESS)
{
APP_ERROR_HANDLER(err_code);
}
else
{
m_sys_evt_handler(evt_id); //调用应用上的SOC事件函数sys_evt_dispatch
}
}
#ifdef BLE_STACK_SUPPORT_REQD
if (!no_more_ble_evts) // 获得BLE事件
{
// 从协议栈中得到事件
uint16_t evt_len = m_ble_evt_buffer_size;
err_code = sd_ble_evt_get(m_evt_buffer, &evt_len);
if (err_code == NRF_ERROR_NOT_FOUND)
{
no_more_ble_evts = true;
}
else if (err_code != NRF_SUCCESS)
{
APP_ERROR_HANDLER(err_code);
}
else
{
//调用应用上的BLE协议栈函数ble_evt_dispatch
m_ble_evt_handler((ble_evt_t *)m_evt_buffer);
}
}
#endif
#ifdef ANT_STACK_SUPPORT_REQD
if (!no_more_ant_evts)// 获得ANT事件
{
//从协议栈上获取事件
err_code = sd_ant_event_get(&((ant_evt_t *)m_evt_buffer)->channel,
&((ant_evt_t *)m_evt_buffer)->event,
((ant_evt_t *)m_evt_buffer)->evt_buffer);
if (err_code == NRF_ERROR_NOT_FOUND)
{
no_more_ant_evts = true;
}
else if (err_code != NRF_SUCCESS)
{
APP_ERROR_HANDLER(err_code);
}
else
{
m_ant_evt_handler((ant_evt_t *)m_evt_buffer); //回调
}
}
#endif
if (no_more_soc_evts) //这里no_more_soc_evts=1后才进入
{
#if defined(ANT_STACK_SUPPORT_REQD) && defined(BLE_STACK_SUPPORT_REQD)
if (no_more_ble_evts && no_more_ant_evts)
{
break;
}
#elif defined(BLE_STACK_SUPPORT_REQD)
if (no_more_ble_evts)
{
break;
}
#elif defined(ANT_STACK_SUPPORT_REQD)
if (no_more_ant_evts)
{
break;
}
#else
break;
#endif
}
}
}
m_sys_evt_handler ,m_ble_evt_handler 在协议栈初始化的时候被赋值,实际指向sys_evt_dispatch,ble_evt_dispatch。ANT协议栈为被用到,忽略。这个函数是获取协议栈中的事件并轮询调度协议栈的事件处理函数,直到事件队列中没有事件,再跳出for循环。
softdevice_ble_evt_handler_set和softdevice_sys_evt_handler_set函数是用来确定蓝牙协议栈的事件调度函数为ble_evt_dispatch和sys_evt_dispatch的。下面具体分析ble_evt_dispatch。
1.3.3.协议栈事件调度(ble_evt_dispatch)
调度的代码放在应用层。虽然用户可以修改,但整体还是基于一个模板来的。
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
on_ble_evt(p_ble_evt);
ble_conn_params_on_ble_evt(p_ble_evt);
ble_lbs_on_ble_evt(&m_lbs, p_ble_evt);
}
在协议栈的调度函数中,主要轮询3个部分:
1>.蓝牙事件
对事件的ID号进行区分不同的事件,调用不同的操作。
比如说GAP连接事件进行绑定接口;断开时间负责解绑;安全参数请求调用sd_ble_gap_sec_params_reply;超时睡眠等。这里一般的事件和事件处理都可以看做是模板,我们可以在处理里面加写自己的指示之类的处理。
static void on_ble_evt(ble_evt_t * p_ble_evt)
{
uint32_t err_code;
static ble_gap_evt_auth_status_t m_auth_status;
ble_gap_enc_info_t * p_enc_info;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
nrf_gpio_pin_set(CONNECTED_LED_PIN_NO);
nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
err_code = app_button_enable();
APP_ERROR_CHECK(err_code);
break;
case BLE_GAP_EVT_DISCONNECTED:
nrf_gpio_pin_clear(CONNECTED_LED_PIN_NO);
m_conn_handle = BLE_CONN_HANDLE_INVALID;
err_code = app_button_disable();
APP_ERROR_CHECK(err_code);
advertising_start();
break;
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
BLE_GAP_SEC_STATUS_SUCCESS,
&m_sec_params);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTS_EVT_SYS_ATTR_MISSING:
err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0);
APP_ERROR_CHECK(err_code);
break;
case BLE_GAP_EVT_AUTH_STATUS:
m_auth_status = p_ble_evt->evt.gap_evt.params.auth_status;
break;
case BLE_GAP_EVT_SEC_INFO_REQUEST:
p_enc_info = &m_auth_status.periph_keys.enc_info;
if (p_enc_info->div == p_ble_evt->evt.gap_evt.params.sec_info_request.div)
{
err_code = sd_ble_gap_sec_info_reply(m_conn_handle, p_enc_info, NULL);
APP_ERROR_CHECK(err_code);
}
else
{
// No keys found for this device
err_code = sd_ble_gap_sec_info_reply(m_conn_handle, NULL, NULL);
APP_ERROR_CHECK(err_code);
}
break;
case BLE_GAP_EVT_TIMEOUT:
if (p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_ADVERTISEMENT)
{
nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);
// Configure buttons with sense level low as wakeup source.
nrf_gpio_cfg_sense_input(WAKEUP_BUTTON_PIN,
BUTTON_PULL,
NRF_GPIO_PIN_SENSE_LOW);
// Go to system-off mode (this function will not return; wakeup will cause a reset)
err_code = sd_power_system_off();
APP_ERROR_CHECK(err_code);
}
break;
default:
// No implementation needed.
break;
}
}
2>.协议栈连接参数及服务事件。
这里感觉和前面有一部重复???同时和完全用户自定义的ble_lbs_on_ble_evt事件处理也有一致的地方。ble_lbs_on_ble_evt在后续应用中再补充。
void ble_conn_params_on_ble_evt(ble_evt_t * p_ble_evt)
{
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_ble_evt);
break;
case BLE_GATTS_EVT_WRITE:
on_write(p_ble_evt);
break;
case BLE_GAP_EVT_CONN_PARAM_UPDATE:
on_conn_params_update(p_ble_evt);
break;
default:
break;
}
}
static void on_connect(ble_evt_t * p_ble_evt)
{
// 保存连接参数
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
m_current_conn_params = p_ble_evt->evt.gap_evt.params.connected.conn_params;
m_update_count = 0;
//检查是否处理连接协商(初始化conn_params_init后默认为BLE_GATT_HANDLE_INVALID)
if (m_conn_params_config.start_on_notify_cccd_handle == BLE_GATT_HANDLE_INVALID)
{
conn_params_negotiation();
}
}
static void on_disconnect(ble_evt_t * p_ble_evt)
{
uint32_t err_code;
m_conn_handle = BLE_CONN_HANDLE_INVALID;
m_update_count = 0; // 连接参数更新需要发生在每一个连接上
err_code = app_timer_stop(m_conn_params_timer_id); //停止定时器
if ((err_code != NRF_SUCCESS) && (m_conn_params_config.error_handler != NULL))
{
m_conn_params_config.error_handler(err_code); //错误处理
}
}
static void on_write(ble_evt_t * p_ble_evt)
{
ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if ( // 检查cccd(描述符)是否正确
(p_evt_write->handle == m_conn_params_config.start_on_notify_cccd_handle)
&& (p_evt_write->len == 2)
)
{
// 检查是否是'start notification
if (ble_srv_is_notification_enabled(p_evt_write->data))
{
conn_params_negotiation(); //如果必要进行连接参数negotiation
}
else
{
uint32_t err_code;
err_code = app_timer_stop(m_conn_params_timer_id); // 终止定时器
if ((err_code != NRF_SUCCESS) &&
(m_conn_params_config.error_handler != NULL))
{
m_conn_params_config.error_handler(err_code);
}
}
}
}
static void on_conn_params_update(ble_evt_t * p_ble_evt)
{
//拷贝参数
m_current_conn_params = p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params;
conn_params_negotiation();
}
中间大部分函数逻辑都很简单。中间有个函数conn_params_negotiation。这个函数是对比当前连接参数m_current_conn_params的最大连接间隔时间是否在参考连接参数m_preferred_conn_params的最大和最小间隔时间内,在范围内通知成功,否则通知失败。并执行m_conn_params_config函数。这里具体的参数连接后面接着说。
1.3.4.系统事件调度(sys_evt_dispatch)
系统事件的调度函数里面调用了pstorage_sys_event_handler函数,该函数用来处理Flash访问结果事件放在 pstorage_platform.h文件中。这部分需要初始化pstorage_init,整个工程中未调用,因此这部分程序不处理if (m_cmd_queue.flash_access == true不成立)。如果后面有这部分的再回来解释。
1.4.连接参数
1.4.1.连接参数初始化
这部分是关于连接参数模块的初始化。这部分结构体定义如下:
typedef struct
{
ble_gap_conn_params_t * p_conn_params; /**< 指向应用程序所需的连接参数的指针。当调用ble_conn_params_init,如果这个参数被设置为null,连接参数将从主机中获得。 */
uint32_t first_conn_params_update_delay; /**<表示从发起事件 (连接或启动通知) 到第一次sd_ble_gap_conn_param_update被调用的时间(定时器ticks的值). */
uint32_t next_conn_params_update_delay; /**< 在每次调用sd_ble_gap_conn_param_update后的第一次时间(在定时器滴答数)。按蓝牙规范4版推荐值30秒。 */
uint8_t max_conn_params_update_count; /**< 连接失败前最大的尝试次数 */
uint16_t start_on_notify_cccd_handle; /**< 如果程序要用来开始通知事件时,这个设置相应的CCCD句柄。 如果程序要开始连接事件,则设置为BLE_GATT_HANDLE_INVALID*/
bool disconnect_on_fail; /**< 如果连接参数更新失败导致自动断开,则设置为true,否则设置为false。 */
ble_conn_params_evt_handler_t evt_handler; /**< 调用事件处理程序来处理连接参数中的事件。*/
ble_srv_error_handler_t error_handler; /**< 出错处理句柄 */
} ble_conn_params_init_t;
下面是初始化过程,在调用ble_conn_params_init初始化
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 = BLE_GATT_HANDLE_INVALID;
cp_init.disconnect_on_fail = false;
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);
}
uint32_t ble_conn_params_init(const ble_conn_params_init_t * p_init)
{
uint32_t err_code;
m_conn_params_config = *p_init;
m_change_param = false;
if (p_init->p_conn_params != NULL)
{
m_preferred_conn_params = *p_init->p_conn_params;
//设置协议栈上的连接参数
err_code = sd_ble_gap_ppcp_set(&m_preferred_conn_params);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
else
{
// 从协议栈中获得连接参数
err_code = sd_ble_gap_ppcp_get(&m_preferred_conn_params);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
m_conn_handle = BLE_CONN_HANDLE_INVALID;
m_update_count = 0;
return app_timer_create(&m_conn_params_timer_id,
APP_TIMER_MODE_SINGLE_SHOT,
update_timeout_handler);
}
因为前面定义的p_conn_params为NULL,因此这里从协议栈中读取数据。此外这里开启了一个定时器来处理连接超时,若定时器到时之后会回调update_timeout_handler函数。
static void update_timeout_handler(void * p_context)
{
UNUSED_PARAMETER(p_context);
if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
{
// 检测是否到达最大尝试次数
m_update_count++;
if (m_update_count <= m_conn_params_config.max_conn_params_update_count)
{
uint32_t err_code;
// 参数不正确, 发送连接更新请求
err_code = sd_ble_gap_conn_param_update(m_conn_handle, &m_preferred_conn_params);
if ((err_code != NRF_SUCCESS) && (m_conn_params_config.error_handler != NULL))
{
m_conn_params_config.error_handler(err_code);
}
}
else
{
m_update_count = 0;
// 连接失败,如果没有被配置,则自动断开
if (m_conn_params_config.disconnect_on_fail)
{
uint32_t err_code;
err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
if ((err_code != NRF_SUCCESS) && (m_conn_params_config.error_handler != NULL))
{
m_conn_params_config.error_handler(err_code);
}
}
// 通知应用连接失败
if (m_conn_params_config.evt_handler != NULL)
{
ble_conn_params_evt_t evt;
evt.evt_type = BLE_CONN_PARAMS_EVT_FAILED;
m_conn_params_config.evt_handler(&evt);
}
}
}
}
此外前面初始化的时候绑定了一个回调函数on_conn_params_evt来处理连接参数中的事件。该函数处理连接参数失败时间,则会断开连接。
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);
}
}
1.4.2.
前面的系统调度中很多事件处理的过程都调用了这个函数。该函数用来判断连接是否成功,不成功再次启动定时器,成功则通知应用。
static void conn_params_negotiation(void)
{
if (!is_conn_params_ok(&m_current_conn_params))
{
uint32_t err_code;
uint32_t timeout_ticks;
if (m_change_param)
{
if (m_conn_params_config.evt_handler != NULL) // 通知应用过程失败
{
ble_conn_params_evt_t evt;
evt.evt_type = BLE_CONN_PARAMS_EVT_FAILED;
m_conn_params_config.evt_handler(&evt);
}
}
else
{//m_change_param == NULL
if (m_update_count == 0)
{
// 第一个连接更新
timeout_ticks = m_conn_params_config.first_conn_params_update_delay;
}
else
{
timeout_ticks = m_conn_params_config.next_conn_params_update_delay;
}
err_code = app_timer_start(m_conn_params_timer_id, timeout_ticks, NULL);
if ((err_code != NRF_SUCCESS) && (m_conn_params_config.error_handler != NULL))
{
m_conn_params_config.error_handler(err_code);
}
}
}
else
{
// 通知应用过程成功执行
if (m_conn_params_config.evt_handler != NULL)
{
ble_conn_params_evt_t evt;
evt.evt_type = BLE_CONN_PARAMS_EVT_SUCCEEDED;
m_conn_params_config.evt_handler(&evt);
}
}
m_change_param = false;
}
1.5.GAP及安全参数
gap部分程序涉及到的比较少,只有一段初始化。
大概意思是设定一下安全模式,设置为(1,1),绑定一下蓝牙的名字,配置并设置一下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);
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
gap_conn_params.slave_latency = SLAVE_LATENCY;
gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
APP_ERROR_CHECK(err_code);
}
安全参数初始化部分的密码也就是赋值一下m_sec_params结构体。
static void sec_params_init(void)
{
m_sec_params.timeout = SEC_PARAM_TIMEOUT;
m_sec_params.bond = SEC_PARAM_BOND;
m_sec_params.mitm = SEC_PARAM_MITM;
m_sec_params.io_caps = SEC_PARAM_IO_CAPABILITIES;
m_sec_params.oob = SEC_PARAM_OOB;
m_sec_params.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
m_sec_params.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
}