SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机、32位ARM Cortex-M0处理器、128kB Flash存储器、以及丰富的数字接口。SYD8801片上集成了Balun无需阻抗匹配网络、高效率DCDC降压转换器,适合用于可穿戴、物联网设备等。具体可咨询:http://www.sydtek.com/
nrf51822学习之样例从广播机制窥探整个工程的运行
原先也对样例有过一些分析,可以在这里看到:http://blog.csdn.net/chengdong1314/article/details/51531504
今天的目的是更加深层次的探究样例,当然即使这样还是不能够说有多深入,只是比以前更加深入一点罢了。这次主要说的是softdevice的实现机制。
在softdevice初始化中初始化了软件中断2:
return sd_nvic_EnableIRQ(SWI2_IRQn);
所以应该BLE事件的回调函数是应该在SWI2_IRQn中断里实现的。
此外softdevice初始化函数还注册了BLE事件的回调函数和系统时间的回调函数:
// Register with the SoftDevice handler module for BLE events.
err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
APP_ERROR_CHECK(err_code);
// Register with the SoftDevice handler module for BLE events.
err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
APP_ERROR_CHECK(err_code);
广播初始化如下:
/**@brief Function for initializing the Advertising functionality.
*
* @details Encodes the required advertising data and passes it to the stack.
* Also builds a structure to be passed to the stack when starting advertising.
*/
static void advertising_init(void)
{
uint32_t err_code;
ble_advdata_t advdata;
uint8_t flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
// YOUR_JOB: Use UUIDs for service(s) used in your application.
ble_uuid_t adv_uuids[] = {{BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}};
// Build and set advertising data
memset(&advdata, 0, sizeof(advdata));
advdata.name_type = BLE_ADVDATA_FULL_NAME;
advdata.include_appearance = true;
advdata.flags.size = sizeof(flags);
advdata.flags.p_data = &flags;
advdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]);
advdata.uuids_complete.p_uuids = adv_uuids;
err_code = ble_advdata_set(&advdata, NULL);
APP_ERROR_CHECK(err_code);
}
其中flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;定义了广播的属性:
#define BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE (BLE_GAP_ADV_FLAG_LE_LIMITED_DISC_MODE | BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED) /**< LE Limited Discoverable Mode, BR/EDR not supported. */
adv_uuids[] = {{BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}};定义了向外广播的UUID,广播名字在之前的gap_params_init();函数中定义
下面在看到的是广播初始化函数:
/**@brief Function for starting advertising.
*/
static void advertising_start(void)
{
uint32_t err_code;
ble_gap_adv_params_t adv_params;
// Start advertising
memset(&adv_params, 0, sizeof(adv_params));
adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND;
adv_params.p_peer_addr = NULL;
adv_params.fp = BLE_GAP_ADV_FP_ANY;
adv_params.interval = APP_ADV_INTERVAL;
adv_params.timeout = APP_ADV_TIMEOUT_IN_SECONDS;
err_code = sd_ble_gap_adv_start(&adv_params);
APP_ERROR_CHECK(err_code);
nrf_gpio_pin_set(ADVERTISING_LED_PIN_NO);
}
这里调用了关键的函数err_code = sd_ble_gap_adv_start(&adv_params);这样以后广播就开始了,广播开始之后就是主循环:
for (;;)
{
app_sched_execute();
power_manage();
}
并没有再次涉及协议栈的问题了。
实验可知单用手机连接上这个蓝牙的时候ADVERTISING_LED_PIN_NO这个灯会熄灭而另外一个灯会点亮,说明协议栈在任何时候都是在跑的,这时候我们就从ADVERTISING_LED_PIN_NO去找,找到了这个函数中有这样的一句话nrf_gpio_pin_clear(ADVERTISING_LED_PIN_NO);:
/**@brief Function for handling the Application's BLE Stack events.
*
* @param[in] p_ble_evt Bluetooth stack event.
*/
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;
/* YOUR_JOB: Uncomment this part if you are using the app_button module to handle button
events (assuming that the button events are only needed in connected
state). If this is uncommented out here,
1. Make sure that app_button_disable() is called when handling
BLE_GAP_EVT_DISCONNECTED below.
2. Make sure the app_button module is initialized.
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;
/* YOUR_JOB: Uncomment this part if you are using the app_button module to handle button
events. This should be done to save power when not connected
to a peer.
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;
}
}
说明当连接上蓝牙的时候函数执行到了这里,也就是说协议栈会跳到这里来执行,再次深究可以知道on_ble_evt函数在我们之前注册的BLE事件回调函数中使用如下:
/**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler.
*
* @details This function is called from the scheduler in the main loop 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)
{
on_ble_evt(p_ble_evt);
ble_conn_params_on_ble_evt(p_ble_evt);
/*
YOUR_JOB: Add service ble_evt handlers calls here, like, for example:
ble_bas_on_ble_evt(&m_bas, p_ble_evt);
*/
}
那这时候我们就要问了协议栈是怎么调用这个函数的:
再次回到注册语句: err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);其中softdevice_ble_evt_handler_set函数如下:
uint32_t softdevice_ble_evt_handler_set(ble_evt_handler_t ble_evt_handler)
{
if (ble_evt_handler == NULL)
{
return NRF_ERROR_NULL;
}
m_ble_evt_handler = ble_evt_handler;
return NRF_SUCCESS;
}
这里转而给全局变量m_ble_evt_handler赋值,也就是说协议栈需要的回调函数是m_ble_evt_handler,它就是我们的回调函数,全局搜索m_ble_evt_handler可以看到在void intern_softdevice_events_execute(void)函数里调用了这个变量: m_ble_evt_handler((ble_evt_t *)m_evt_buffer);
整个函数如下:
void intern_softdevice_events_execute(void)
{
if (!m_softdevice_enabled)
{
// SoftDevice not enabled. This can be possible if the SoftDevice was enabled by the
// application without using this module's API (i.e softdevice_handler_init)
return;
}
bool no_more_soc_evts = (m_sys_evt_handler == NULL);
#ifdef BLE_STACK_SUPPORT_REQD
bool no_more_ble_evts = (m_ble_evt_handler == NULL);
#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)
{
uint32_t evt_id;
// Pull event from 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
{
// Call application's SOC event handler.
m_sys_evt_handler(evt_id);
}
}
#ifdef BLE_STACK_SUPPORT_REQD
// Fetch BLE Events.
if (!no_more_ble_evts)
{
// Pull event from stack
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
{
// Call application's BLE stack event handler.
m_ble_evt_handler((ble_evt_t *)m_evt_buffer);
}
}
#endif
#ifdef ANT_STACK_SUPPORT_REQD
// Fetch ANT Events.
if (!no_more_ant_evts)
{
// Pull event from stack
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
{
// Call application's ANT stack event handler.
m_ant_evt_handler((ant_evt_t *)m_evt_buffer);
}
}
#endif
if (no_more_soc_evts)
{
// There are no remaining System (SOC) events to be fetched from the SoftDevice.
#if defined(ANT_STACK_SUPPORT_REQD) && defined(BLE_STACK_SUPPORT_REQD)
// Check if there are any remaining BLE and ANT events.
if (no_more_ble_evts && no_more_ant_evts)
{
break;
}
#elif defined(BLE_STACK_SUPPORT_REQD)
// Check if there are any remaining BLE events.
if (no_more_ble_evts)
{
break;
}
#elif defined(ANT_STACK_SUPPORT_REQD)
// Check if there are any remaining ANT events.
if (no_more_ant_evts)
{
break;
}
#else
// No need to check for BLE or ANT events since there is no support for BLE and ANT
// required.
break;
#endif
}
}
}
分析一下这个函数可以知道关于BLE事件的就是下面这些内容:
#ifdef BLE_STACK_SUPPORT_REQD
// Fetch BLE Events.
if (!no_more_ble_evts)
{
// Pull event from stack
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
{
// Call application's BLE stack event handler.
m_ble_evt_handler((ble_evt_t *)m_evt_buffer);
}
}
#endif
其中sd_ble_evt_get(m_evt_buffer, &evt_len);函数是获取时候存在BLE事件的函数,现在再次深究函数调用,这一看到void SWI2_IRQHandler(void)函数调用了该函数:intern_softdevice_events_execute();如下:
/**@brief Function for handling the Application's BLE Stack events interrupt.
*
* @details This function is called whenever an event is ready to be pulled.
*/
void SWI2_IRQHandler(void)
{
if (m_evt_schedule_func != NULL)
{
uint32_t err_code = m_evt_schedule_func();
APP_ERROR_CHECK(err_code);
}
else
{
intern_softdevice_events_execute();
}
}
这个就是第二号软件中断的中断服务函数。
到了这里总结如下:
在ble_stack_init -》 SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false); -》 ERR_CODE = softdevice_handler_init((CLOCK_SOURCE), EVT_BUFFER, sizeof(EVT_BUFFER), (USE_SCHEDULER) ? softdevice_evt_schedule : NULL); -》 err_code = sd_softdevice_enable(clock_source, softdevice_assertion_handler);初始化了SOFTDEVICE这里协议栈应该初始化了内部的2.4G无线模块,等,再结合这个表:
就可以判断应该所有为Blocked的都被初始化了,advertising_init();只是设置了广播,advertising_start();把广播打开,最后协议栈应该是进入了后台工作,我们可以看到RTC和TIMERO都是BLOCK也就是这都是给协议栈使用的,从这里可以看出协议栈和主函数之间是靠定时器来切换的,也就相当于操作系统里面的两个任务,从主函数可以看出进入while循环之后就再也没有协议栈的事了,说明协议栈是通过定时器切换的。
最后开看看点灯情况:
在advertising_start点亮ADVERTISING_LED_PIN_NO,如下:
nrf_gpio_pin_set(ADVERTISING_LED_PIN_NO);
当连接的时候关掉ADVERTISING_LED_PIN_NO,同时打开了CONNECTED_LED_PIN_NO,如下:
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;
/* YOUR_JOB: Uncomment this part if you are using the app_button module to handle button
events (assuming that the button events are only needed in connected
state). If this is uncommented out here,
1. Make sure that app_button_disable() is called when handling
BLE_GAP_EVT_DISCONNECTED below.
2. Make sure the app_button module is initialized.
err_code = app_button_enable();
APP_ERROR_CHECK(err_code);
*/
break;
当失去连接的时候再次进行广播也就是打开ADVERTISING_LED_PIN_NO,同时关闭了CONNECTED_LED_PIN_NO,如下:
case BLE_GAP_EVT_DISCONNECTED:
nrf_gpio_pin_clear(CONNECTED_LED_PIN_NO);
m_conn_handle = BLE_CONN_HANDLE_INVALID;
/* YOUR_JOB: Uncomment this part if you are using the app_button module to handle button
events. This should be done to save power when not connected
to a peer.
err_code = app_button_disable();
APP_ERROR_CHECK(err_code);
*/
advertising_start();
break;
这也就是我们所看到的实验现象了
实验代码和现象请看:http://blog.csdn.net/chengdong1314/article/details/51531504