1
\$\begingroup\$

So I'm baffled by this...

I'm working with an ARMv7-M microcontroller and GCC compiler, debugging some code where I'm trying to enable an interrupt. I'm trying to do the following: enable an interrupt so that on a value change for pin PA31, pin PA9 gets set to low, using the MCU's PIO controller. The interrupt handler is just called PIOA_Handler, it is declared in one of the device files as:

void PIOA_Handler ( void ) attribute ((interrupt));

And the guts of the function are in main.cpp:

void PIOA_Handler(void)
{

    if ((*((Pio*)PIOA)).PIO_PDSR < PIO_PDSR_P31_Msk) {
        ((*((Pio*)PIOA)).PIO_CODR |= PIO_CODR_P9_Msk);      // clear output on pin 9 if pin PA31 is logic low
    }

I start by enabling global interrupts and setting PA9 to out, and enabling the change interrupt for pin PA31 on the peripheral side and also enabling the NVIC interrupt vector so the processor accepts and handles the interrupt:

        gpio_set_pin_direction(PIN_PA9, GPIO_DIRECTION_OUT);    // set PA9 to out and PA31 to in
        sys->setPin(PIN_PA9, true);                             // enable PA9

        ((Pio *)PIOA)->PIO_IER |= PIO_IER_P31_Msk;
        NVIC_EnableIRQ(PIOA_IRQn);

The actual problem that I'm having is this: for some reason, when I'm debugging without setting a breakpoint, rather than calling PIOA_Handler when there is a pin change, it just gets called continuously, over and over for the entire length of the debugging session.

If, however, I set a breakpoint on the line of code where I enable the PIO level change interrupt:

((Pio *)PIOA)->PIO_IER |= PIO_IER_P31_Msk;

this somehow doesn't happen and PIOA_Handler gets called as it should, when there really is a level change on PA31.

One more thing I've noticed is that if I set a breakpoint on the next line NVIC_EnableIRQ(PIOA_IRQn);, and look at the NVIC Interrupt Set Pending Register (ISPR) in one of the debugger watch windows, there is a pending interrupt for PIOA_IRQn. But if I set a breakpoint on ((Pio *)PIOA)->PIO_IER |= PIO_IER_P31_Msk; and step over to the NVIC_EnableIRQ line, there is no pending interrupt.

I've tried inserting NVIC_ClearPendingIRQ between the two lines, but it doesn't clear the pending IRQ. For whatever reason somehow the breakpoint clears it, but using an actual Clear Pending function does not do the trick. Why is this and how do I write code that doesn't need a breakpoint for the interrupt handler to be called properly?

Thanks in advance!

\$\endgroup\$
  • 6
    \$\begingroup\$ For some micros, you have to read the Port Input Data Register in order to fully clear a pin-change interrupt. If your debugger does this for you (perhaps because you're looking at the input status in a watch window) then this could explain why your code appears to work under certain debug conditions. \$\endgroup\$ – brhans Mar 11 at 15:39
  • \$\begingroup\$ Hm... looked for the Port Input Data register, having a hard time finding anything by that name, other than as a feature of the chip's encryption system. Were you referring to a register that's part of the PIO, maybe there's a different name for it in this particular microcontroller? Or was it more part of the SWD Debugging Interface you were referring to? \$\endgroup\$ – J. Fernwright Mar 11 at 19:26
  • \$\begingroup\$ Yes, it'll be one of your PIO registers. For example in an STM32F4xx it's GPIOx_IDR, or GPIOx_PDIR in a Kinetis MK02xx \$\endgroup\$ – brhans Mar 11 at 19:30
  • \$\begingroup\$ Hmm yeah there is an equivalent in my processor, but unfortunately reading the PIO data register doesn't seem to do the trick. \$\endgroup\$ – J. Fernwright Mar 11 at 20:18
  • \$\begingroup\$ You might also need to clear an individual pin interrupt status flag. For example in the MK02 you'd have to write a '1' to the ISF bit in the PORTx_PCRn register. \$\endgroup\$ – brhans Mar 11 at 20:29
0
\$\begingroup\$

you were correct, it turned out to be a matter of just adding a register read to my code when setting up the interrupt, and inside the interrupt handler itself. All it took was a process of elimination to see which register read in the watch was helping the interrupt to trigger properly, and then just replicate that in my code. But for some reason I didn't think of that particular testing approach until I had been working on a separate problem for a little while. Thanks!!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.