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僅轉移定時器處理,那麼從理論上是沒有危險的。若是我上述認識都沒有錯,那這麼看來,這整個事件處理系統是首先保證藍牙無線功能的正常使用的,然而在實際使用中居然出現藍牙屢屢崩潰的狀況,這實在使人費解。
app