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,自己从底层实现消抖为好。

你可能感兴趣的:(BLE)