Bluetooth nrf 51822プログラムフレームワークの概要

20278 ワード

この文章はまずテンプレートを提供するフレームワークの部分プログラムを分析します.ここでは、テンプレートのコード(ランプとボタン)を例に挙げます.http://download.csdn.net/download/dfsae/99873181.主関数NRF 51822のフレームワークはイベント駆動フレームワークを採用している.まず主関数から分析する

int main(void)
{
    // Initialize
    leds_init();           //led   ,    
    timers_init();
    gpiote_init();          //     
    buttons_init();
    ble_stack_init();
    scheduler_init();    
    gap_params_init();
    services_init();
    advertising_init();
    conn_params_init();
    sec_params_init();

    // Start execution
    timers_start();
    advertising_start();

    // Enter main loop
    for (;;)
    {
        app_sched_execute();
        power_manage();
    }
}

メイン関数で初期化を行い、タイマーとブロードキャストを再起動し、メインサイクルでタスクスケジューリングと電源管理powerを実現します.manage();
1.1.タイマ
NRF 51822のタイマは、キューによって複数のタイマの管理が行われる.
1.1.1.データ構造
タイマーは主にtimer_に置かれますnode_t構造体構成配列で集中管理を行い、格納する方法はtimers_を参照Initの解析.timer_node_tの構成は以下の通りである.
typedef struct
{
    timer_alloc_state_t         state;    /**<         */
    app_timer_mode_t            mode;     /**<       */
    uint32_t                    ticks_to_expire;  /**<             ticks. */
    uint32_t                    ticks_at_start;   /**<          RTC   . */
    uint32_t                    ticks_first_interval;  /**<          ticks */
    uint32_t                    ticks_periodic_interval; /**<      */
    bool                        is_running;   /**< True    , False  . */
    app_timer_timeout_handler_t p_timeout_handler;   /**<                */
    void *                      p_context;  /**

app_timer.cでは、タイマキューに基づいた追加削除操作が提供される.
1.1.2.初期化関数
メイン関数でtimers_を呼び出すInit実現タイマの初期化
static void timers_init(void)
{
    // Initialize timer module, making it use the scheduler
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, true);
}

#define APP_TIMER_INIT(PRESCALER, MAX_TIMERS, OP_QUEUES_SIZE, USE_SCHEDULER)             \
    do                                                                        \
    {                                                                                 \
        static uint32_t APP_TIMER_BUF[CEIL_DIV(APP_TIMER_BUF_SIZE((MAX_TIMERS),          \
                                       (OP_QUEUES_SIZE) + 1),                     \
                                       sizeof(uint32_t))];                     \
        uint32_t ERR_CODE = app_timer_init((PRESCALER),                          \
                                      (MAX_TIMERS),                          \
                                      (OP_QUEUES_SIZE) + 1,                    \
                                       APP_TIMER_BUF,                            \
                                       (USE_SCHEDULER) ? app_timer_evt_schedule : NULL); \
        APP_ERROR_CHECK(ERR_CODE);                                                     \
    } while (0)

この初期化はapp_を呼び出したtimer.cのapp_timer_Init、同時にUSE_によるSCHEDULERコールバック関数app_を設定するtimer_evt_schedule.
static __INLINE uint32_t app_timer_evt_schedule(app_timer_timeout_handler_t timeout_handler,
                                                void *                      p_context)
{
    app_timer_event_t timer_event;

    timer_event.timeout_handler = timeout_handler;
    timer_event.p_context       = p_context;

    return app_sched_event_put(&timer_event, sizeof(timer_event), app_timer_evt_get);
}


app_timer_evt_scheduleでやった:1>.イベントを生成します.2>.イベントは、イベントスケジュールのAPI(app_sched_event_put)によって送信される.
uint32_t app_timer_init(uint32_t                      prescaler,//    
                        uint8_t                       max_timers,//    
                        uint8_t                       op_queues_size,
                        void *                        p_buffer,
                        app_timer_evt_schedule_func_t evt_schedule_func)
{
    int i;
    //             
    if (!is_word_aligned(p_buffer))
    {
        return NRF_ERROR_INVALID_PARAM;
    }
    if (p_buffer == NULL)//       
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    rtc1_stop(); // RTC                 

    m_evt_schedule_func = evt_schedule_func;//      :app_timer_evt_schedule

    // Initialize timer node array          APP_TIMER_BUF
    m_node_array_size = max_timers;
    mp_nodes          = p_buffer;

    for (i = 0; i < max_timers; i++)
    {
        mp_nodes[i].state      = STATE_FREE;
        mp_nodes[i].is_running = false;
    }

    // Skip timer node array
    p_buffer = &((uint8_t *)p_buffer)[max_timers * sizeof(timer_node_t)];

    // Initialize users array
    m_user_array_size = APP_TIMER_INT_LEVELS;
    mp_users          = p_buffer;

    // Skip user array
    p_buffer = &((uint8_t *)p_buffer)[APP_TIMER_INT_LEVELS * sizeof(timer_user_t)];

    //     operation  
    for (i = 0; i < APP_TIMER_INT_LEVELS; i++)
    {
        timer_user_t * p_user = &mp_users[i];
        p_user->first              = 0;
        p_user->last               = 0;
        p_user->user_op_queue_size = op_queues_size;
        p_user->p_user_op_queue    = p_buffer;

        // Skip operation queue
        p_buffer = &((uint8_t *)p_buffer)[op_queues_size * sizeof(timer_user_op_t)];
    }

    m_timer_id_head             = TIMER_NULL;
    m_ticks_elapsed_q_read_ind  = 0;
    m_ticks_elapsed_q_write_ind = 0;

    NVIC_ClearPendingIRQ(SWI0_IRQn);
    NVIC_SetPriority(SWI0_IRQn, SWI0_IRQ_PRI);
    NVIC_EnableIRQ(SWI0_IRQn);

    rtc1_init(prescaler);
    m_ticks_latest = rtc1_counter_get();
    return NRF_SUCCESS;
}

タイマーの初期化は主に以下の通りである:1>.イベントコールバック関数(ある場合)を設定し、app_をバインドします.timer_evt_schedule関数.2>.割り当て転送数のタイマを初期化し、app_に対応する空間を割り当てるtimer.cでは、タイマシステム全体を管理するために内部変数を定義し、いくつかのパラメータを入力メモリに保存します.メモリの格納は、浅析蓝牙nrf51822程序框架_第1张图片の最初のメモリに各タイマの割り当てステータス(state)と実行ステータス(is_running)が保存され、どちらもtimer_node_t構造体のパラメータ.中間保存timer_user_t構造体のデータの最後のブロックは対応するtimer_を格納するuser_op_t構造体データ.3>.オープン割込みの設定
1.1.2.タイマー関数の作成
最初にタイマの割り当てが完了すると、後続のタイマは使用前にユーザーによってカスタマイズされて割り当てることができます.割り当てはapp_を呼び出すだけですtimer_create関数でいいです.ユーザがこのタイマの動作モードとコールバック関数を入力すると、通常は空きタイマがp_に置かれていることがわかります.timer_idには、ユーザが現在割り当てられているタイマidを返す.プロセス中に対応IDのmp_しか修正されませんでしたnodesの値.たとえば、このインスタンスでbuttonを初期化すると、最後にタイマが割り当てられます.
uint32_t app_timer_create(app_timer_id_t *            p_timer_id,
                          app_timer_mode_t            mode,
                          app_timer_timeout_handler_t timeout_handler)
{
    int i;
    if (mp_nodes == NULL)
    {
        return NRF_ERROR_INVALID_STATE;
    }
    if (timeout_handler == NULL)
    {
        return NRF_ERROR_INVALID_PARAM;
    }
    if (p_timer_id == NULL)
    {
        return NRF_ERROR_INVALID_PARAM;
    }    

    //          
    for (i = 0; i < m_node_array_size; i++)
    {
        if (mp_nodes[i].state == STATE_FREE)
        {
            mp_nodes[i].state             = STATE_ALLOCATED;
            mp_nodes[i].mode              = mode;
            mp_nodes[i].p_timeout_handler = timeout_handler;

            *p_timer_id = i;
            return NRF_SUCCESS;
        }
    }

    return NRF_ERROR_NO_MEM;
}


1.1.3.タイマわりこみ
app_timer.cには、下位RTCを動作させる関数がいくつか用意されています.rtc 1_init-rtc 1_の初期化start-起動タイマrtc 1_stop-タイマーrtc 1_を終了counter_get-タイマのカウント値rtc 1_を取得compare0_set-ゼロオーバーコンパレータRTC 1_の設定IRQHandler-タイマ割り込み処理関数割り込み処理ここから始める.
void RTC1_IRQHandler(void)
{
    //     
    NRF_RTC1->EVENTS_COMPARE[0] = 0;
    NRF_RTC1->EVENTS_COMPARE[1] = 0;
    NRF_RTC1->EVENTS_COMPARE[2] = 0;
    NRF_RTC1->EVENTS_COMPARE[3] = 0;
    NRF_RTC1->EVENTS_TICK       = 0;
    NRF_RTC1->EVENTS_OVRFLW     = 0;
    timer_timeouts_check();//         
}

timer_timeouts_check関数は,設定する対応するアプリケーションが時間のタイマに到達するか否かの検出を担当する.
static void timer_timeouts_check(void)
{
    if (m_timer_id_head != TIMER_NULL)  //         
    {
        app_timer_id_t  timer_id;
        uint32_t        ticks_elapsed;
        uint32_t        ticks_expired;
        //          ticks 0
        ticks_expired = 0;

        // ticks_elapsed(    )      ,              
        ticks_elapsed = ticks_diff_get(rtc1_counter_get(), m_ticks_latest);

        // Auto variable containing the head of timers expiring 
        timer_id = m_timer_id_head;

        //         ticks_elapsed     ticks_expired (    )
        while (timer_id != TIMER_NULL)
        {
            timer_node_t * p_timer;
            p_timer = &mp_nodes[timer_id]; //         

            //          
            if (ticks_elapsed < p_timer->ticks_to_expire)
            {
                break;
            }

            //   ticks_elapsed(    )    expired ticks (    )
            ticks_elapsed -= p_timer->ticks_to_expire;
            ticks_expired += p_timer->ticks_to_expire;

            //         
            timer_id = p_timer->next;

            //  
            timeout_handler_exec(p_timer);
        }

        //    m_ticks_elapsed    ticks      
        if (m_ticks_elapsed_q_read_ind == m_ticks_elapsed_q_write_ind)
        {
            //         。    ticks_expired            
            //  m_ticks_elapsed  (         。)
            //          
            if (++m_ticks_elapsed_q_write_ind == CONTEXT_QUEUE_SIZE_MAX)
            {
                //     .   ,          
                m_ticks_elapsed_q_write_ind = 0;
            }
        }

        //    ticks  .
        m_ticks_elapsed[m_ticks_elapsed_q_write_ind] = ticks_expired;

        timer_list_handler_sched();
    }
}
static void timeout_handler_exec(timer_node_t * p_timer)
{
    if (m_evt_schedule_func != NULL)
    {
        uint32_t err_code = m_evt_schedule_func(p_timer->p_timeout_handler, 
                                                p_timer->p_context);
        APP_ERROR_CHECK(err_code);
    }
    else
    {
        p_timer->p_timeout_handler(p_timer->p_context);
    }
}

ここのticks_Elapsedとticks_expired私も迂回されてめまいがします.でもこれを捨てて.この関数の本意は,タイムアウトしたタイマに対して,彼らが最初に設定したコールバック関数のコールバックを用いることである.以下のキーは参考になります.呼び出されたコールバック関数は2つのm_evt_schedule_funcとp_timer->p_timeout_handler.スケジューリングメカニズムがある場合は前者を呼び出し、スケジューリングカーネルに送信し、最後にメインサイクルでtimer_を行うcreate時にバインドされたコールバック関数スケジューリング.この例ではapp_がデフォルトで呼び出されます.timer_evt_schedule.スケジューリングメカニズムがない場合はtimer_を直接呼び出すcreate時にバインドされたコールバック関数.
もう1つのSWI 0割り込み、ソフトウェア割り込みがあります.SWI0_IRQHandler--SWI 0が中断し、プログラムの多くの場所でこの中断が置かれます.例えば前述のtimer_timeouts_check. SWI 0割り込み中にすべてのタイマ更新を実行
void SWI0_IRQHandler(void)
{
    timer_list_handler();
}

static void timer_list_handler(void)
{
    app_timer_id_t restart_list_head = TIMER_NULL;
    uint32_t       ticks_elapsed;
    uint32_t       ticks_previous;
    bool           ticks_have_elapsed;
    bool           compare_update;
    app_timer_id_t timer_id_head_old;

    //         tick List 
    ticks_previous    = m_ticks_latest;
    timer_id_head_old = m_timer_id_head;

    //      ticks 
    ticks_have_elapsed = elapsed_ticks_acquire(&ticks_elapsed);

    //       
    compare_update = list_deletions_handler();

    //         
    if (ticks_have_elapsed)
    {
        expired_timers_handler(ticks_elapsed, ticks_previous, &restart_list_head);
        compare_update = true;
    }

    //       
    if (list_insertions_handler(restart_list_head))
    {
        compare_update = true;
    }

    //           
    if (compare_update)
    {
        compare_reg_update(timer_id_head_old);
    }
}


1.1.4.スタートタイマ
app_timer_start関数を使用してタイマを起動します.この関数にはtimerが呼び出されていますstart_op_schedule関数.ここで関数を割り当てるにはなぜmp_というパラメータがあるのかusers
uint32_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context)
{
    uint32_t timeout_periodic;
    // Schedule timer start operation
    timeout_periodic = (mp_nodes[timer_id].mode == APP_TIMER_MODE_REPEATED) ? timeout_ticks : 0;

    return timer_start_op_schedule(user_id_get(),
                                   timer_id,
                                   timeout_ticks,
                                   timeout_periodic,
                                   p_context);
}

static uint32_t timer_start_op_schedule(timer_user_id_t user_id,
                                        app_timer_id_t  timer_id,
                                        uint32_t        timeout_initial,
                                        uint32_t        timeout_periodic,
                                        void *          p_context)
{
    app_timer_id_t last_index;

   //        
    timer_user_op_t * p_user_op = user_op_alloc(&mp_users[user_id], &last_index);
    if (p_user_op == NULL)
    {
        return NRF_ERROR_NO_MEM;
    }

    p_user_op->op_type                              = TIMER_USER_OP_TYPE_START;
    p_user_op->timer_id                             = timer_id;
    p_user_op->params.start.ticks_at_start          = rtc1_counter_get();
    p_user_op->params.start.ticks_first_interval    = timeout_initial;
    p_user_op->params.start.ticks_periodic_interval = timeout_periodic;
    p_user_op->params.start.p_context               = p_context;

    user_op_enque(&mp_users[user_id], last_index);    

    timer_list_handler_sched();

    return NRF_SUCCESS;
}




1.2.キーを押す
キーを初期化し、buttons配列で使用するすべてのキーとその構成を定義します.具体的な意味はapp_button_cfg_t構造体.ここで変数を押します:m_detection_delay_timer_idタイマ.このタイマは遅延を計算するために使用され、初期化で作成され、後コールバックdetection_に時間を設定します.delay_timeout_handler関数.
static void buttons_init(void)
{
    // Note: Array must be static because a pointer to it will be saved in the Button handler
    //       module.
    static app_button_cfg_t buttons[] =
    {
        {WAKEUP_BUTTON_PIN, false, BUTTON_PULL, NULL},
        {LEDBUTTON_BUTTON_PIN_NO, false, BUTTON_PULL, button_event_handler}
    };

    APP_BUTTON_INIT(buttons, sizeof(buttons) / sizeof(buttons[0]), BUTTON_DETECTION_DELAY, true);
}
#define APP_BUTTON_INIT(BUTTONS, BUTTON_COUNT, DETECTION_DELAY, USE_SCHEDULER)  \
    do        \
    {                                          \
        uint32_t ERR_CODE = app_button_init((BUTTONS),       \
                                            (BUTTON_COUNT),         \
                                            (DETECTION_DELAY),    \
                                    (USE_SCHEDULER) ? app_button_evt_schedule : NULL); \
        APP_ERROR_CHECK(ERR_CODE);                         \
    } while (0)

同様に、初期化でイベントコールバック関数(ある場合)を設定し、app_をバインドします.button_evt_schedule関数.この関数の中の操作とタイマーの中の操作はあまり差がありません.
uint32_t app_button_init(app_button_cfg_t *             p_buttons,
                         uint8_t                        button_count,
                         uint32_t                       detection_delay,
                         app_button_evt_schedule_func_t evt_schedule_func)
{
    uint32_t err_code;
    if (detection_delay < APP_TIMER_MIN_TIMEOUT_TICKS)
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    //    .
    mp_buttons          = p_buttons;
    m_button_count      = button_count;
    m_detection_delay   = detection_delay;
    m_evt_schedule_func = evt_schedule_func;

    uint32_t pins_transition_mask = 0;   

    while (button_count--)
    {
        app_button_cfg_t * p_btn = &p_buttons[button_count];
        nrf_gpio_cfg_input(p_btn->pin_no, p_btn->pull_cfg);   //    
        pins_transition_mask |= (1 << p_btn->pin_no); //           
    }

    // Register button module as a GPIOTE user.
    err_code = app_gpiote_user_register(&m_gpiote_user_id,
                                        pins_transition_mask,
                                        pins_transition_mask,
                                        gpiote_event_handler);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Create polling timer.
    return app_timer_create(&m_detection_delay_timer_id,
                            APP_TIMER_MODE_SINGLE_SHOT,
                            detection_delay_timeout_handler);
}

キー初期化では、主にキー部が管理する変数を初期化し、ハードウェアと割り込み部を構成し、割り込みコールバック関数gpiote_を設定します.event_handler、ピンレベル状態がマークされています.
キーが押されると、まずgpiote_がコールバックされます.event_handler関数.同時に対応する遅延パラメータを設定して起動するタイマタイミング.
static void gpiote_event_handler(uint32_t event_pins_low_to_high, uint32_t event_pins_high_to_low)
{
    uint32_t err_code;

    //        。         ,        
    //  :   app_timer_start()  p_context                
    STATIC_ASSERT(sizeof(void *) == sizeof(uint32_t));

    err_code = app_timer_stop(m_detection_delay_timer_id);    //     
    if (err_code != NRF_SUCCESS)
    {
        // The impact in app_button of the app_timer queue running full is losing a button press.
        // The current implementation ensures that the system will continue working as normal. 
        return;
    }

    m_pin_transition.low_to_high = event_pins_low_to_high;
    m_pin_transition.high_to_low = event_pins_high_to_low;

    err_code = app_timer_start(m_detection_delay_timer_id,
                               m_detection_delay,
                               (void *)(event_pins_low_to_high | event_pins_high_to_low));
    if (err_code != NRF_SUCCESS)
    {
        // The impact in app_button of the app_timer queue running full is losing a button press.
        // The current implementation ensures that the system will continue working as normal. 
    }
}

検出遅延時間が達したらdetection_を呼び出すdelay_timeout_handlerコールバック関数、この関数でbuttonが呼び出されます.handler_executeキーで押した実行関数.この関数では、前のコールバック関数app_が呼び出されます.button_evt_scheduleはイベントをスケジューリングカーネルに送信します.次のカーネルがこのイベントをスケジューリングすると、キー応答イベントがスケジューリングされます.この例ではLEDBETTON_BUTTON_PIN_NO呼び出しを押すbutton_event_handler、これはサービスの特性を変更する値です.ここでは先に言いません.
static void detection_delay_timeout_handler(void * p_context)
{
    uint32_t err_code;
    uint32_t current_state_pins;

    //        
    err_code = app_gpiote_pins_state_get(m_gpiote_user_id, &current_state_pins);

    if (err_code != NRF_SUCCESS)
    {
        return;
    }

    uint8_t i;
    //       ,      
    for (i = 0; i < m_button_count; i++)
    {
        app_button_cfg_t * p_btn = &mp_buttons[i];

        if (((m_pin_transition.high_to_low & (1 << p_btn->pin_no)) != 0) 
             && (p_btn->button_handler != NULL))
        {
            //            ,             
            if(p_btn->active_state == APP_BUTTON_ACTIVE_HIGH)
            {
                button_handler_execute(p_btn, APP_BUTTON_RELEASE);
            }
            //            ,             
            else
            {
                button_handler_execute(p_btn, APP_BUTTON_PUSH);
            }
        }
        else if (((m_pin_transition.low_to_high & (1 << p_btn->pin_no)) != 0) 
                && (p_btn->button_handler != NULL))
        {
            //            ,             
            if(p_btn->active_state == APP_BUTTON_ACTIVE_HIGH)
            {
                button_handler_execute(p_btn,APP_BUTTON_PUSH);
            }
            //            ,             
            else
            {
                button_handler_execute(p_btn,APP_BUTTON_RELEASE);
            }
        }
    }
}


static void button_handler_execute(app_button_cfg_t * p_btn, uint32_t transition)
{
    if (m_evt_schedule_func != NULL)
    {
        uint32_t err_code = m_evt_schedule_func(p_btn->button_handler, p_btn->pin_no,transition);
        APP_ERROR_CHECK(err_code);
    }
    else
    {
        if(transition == APP_BUTTON_PUSH)
        {
            p_btn->button_handler(p_btn->pin_no, APP_BUTTON_PUSH);
        }
        else if(transition == APP_BUTTON_RELEASE)
        {
            p_btn->button_handler(p_btn->pin_no, APP_BUTTON_RELEASE);
        }
    }
}


理解できるのはグループを加えることができます:805601459(注釈CSDN)は交流を行います.本文は内部交流に限られ、商用は禁止されています!