I'm having some difficulty with GPIO interrupts on an LPC1788 on the embedded artists development board. I've reproduced the issue with a really stripped down project which is just looking at a clock signal coming in on one pin (~1200 Hz) and sending a pulse out on a different pin, and checking both on a oscilloscope.
the issue is that the interrupt should be firing on the falling edge of the clock signal and while it is firing, it effectively keeps shifting to the right relative to the signal going in, I have scope screenshots to illustrate the issue if it helps. the code is:
#include "cmsis_os2.h" #include "LPC177x_8x.h" #include "GPIO_LPC17xx.h" #include <stdio.h> uint8_t clock_port = 0; uint8_t clock_num = 9; uint8_t twiddle_port = 1; uint8_t twiddle_num = 28; void GPIO_IRQHandler(void) { GPIO_PinWrite(twiddle_port, twiddle_num, 1); LPC_GPIOINT->IO0IntClr |= 1<<9; GPIO_PinWrite(twiddle_port, twiddle_num, 0); } int main() { osKernelInitialize(); // initialize CMSIS-RTOS SystemCoreClockUpdate(); GPIO_PortClock(1); GPIO_SetDir(clock_port, clock_num, GPIO_DIR_INPUT); GPIO_SetDir(twiddle_port, twiddle_num, GPIO_DIR_OUTPUT); LPC_GPIOINT->IO0IntEnF |= 0x01 << 9; // trigger on falling edge NVIC_SetPriority(GPIO_IRQn, 0); NVIC_EnableIRQ(GPIO_IRQn); osKernelStart (); // start thread execution for (;;) { } return 0; }
I have tried this both with and without the RTOS initialised/started and see exactly the same behaviour with both. for my application it is not possible to use the NMI pin or the external interrupts as I have 6 pins I need to work with this way.
the clock speed is 120MHz, the peripheral clock is 60MHz. I was originally using keil 5.27.1, have now updated to 5.28.0. all packs show as up to date.
am I missing something in my setup that explains the slow/mistimed interrupts?
How slow is "slow" ?
Have you looked what overhead is in those GPIO_PinWrite() calls ?
so timing it from reset until the first trigger of the IRQ, the first falling before the trigger is 655uS before, but it also seems to drift to the right relative to the trigger, and keep drifting until it passes what should be the next trigger after it.
and the definition of the GPIO_PinWrite:
void GPIO_PinWrite (uint32_t port_num, uint32_t pin_num, uint32_t val) { val ? (LPC_GPIO(port_num)->SET = (1UL << pin_num)) : \ (LPC_GPIO(port_num)->CLR = (1UL << pin_num)); }
to add further information, I have altered my test code to work with external interrupt 3 on p2.13 with the same results. namely the interrupt firing is not where it should be relative to the input, and appears to drift also. EINT3 code:
#include "cmsis_os2.h" #include "LPC177x_8x.h" #include "GPIO_LPC17xx.h" #include <stdio.h> uint8_t clock_port = 2; uint8_t clock_num = 13; uint8_t twiddle_port = 1; uint8_t twiddle_num = 28; void EINT3_IRQHandler(void) { LPC_SC->EXTINT |= 1<<3; GPIO_PinWrite(twiddle_port, twiddle_num, 1); GPIO_PinWrite(twiddle_port, twiddle_num, 0); } int main() { osKernelInitialize(); // initialize CMSIS-RTOS SystemCoreClockUpdate(); GPIO_PortClock(1); GPIO_SetDir(clock_port, clock_num, GPIO_DIR_INPUT); GPIO_SetDir(twiddle_port, twiddle_num, GPIO_DIR_OUTPUT); LPC_SC->EXTINT = (1<<3); /* Clear Pending interrupts */ LPC_IOCON->P2_13 = 1; LPC_SC->EXTMODE = (1<<3); /* Configure EINTx as Edge Triggered*/ LPC_SC->EXTPOLAR = (1<<3); /* Configure EINTx as Falling Edge */ NVIC_SetPriority(EINT3_IRQn, 0); NVIC_EnableIRQ(EINT3_IRQn); osKernelStart (); // start thread execution for (;;) { } return 0; }
Your generating an interrupt every 16.66[ns] - that _really_ fast, and may not even be reconcilable with higher compiler optimization levels. Do you still see the problem if you slow down to, say, 100[us] (100KHz) ?
How many clock cycles does the MCU need to detect and switch to the ISR? I don't remember the exact number, but I believe it to be around 16 clock cycles. At 120[MHz] that's quite some nanoseconds then and there...
I shouldn't be generating an interrupt that often, the input signal that I'm using to trigger the interrupt is only 1200Hz, so it should be every 0.833 milliseconds or 833 uS. depending on the type of interrupt it should be 12(ish) cycles with some jitter, so 16 would be reasonable, I'm not sure if that is based off the peripheral clock or the core clock but with the delay of 655uS before it actually triggers the ISR that's more like 39300 or 78600 clock cycles....
For clarity my input signal has 50% duty cycle and has nice clean edges on an oscilloscope so it shouldn't cause any issues being used to trigger a falling edge interrupt.
Have you raised this with NXP and/or on their forum?
Have you done a test to prove that the CPU is really running at the speed you think it is?
It seems that on a LPC17xx one recommends clearing interrupts at the very beginning of the ISR. You did that for the external interrupt (but not the other), but did you test it like that? Also, does the behavior change when you write proper "toggle" logic in the ISR? You're creating a very short pulse - maybe your scope somehow missing that...?
I have not yet raised this on the NXP forum, I have used the debugging within keil to check how the registers that control the clock are set up and they are set up correctly, I have not routed the clock to an output pin to double check but I shall look into that. I have since changed the ISR to clear either as the first thing inside the ISR and this made no difference, and I have since implemented toggle logic and I still have the same problem. my scope is rated at 1GHz or 4GSa/s so it should be able to pick up short pulses without issue, but I'll stick with the toggle logic for now.
Is it possible that you somehow collide with the LCD peripheral?
According to the user manual,
GPIO23 P0.9 LCD databit 17
I don't have the LCD peripheral connected, and I get exactly the same behaviour and timings on EINT3 port 2 pin 13.
What about the execution time of the ISR itself? Look like something that should take a few microseconds, but you never know if your CPU clock if not configured right...