After reading http://mbed.org/questions/1228/Can-I-use-InterruptIn-to-receive-interru/ I got curious and decided to check the LPC1768's interruptIn code, which is here: http://mbed.org/users/mbed_official/code/mbed-src/file/3bc89ef62ce7/vendor/NXP/LPC1768/hal/gpio_irq_api.c.
This code runs loops to check every possible interrupt pending bit, which works but isn't exactly efficient. So plan A making a simple search algorithm to find the bit that is '1'. Considering by far the most likely situation is only a single interrupt pending, that would in most use cases speed up the process alot. Sadly after roughly 5 minutes I got bored of trying to make that.
So time for plan B, and going oldschool (at least for me personally), google the instruction set, and see if there is anything useful in it. The instruction set is surprisingly small and easy to understand, and after almost missing it I found what I was looking for: clz -> Count Leading Zeros. And with some more googling I found it is somewhere in the standard code also hidden as C command:
So at that point it becomes fairly straightforward: Run a loop while there are still bits at '1', count the leading zeros, use that to calculate the bit position, and call the required user function. Next clear the interrupt, and check if there are more pending:
while(fall0 > 0) { //Continue as long as there are interrupts pending
bitloc = 31 - __CLZ(fall0); //CLZ returns number of leading zeros, 31 minus that is location of first pending interrupt
irq_handler(channel_ids[bitloc], IRQ_FALL); //Run that interrupt
//Both clear the interrupt with clear register, and remove it from our local copy of the interrupt pending register
LPC_GPIOINT->IO0IntClr = 1 << bitloc;
fall0 -= 1<<bitloc;
}
And rising interrupt is exactly the same. For port2 we have to give an offset and take into account port2 is only half a port.
while(fall2 > 0) { //Continue as long as there are interrupts pending
bitloc = 31 - __CLZ(fall2); //CLZ returns number of leading zeros, 31 minus that is location of first pending interrupt
if (bitloc < 16) //Not sure if this is actually needed
irq_handler(channel_ids[bitloc+32], IRQ_FALL); //Run that interrupt
//Both clear the interrupt with clear register, and remove it from our local copy of the interrupt pending register
LPC_GPIOINT->IO2IntClr = 1 << bitloc;
fall2 -= 1<<bitloc;
}
In the testcase a simple program where you have to short p21 and p22. p21 creates a 50% duty cycle square wave with increasing frequencies, p22 uses interruptIn to count the number of rising edges in one second. If more than 90% of the edges are counted it is considered a pass, otherwise a fail (it isn't exact, since there is some overhead also present, and not all frequencies can be generated exactly).
In Vendor / NXP / LPC1768 / hal / gpio_irq_api.c there is a define to switch between old and new irq routines. (Apparently it doesn't work if I try to make the switch in main.cpp).
The results I get is that the old routine fails somewhere between 90kHz and 100kHz, while the new one starts missing edges at 390kHz. A four times increase in speed :)
Example program
Hi Erik,
This certainly looks like a nice improvement, thanks! What I don't understand is why you call irq_handler directly now, without checking for a proper ID like the old code did:
If you ignore the test, it looks like you might end up calling handlers for pins that don't actually have a handler set (that is, there is no InterruptIn object associated with that pin).
Thanks, Bogdan