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.
pull/6984/head
Steven 2018-05-22 22:43:43 +02:00
parent ea13262aec
commit b011e2b8c7
1 changed files with 17 additions and 6 deletions

View File

@ -61,12 +61,26 @@ static void handle_interrupt_in(uint8_t pin)
return; return;
} }
// Get trigger event // trying to discern which GPIO IRQ we got
gpio_irq_event event = IRQ_NONE; 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; 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; 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); GPIO_IntClear(pin);
irq_handler(channel_ids[pin], event); 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 */ /* Disable, set config and enable */
gpio_irq_disable(obj); 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); GPIO_IntConfig((GPIO_Port_TypeDef)((obj->pin >> 4) & 0xF), obj->pin &0xF, obj->risingEdge, obj->fallingEdge, obj->risingEdge || obj->fallingEdge);
} }