Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

User Interrupt on NMI Pin with Kinetis and ExtInt Component

DZone's Guide to

User Interrupt on NMI Pin with Kinetis and ExtInt Component

· IoT Zone
Free Resource

Discover why Bluetooth mesh is the next evolution of IoT solutions. Download the mesh overview.

While my beef brisket (see “My First DIY Smoked Beef Brisket: Day 1“) is smoking on ;-), I have time to investigate a problem I was running on in my lecture on Friday: For the Joystick shield (see “JoyStick Shield with the FRDM Board“) on the FRDM-KL25Z board, I wanted to use an interrupt if I press the green button:

No Interrupts for Button C

No Interrupts for Button C

However, that did not work :-(.

First, I was thinking that the board might have a problem, as I had cases where such boards had bad soldering points. A verification with the logic analyzer showed that the hardware wiring is working:

Button C Pin Change

Button C Pin Change

But no interrupt for button C :-(. And it worked for all other pins which have interrupt ability. So what could be wrong?

First, I need to mention that this pin is on PTA4 on the Freescale FRDM-KL25Z board. PTA4 is used by default as NMI (Non Maskable Interrupt pin). I have configured it in Processor Expert with the ExtInt (External Interrupt) component:

ExtInt on PTA4

ExtInt on PTA4

Because PTA4 is configured by default for NMI interrupt, I have properly disabled NMI in the CPU component:

NMI Function Disabled in CPU component

NMI Function Disabled in CPU component

I have ExtInt components for several port A pin (PTA12, PTA4, PTA5 and PTA13), and they are all properly hooked into the Cpu_ivINT_PORTA interrupt service routine and interrupt vector:

PE_ISR(Cpu_ivINT_PORTA)
{
  ExtIntLdd2_Interrupt();              /* Call the service routine */
  ExtIntLdd3_Interrupt();              /* Call the service routine */
  ExtIntLdd4_Interrupt();              /* Call the service routine */
  ExtIntLdd5_Interrupt();              /* Call the service routine */
}

So I believe I might have uncovered a Processor Expert bug? Somehow my PTA4 ExtInt component gets in conflict with the NMI internal interrupt vector settings. As a result, my PTA4 user interrupt is not firing :-(.

I have found at least a way how to get it working. For this, I add the Init_GPIO component to my project and configure it to raise an interrupt for PTA4:

Init_GPIO Settings to enable interrupt on PTA4

Init_GPIO Settings to enable interrupt on PTA4

Now as there is my PORTA_OnInterrupt() used for the interrupt vector, I need to route it to the handler in Cpu.c:

void PORTA_OnInterrupt(void) {
  void Cpu_ivINT_PORTA(void); /* prototype of ISR in Cpu.c */
 
  Cpu_ivINT_PORTA(); /* call interrupt handler created by the ExtInt components */
}

Cpu_ivINT_PORTA() generated by Processor Expert in Cpu.c:

PE_ISR(Cpu_ivINT_PORTA)
{
  ExtIntLdd2_Interrupt();              /* Call the service routine */
  ExtIntLdd3_Interrupt();              /* Call the service routine */
  ExtIntLdd4_Interrupt();              /* Call the service routine */
  ExtIntLdd5_Interrupt();              /* Call the service routine */
}

This is calling the NMI interrupt like this

void ExtIntLdd3_Interrupt(void)
{
  /* {Default RTOS Adapter} ISR parameter is passed through the global variable */
  ExtIntLdd3_TDeviceDataPtr DeviceDataPrv = INT_PORTA__DEFAULT_RTOS_ISRPARAM;
 
  /* Check if the component is disabled */
  if (!DeviceDataPrv->UserEnabled) {
    return;
  }
  /* Call OnInterrupt event */
  ExtIntLdd3_OnInterrupt(DeviceDataPrv->UserData);
}

Note that this code does *not* acknowledge the ISR flag. It calls then the following handler:

void ExtIntLdd3_OnInterrupt(LDD_TUserData *UserDataPtr)
{
  (void)UserDataPtr;                   /* Parameter is not used, suppress unused argument warning */
  SW3_OnInterrupt();                   /* Invoke OnInterrupt event */
}

With this, I need to handle the acknowledge of the interrupt flag in my SW3_OnInterrupt():

void SW3_OnInterrupt(void)
{
#if PL_HAS_KBI
#if 1 /* Problem with Processor Expert and sharing PTA4/NMI interrupt: code below is missing in ExtIntLdd3_OnInterrupt() */
  /* Check the pin interrupt flag of the shared interrupt */
  if (PORT_PDD_GetPinInterruptFlag(PORTA_BASE_PTR, ExtIntLdd3_PIN_INDEX)) {
    /* Clear the interrupt flag */
    PORT_PDD_ClearPinInterruptFlag(PORTA_BASE_PTR, ExtIntLdd3_PIN_INDEX);
    /* call user event */
    KEY_OnInterrupt(KEY_BTN3);
  }
#else
  if (KEY3_Get()) {
    KEY_OnInterrupt(KEY_BTN3);
  }
#endif
#endif
}

With this, it works :-)

Summary

It is possible to disable and predefined interrupts like NMI for the Kinetis. However, that might conflict with the default settings of the CPU (or is a bug in Processor Expert). In any case, I have found a solution to get it working:

  1. Use the Init_GPIO component to enable interrupts on PORTA with custom handler.
  2. Route the interrupts from the custom handler to the CPU component handler.

Happy Interrupting :-)

 

Take a deep dive into Bluetooth mesh. Read the tech overview and discover new IoT innovations.

Topics:

Published at DZone with permission of Erich Styger, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}