nRF52832 GPIOTE中斷丟失的問題

        產品中需要用到GPIO外部中斷功能,可以使用芯片的GPIOTE功能實現。但是自己配置GPIOTE有點麻煩,於是偷個懶,直接用例程中的Button相關代碼,把外部中斷當成是按鍵輸入。

        實際上Button模型底層也是通過GPIOTE加定時器實現的,只不過SDK做了一些封裝,加了延時消抖功能。使用起來確實很方面,但是也有弊端,那就是容易“丟中斷”,意思就是外部中斷信號並不能保證百分之百被芯片(準確的說是上層應用)檢測到,這對於一些對外部中斷次數要求嚴格的場合是不能接受的。可是爲什麼會發生這個問題呢?下面就來探究一下。

        首先看一下Button模型的工作原理:

1.當按鍵按下,或者外部中斷信號輸入時,芯片產生一個GPIOTE事件(中斷),對應的服務函數是gpiote_event_handler()

 2.在GPIOTE中斷服務函數中,SDK自動開啓一個定時器,這個定時器的超時時間由用戶在初始化Button時設置,默認是50ms。定時時間到後再次檢測GPIO狀態,如果仍然爲有效,則產生一個按鍵事件,對應的回調函數是bsp_event_handle,通過函數的入參,可以知道是哪個通道的中斷髮生了。

 3.如果在50ms定時時間到達之前,又來了一次GPIOTE中斷,則SDK自動停止當前的定時器,接着立即啓動當前定時器,相當於重置了一次定時器。

        上述過程3的動作每執行一次,會在定時器隊列中額外增加一個節點,雖然無效的定時器節點會很快被清除掉,但是如果GPIOTE事件連續而且密集地到達的話,可能會導致SDK來不及清理無效定時器,而使定時器隊列爆滿,導致定時器重啓失敗。而從上述過程2中可以得知,底層的GPIOTE事件是靠定時器超時來傳遞給上層應用的,如果定時器失效的話,就無法超時,也就無法將事件傳遞到上層了。對於上層應用來說,就是本次按鍵事件丟失。

        其實,SDK開發者在註釋裏面對這個問題已有說明,原文如下:

static void gpiote_event_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
    uint32_t err_code;
    uint32_t pin_mask = 1 << pin;

    // Start detection timer. If timer is already running, the detection period is restarted.
    // NOTE: Using the p_context parameter of app_timer_start() to transfer the pin states to the
    //       timeout handler (by casting event_pins_mask into the equally sized void * p_context
    //       parameter).

    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;
    }

    // ...
}

        所以,如果想解決這個問題,還是自己配置GPIOTE,自己從底層實現消抖爲好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章