From b011e2b8c77df9dd38e50ab8bb36f2b79ee9b7f6 Mon Sep 17 00:00:00 2001 From: Steven Date: Tue, 22 May 2018 22:43:43 +0200 Subject: [PATCH] EFM32 IRQ handling fix * IRQ handling got updated previously to a non-functional state when both callbacks were registered (it'd fire a fall callback for both rise and fall events). With this update, that faulty behaviour is corrected. Due to delays between the detection of the edge and the handling of the interrupt (and the fact that information about which edge you received on the pin is not stored anywhere), there is no way to be absolutely sure which edge got triggered on the pin. Therefore, we make a best-guess effort by looking at the pin state at the time of IRQ handling, and fire a callback as if that was the end state of the event. This will usually work out fine, except in cases were the signal is toggling faster than the IRQ handler's response time. In that case, a user won't get both callbacks (as expected for a pulse), but only the last event. * Stripped some dead code. --- .../TARGET_EFM32/gpio_irq_api.c | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/targets/TARGET_Silicon_Labs/TARGET_EFM32/gpio_irq_api.c b/targets/TARGET_Silicon_Labs/TARGET_EFM32/gpio_irq_api.c index 4816d0f25e..f2aa6172cc 100644 --- a/targets/TARGET_Silicon_Labs/TARGET_EFM32/gpio_irq_api.c +++ b/targets/TARGET_Silicon_Labs/TARGET_EFM32/gpio_irq_api.c @@ -61,12 +61,26 @@ static void handle_interrupt_in(uint8_t pin) return; } - // Get trigger event + // trying to discern which GPIO IRQ we got gpio_irq_event event = IRQ_NONE; - if (GPIO->EXTIFALL & (1 << pin)) { + if (((GPIO->EXTIFALL & (1 << pin)) != 0) && ((GPIO->EXTIRISE & (1 << pin)) == 0)) { + // Only the fall handler is active, so this must be a falling edge event = IRQ_FALL; - } else if (GPIO->EXTIRISE & (1 << pin)) { + } else if (((GPIO->EXTIFALL & (1 << pin)) == 0) && ((GPIO->EXTIRISE & (1 << pin)) != 0)) { + // Only the rise handler is active, so this must be a rising edge event = IRQ_RISE; + } else { + // Ambiguous as to which IRQ we've received. Poll the pin to check which one to fire. + // NOTE: If trying to detect a pulse where the width is shorter than this handler's + // reaction time, there will only be one callback (for the trailing edge) called. + + // we are storing two ports in each uint8, so we must acquire the one we want. + // If pin is odd, the port is encoded in the 4 most significant bits. + // If pin is even, the port is encoded in the 4 least significant bits + uint8_t isRise = GPIO_PinInGet((pin & 0x1) ? + channel_ports[(pin>>1) & 0x7] >> 4 & 0xF : + channel_ports[(pin>>1) & 0x7] >> 0 & 0xF, pin); + event = (isRise == 1 ? IRQ_RISE : IRQ_FALL); } GPIO_IntClear(pin); irq_handler(channel_ids[pin], event); @@ -132,9 +146,6 @@ void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable) /* Disable, set config and enable */ gpio_irq_disable(obj); - bool was_disabled = false; - if(GPIO->IEN == 0) was_disabled = true; - GPIO_IntConfig((GPIO_Port_TypeDef)((obj->pin >> 4) & 0xF), obj->pin &0xF, obj->risingEdge, obj->fallingEdge, obj->risingEdge || obj->fallingEdge); }