SDK 事件处理机制
书写规则:
-- 表示调用关系
<- 表示赋值关系
--> 表示执行步骤为顺序执行
简写:
SD : SoftDevice
蓝牙事件处理:
init:
ble_stack_init -- softdevice_ble_evt_handler_set -- ble_evt_dispatch (m_ble_evt_handler <- ble_evt_dispatch)
execute:
SWI2_IRQHandler(1) -- SD_EVT_IRQHandler -- SOFTDEVICE_EVT_IRQHandler -- intern_softdevice_events_execute -- m_ble_evt_handler
系统事件处理:
init:
ble_stack_init -- softdevice_sys_evt_handler_set -- sys_evt_handler (m_sys_evt_handler <- sys_evt_handler)
execute:
SWI2_IRQHandler(1) -- SD_EVT_IRQHandler -- SOFTDEVICE_EVT_IRQHandler -- intern_softdevice_events_execute -- m_sys_evt_handler
上面可以看到这两个主要事件处理都是被 intern_softdevice_events_execute 触发的,观察程序(2)可以看到同样被这个函数触发的还有 m_ant_evt_handler(3),它并非低功耗蓝牙,目前并未涉及过.
m_ble_evt_handler 的主要工作是将事件派发给实际组件。而 m_sys_evt_handler 也大同小异。
m_ble_evt_handler 的常用组件:
ble_conn_state_on_ble_evt
pm_on_ble_evt
ble_db_discovery_on_ble_evt
ble_conn_params_on_ble_evt
ble_advertising_on_ble_evt
on_ble_evt (自定义的处理)
m_sys_evt_handler 则负责系统内部工作事件处理 ,常用的组件有:
fs_sys_event_handler (flash)
ble_advertising_on_sys_evt (处理内容也和flash有关)
(1) SWI: Software Interrupts,即软件中断,是NRF51芯片为软件中断预留的一个中断。
(2) 取自 SDK12.3\components\softdevice\common\softdevice_handler\softdevice_handler.c
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;
}
#if NRF_MODULE_ENABLED(CLOCK)
bool no_more_soc_evts = false;
#else
bool no_more_soc_evts = (m_sys_evt_handler == NULL);
#endif
#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)
{
if (m_suspended)
{
// Cancel pulling next event if event handler was suspended by user.
return;
}
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.
#if (NRF_MODULE_ENABLED(CLOCK) && defined(SOFTDEVICE_PRESENT))
nrf_drv_clock_on_soc_event(evt_id);
if (m_sys_evt_handler)
{
m_sys_evt_handler(evt_id);
}
#else
m_sys_evt_handler(evt_id);
#endif
}
}
#ifdef BLE_STACK_SUPPORT_REQD
// Fetch BLE Events.
if (!no_more_ble_evts)
{
if (m_suspended)
{
// Cancel pulling next event if event handler was suspended by user.
return;
}
// Pull event from stack
uint16_t evt_len = m_ble_evt_buffer_size;
err_code = sd_ble_evt_get(mp_ble_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 *)mp_ble_evt_buffer);
}
}
#endif
#ifdef ANT_STACK_SUPPORT_REQD
// Fetch ANT Events.
if (!no_more_ant_evts)
{
if (m_suspended)
{
// Cancel pulling next event if event handler was suspended by user.
return;
}
// Pull event from stack
err_code = sd_ant_event_get(&m_ant_evt_buffer.channel,
&m_ant_evt_buffer.event,
m_ant_evt_buffer.msg.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(&m_ant_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
}
}
}
(3) m_ant_evt_handler 是ANT事件处理。ANT不同于蓝牙,是另一种无线传输协议,它主要运用在运动领域,而低功耗蓝牙则是最近才进入运动领域的,所以现在运动领域中最普及的无线传输协议还是ANT。
软件定时器事件:
init:
APP_TIMER_INIT/APP_TIMER_APPSH_INIT(4) --> app_timer_create --> app_timer_start
execute:
SWI0_IRQHandler -- SWI_IRQHandler -- timer_list_handler
(1) 使用 scheduler 。
可以看出实际上整个系统的事件处理机制,其底层是 SWI。SWI 是中断向量表中的一个中断(2)。 nrf51 的 SWI 有六个,即 SWI0-SWI5(1)。SDK12.3的协议栈只使用了前三个,分别是SWI0(软件定时器)、SWI1(无线电)和SWI2(系统事件)(4)。如果要了解 SWI 在中断中的位置,那么需要知道它的优先级。SWI0的优先级设置可是在SDK的程序发现,它的优先级为最低优先级(3)。其余优先级的设置则找不到。但是在NORDIC的官方文档(5)中关于SWI的描述最后部分可以推测SWI2 的优先级为软件可设的最高优先级(第二高优先级);SWI1为最高优先级(6)。至于向量表中的其他常用硬件中断,比如定时器中断,串口中断等等,它们的优先级可以在初始化该外设时自由设置,也可以使用SDK中的默认配置,一般情况下该配置为3(也就是contex_m0的第四高优先级,其实就是最低优先级APP_IRQ_PRIORITY_LOWEST/_PRIO_APP_LOWEST)(9)。
(1) 《nrf51 reference manual v3.0.1》第三十三章,第二小节。
(2) 取自 SDK12.3\examples\ble_peripheral\ble_app_ancs_c\pca10028\s130\arm5_no_packs\RTE\Device\nRF51422_xxAC\arm_startup_nrf51.s
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler
DCD NMI_Handler
DCD HardFault_Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD PendSV_Handler
DCD SysTick_Handler
; External Interrupts
DCD POWER_CLOCK_IRQHandler
DCD RADIO_IRQHandler
DCD UART0_IRQHandler
DCD SPI0_TWI0_IRQHandler
DCD SPI1_TWI1_IRQHandler
DCD 0 ; Reserved
DCD GPIOTE_IRQHandler
DCD ADC_IRQHandler
DCD TIMER0_IRQHandler
DCD TIMER1_IRQHandler
DCD TIMER2_IRQHandler
DCD RTC0_IRQHandler
DCD TEMP_IRQHandler
DCD RNG_IRQHandler
DCD ECB_IRQHandler
DCD CCM_AAR_IRQHandler
DCD WDT_IRQHandler
DCD RTC1_IRQHandler
DCD QDEC_IRQHandler
DCD LPCOMP_IRQHandler
DCD SWI0_IRQHandler
DCD SWI1_IRQHandler
DCD SWI2_IRQHandler
DCD SWI3_IRQHandler
DCD SWI4_IRQHandler
DCD SWI5_IRQHandler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
__Vectors_End
(3) SDK12.3\components\libraries\timer\app_timer.c 第五十行和第九百二十七行
#define SWI_IRQ_PRI APP_IRQ_PRIORITY_LOWEST /**< Priority of the SWI interrupt (used for updating the timer list). */
uint32_t app_timer_init(uint32_t prescaler,
uint8_t op_queue_size,
void * p_buffer,
app_timer_evt_schedule_func_t evt_schedule_func)
{
// Check that buffer is correctly aligned
if (!is_word_aligned(p_buffer))
{
return NRF_ERROR_INVALID_PARAM;
}
// Check for NULL buffer
if (p_buffer == NULL)
{
return NRF_ERROR_INVALID_PARAM;
}
// Stop RTC to prevent any running timers from expiring (in case of reinitialization)
rtc1_stop();
m_evt_schedule_func = evt_schedule_func;
// Initialize operation queue
m_op_queue.first = 0;
m_op_queue.last = 0;
m_op_queue.size = op_queue_size;
m_op_queue.p_user_op_queue = p_buffer;
mp_timer_id_head = NULL;
m_ticks_elapsed_q_read_ind = 0;
m_ticks_elapsed_q_write_ind = 0;
#if APP_TIMER_WITH_PROFILER
m_max_user_op_queue_utilization = 0;
#endif
NVIC_ClearPendingIRQ(SWI_IRQn);
NVIC_SetPriority(SWI_IRQn, SWI_IRQ_PRI);
NVIC_EnableIRQ(SWI_IRQn);
rtc1_init(prescaler);
m_ticks_latest = rtc1_counter_get();
return NRF_SUCCESS;
}
(4) SDK12.3\components\libraries\timer\app_timer.c 第六十五行
SDK12.3\components\softdevice\s130\headers\nrf_soc.h 第七十九行和第七十七行
#define SWI_IRQHandler SWI0_IRQHandler
#define RADIO_NOTIFICATION_IRQHandler (SWI1_IRQHandler) /**< The radio notification IRQ handler. */
#define SD_EVT_IRQHandler (SWI2_IRQHandler) /**< SoftDevice Event IRQ handler. Used for both protocol events and SoC events. */
(5) 指的是 NORDIC/Support&Communlty/DOCUMENTATION
(6) NORDIC/Support&Communlty/DOCUMENTATION/Software Development Kit > nRF5 SDK > nRF5 SDK v12.3.0 > Hardware Drivers > SWI 最后一部分 Usage with a SoftDevice
Be careful when specifiying APP_IRQ_PRIORITY_HIGH as SWI priority. Long interrupt routines with high priority might affect the proper operation of the SoftDevice.
这里说如果有一个很长的 APP_IRQ_PRIORITY_HIGH 优先级的应用程序,会影响软件协议栈进行适当操作。
而软件协议栈的职能是实现无线电协议(7),在SDK源码中可以看到如果使用协议栈,那么APP最高优先级为cortex_m0的第二高优先级(8)。既然说APP可能会影响软件协议栈进行适当操作,那么推测软件协议栈处理程序的优先级为 APP_IRQ_PRIORITY_HIGH 是合理的。另外,cortex_m0的第一高优先级只能提供给软件协议栈,它据此实现无线电协议。
(7) NORDIC/Support&Communlty/DOCUMENTATION/cortex_m0 > SoftDevice
A SoftDevice is a precompiled and linked binary software implementing a wireless protocol developed by Nordic Semiconductor.
软件协议栈是预编译的,即不可见的,用来实现无线协议。
(8) SDK12.3\components\libraries\util\app_util_platform.h 第六十七行和第九十一行
#if __CORTEX_M == (0x00U)
#define _PRIO_SD_HIGH 0
#define _PRIO_APP_HIGH 1
#define _PRIO_APP_MID 1
#define _PRIO_SD_LOW 2
#define _PRIO_APP_LOW 3
#define _PRIO_APP_LOWEST 3
#define _PRIO_THREAD 4
#elif __CORTEX_M == (0x04U)
#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
#error "No platform defined"
#endif
//lint -save -e113 -e452
/**@brief The interrupt priorities available to the application while the SoftDevice is active. */
typedef enum
{
#ifndef SOFTDEVICE_PRESENT
APP_IRQ_PRIORITY_HIGHEST = _PRIO_SD_HIGH,
#else
APP_IRQ_PRIORITY_HIGHEST = _PRIO_APP_HIGH,
#endif
APP_IRQ_PRIORITY_HIGH = _PRIO_APP_HIGH,
#ifndef SOFTDEVICE_PRESENT
APP_IRQ_PRIORITY_MID = _PRIO_SD_LOW,
#else
APP_IRQ_PRIORITY_MID = _PRIO_APP_MID,
#endif
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;
//lint -restore
(9) SDK12.3
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler
DCD NMI_Handler
DCD HardFault_Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD PendSV_Handler
DCD SysTick_Handler
; External Interrupts
DCD POWER_CLOCK_IRQHandler
DCD RADIO_IRQHandler
DCD UART0_IRQHandler ; #define UART_DEFAULT_CONFIG_IRQ_PRIORITY 3
DCD SPI0_TWI0_IRQHandler
DCD SPI1_TWI1_IRQHandler
DCD 0 ; Reserved
DCD GPIOTE_IRQHandler ; #define GPIOTE_CONFIG_IRQ_PRIORITY 3
DCD ADC_IRQHandler ; #define ADC_CONFIG_IRQ_PRIORITY 3
DCD TIMER0_IRQHandler ; #define TIMER_DEFAULT_CONFIG_IRQ_PRIORITY 3
DCD TIMER1_IRQHandler
DCD TIMER2_IRQHandler
DCD RTC0_IRQHandler
DCD TEMP_IRQHandler
DCD RNG_IRQHandler
DCD ECB_IRQHandler
DCD CCM_AAR_IRQHandler
DCD WDT_IRQHandler ; #define WDT_CONFIG_IRQ_PRIORITY 3
DCD RTC1_IRQHandler
DCD QDEC_IRQHandler
DCD LPCOMP_IRQHandler
DCD SWI0_IRQHandler
DCD SWI1_IRQHandler
DCD SWI2_IRQHandler
DCD SWI3_IRQHandler
DCD SWI4_IRQHandler
DCD SWI5_IRQHandler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
__Vectors_End
上面的资料说明,按照标准配置,我们足以构建出一个优先保证蓝牙工作的系统。在这个系统中,也提供有一个设置可以导致该稳定性崩盘,那就是scheduler,它的作用是在中断产生后将它转移至线程模式,即在main循环中执行。不过,scheduler可以将蓝牙协议栈事件处理和/或定时器事件处理转移至线程模式,如果scheduler仅转移定时器处理,那么从理论上是没有危险的。如果我上述认识都没有错,那这么看来,这整个事件处理系统是首先保证蓝牙无线功能的正常使用的,然而在实际使用中竟然出现蓝牙屡屡崩溃的情况,这实在令人费解。