NRF51822 SDK12.3 事件处理机制

曾实
2023-12-01

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仅转移定时器处理,那么从理论上是没有危险的。如果我上述认识都没有错,那这么看来,这整个事件处理系统是首先保证蓝牙无线功能的正常使用的,然而在实际使用中竟然出现蓝牙屡屡崩溃的情况,这实在令人费解。


转载于:https://www.cnblogs.com/lqy-/p/9647107.html

 类似资料: