LPC2148 FIQ Handler problem

capablazab wrote on Thursday, November 08, 2012:

Hello,

I’m using FreeRTOS with an LPC2148. I ported the code from the LPC 2106 example. (It works fine)

The FIQ interrupt is working fine too, but I need it to launch a function from the ISR, to toggle a pin of the MCU.

I have 2 tasks: one that blinks an GREEN LED (just to be sure the system is running), and an ISRHandler which is synchronized with a semaphore. In this case, the ISRHandler turns ON an RED LED. The code I’m using is very similar to the one proposed in the documentation.

I’m sure the FIQ interrupt is working fine: With the GREEN LED blinking, if I dont use the ISRHandler, I can turn ON the RED LED in the interrupt when a pin is LOW, and then turn it OFF when the pin returns to HIGH, and the GREEN LED returns to blink. So, the interrupt ends and the blinker task returns to run.

BUT, If I enable the ISRHandler task (with xTaskCreate()), the ISRHandler preempts the blinker task after the FIQ interrupt, and apparently doesnt block when the pin is HIGH again, so the GREEN LED doenst blink anymore.

I hope my explanation isn’t too confusing.

This is the code I’m using: (Ill do a brief summary)


main.h

xSemaphoreHandle xBinarySemaphore;


main.c

int main( void )
{
  // Setup the hardware for use with the Olimex demo board.
  prvSetupHardware();

  // Create Binary Semaphore
  vSemaphoreCreateBinary(xBinarySemaphore);

  // SETUP FIQ
  PINSEL0 = 0x20000000; // sets p0.14 as EINT1
  VICIntSelect |= 0x00008000;
  VICIntEnable = 0x00008000;

  xTaskCreate( vISRHandlerTask, ( signed char * ) “ISRHandler”, 1000, NULL, 3, NULL );
  xTaskCreate( vBlinkTask, (signed char * ) “blinker”, configMINIMAL_STACK_SIZE, NULL, 1, NULL);

  vTaskStartScheduler();

  // Should never reach here!
  return 0;
}

void FIQ_Handler (void)
{
  // Remove FIQ flag
  EXTINT = 0x2;

  **static portBASE_TYPE xHigherPriorityTaskWoken;

  xHigherPriorityTaskWoken = pdFALSE;

  xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken);

  if(xHigherPriorityTaskWoken == pdTRUE)
    portYIELD_FROM_ISR();**
}


ISRHandler.c

#include “main.h”

#define LED (1 << 20)

void vISRHandlerTask(void)
{
  IO1DIR |= (LED); // Initializes P1.16 as output.

  for(;:wink:
  {
    // This task will be blocked until the FIQ gives it the semaphore.
   ** xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);**

    IO1SET |= LED;  // turns ON pin 1.16

    // When finishing the routine, this task will return to blocked state
    // waiting for another semaphore which will be given again by the FIQ.
  
   /// I tried with giving the semaphore, but the MCU starts with the RED LED ON and doesnt do anything.
    ////xSemaphoreGive(xBinarySemaphore);
  }
}


Blinker.c

#define LED (1 << 19)

void vBlinkTask(void)
{
  pwmInit();

  pwmFrequency(500);
  pwmDutyCycle(90);

  IO1DIR |= LED;

  for(;:wink:
  {
    IO1CLR |= LED;      // just blinks an LED every 100 ticks
    vTaskDelay(50);
    IO1SET |= LED;
    vTaskDelay(50);
  }
}


I’d greatly appreciate any help.

Regards,
Carlos

rtel wrote on Thursday, November 08, 2012:

A couple of comments:

1) Do you have a stack setup for FIQ mode?  Most of the demos provided don’t.  The stacks are normally configured in assembly code from the C start up code, before main() is called.

2) If you are using FIQ then you will have to ensure your portDISABLE_INTERRUPTS() and portENABLE_INTERRUPTS() functions disable/enable both IRQ and FIQ.

3) You are likely to get problems using FreeRTOS API functions from an FIQ handler because FIQ can pre-empt IRQ, and the code does not take that into account (none of the demos use FIQ).  You might be ok if you disable the FIQ interrupt before using any FreeRTOS functions from an IRQ handler, but then that might defeat the object of using FIQ in the first place.

Regards.

capablazab wrote on Friday, November 09, 2012:

Richard,

Thank you for your fast response.

As you suggested I made a few changes:

I increased the FIQ stack size to 400 bytes in startup.s:

/* Stack Sizes */
    .set  UND_STACK_SIZE, 0x00000004
    .set  ABT_STACK_SIZE, 0x00000004
    .set  FIQ_STACK_SIZE, 0x00000800
    .set  IRQ_STACK_SIZE, 0X00000400
    .set  SVC_STACK_SIZE, 0x00000400


in main (just to be sure), i added:

int main( void )
{
** portENABLE_INTERRUPTS();**


Also, in portmacro.h the codes seems to be correctly handling the activation/deactivation of FIQ and IRQ:

#define portDISABLE_INTERRUPTS() \
__asm volatile ( \
“STMDB SP!, {R0} \n\t” /* Push R0. */ \
“MRS R0, CPSR \n\t” /* Get CPSR. */ \
*“ORR R0, R0, #0xC0 \n\t” /* Disable IRQ, FIQ. */ *
“MSR CPSR, R0 \n\t” /* Write back modified value. */ \
"LDMIA SP!, {R0} " ) /* Pop R0. */

#define portENABLE_INTERRUPTS() \
__asm volatile ( \
“STMDB SP!, {R0} \n\t” /* Push R0. */ \
“MRS R0, CPSR \n\t” /* Get CPSR. */ \
*“BIC R0, R0, #0xC0 \n\t” /* Enable IRQ, FIQ. */ *
“MSR CPSR, R0 \n\t” /* Write back modified value. */ \
"LDMIA SP!, {R0}


Also I added to the ISRHandler:

void vISRHandlerTask(void)
{
  IO1DIR |= (LED); // Initializes P1.16 as output.

  for(;:wink:
  {
// This task will be blocked until the FIQ gives it the semaphore.
    xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);

    portDISABLE_INTERRUPTS();

    IO1SET |= LED;

    // When finishing the routine, this task will return to blocked state
    // waiting for another semaphore which will be given again by the FIQ.
    //xSemaphoreGive(xBinarySemaphore);

   ** portENABLE_INTERRUPTS();**
  }
}

As you said on number 3:
_You are likely to get problems using FreeRTOS API functions from an FIQ handler because FIQ can pre-empt IRQ, and the code does not take that into account (none of the demos use FIQ). _

If I understand, you mean, the FIQ can preempt the IRQ? But I only have a FIQ. there are not other interrupts.

You might be ok if you disable the FIQ interrupt before using any FreeRTOS functions from an IRQ handler, but then that might defeat the object of using FIQ in the first place.

I think this too is applicable when having FIQ and IRQ interrupts? Or am I wrong?

I can add now, that, with this code if I remove the call to xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken); in the FIQ routine, the LED returns to blink, so that seems to be the trouble.

I’m using the FIQ because its the fastest one only, but if you tell me that the IRQ will work, then I could give it a try. (Or any other workaround :D)

Thank you for your support!!
Carlos

capablazab wrote on Friday, November 09, 2012:

Sorry,

This doesnt work either.

void vISRHandlerTask(void)
{
  IO1DIR |= (LED); // Initializes P1.16 as output.

  for(;:wink:
  {
portDISABLE_INTERRUPTS();

// This task will be blocked until the FIQ gives it the semaphore.
    xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);

    IO1SET |= LED;

    // When finishing the routine, this task will return to blocked state
    // waiting for another semaphore which will be given again by the FIQ.
    //xSemaphoreGive(xBinarySemaphore);

    portENABLE_INTERRUPTS();
  }
}

Carlos

anonymous wrote on Friday, November 16, 2012:

No one?

Any help please?

I just need to interrupt the MCU with an external interrupt, and then do some processing with a task, periodically…

travfrog wrote on Friday, November 16, 2012:

Hi Carlos,

I don’t know anything about this specific MCU so I did a quick read of the user manual. From your code above, I do not see where you are configuring the EXT interrupt mode and polarity. These registers apparently default to 0 which means level sensitive active low.

I suspect  when you change the state of the external pin to low, the interrupt fires as expected. You clear the interrupt flag but at this point the input is still low so as soon as the interrupt exits, it will fire again. You end up doing nothing more than servicing the interrupt. I’ve done this sort of thing.

You could make the input edge triggered (falling or rising).

Hope this helps
travfrog

anonymous wrote on Saturday, November 17, 2012:

Dear all,

The problem is solved!

travfrog thank you too for your help: The EXT is set to level sensitive by default as you said, so I was triggering the interrupt by moving a wire from VCC/GND. Now I’ve set it to edge sensitive so I can trigger it with a PWM.

Here’s what I dit  to make the **FIQ[/b work (thanks to mikaeleiman from lpc2000 yahoo group)


main.c:

void FIQ_Handler (void) __attribute__ ((interrupt(“FIQ”))) __attribute((naked));

void FIQ_Handler (void)
{
  portSAVE_CONTEXT();

  actualFIQHandler();

  EXTINT = 0x2;

  portRESTORE_CONTEXT();
}


actualFIQHandler.c

void actualFIQHandler(void) __attribute__((noinline));
void actualFIQHandler(void)
{
  static signed portBASE_TYPE xHigherPriorityTaskWoken;

  xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken);

  if ( xHigherPriorityTaskWoken )
  {
    // switch context to newly awoken task
    portYIELD_FROM_ISR();
  }
}


and finally ISRHandler.c

void vISRHandlerTask(void)
{
  IO1DIR |= (LED); // Initializes P1.16 as output.

  int flag=0;

  for(;:wink:
  {
portDISABLE_INTERRUPTS();

// This task will be blocked until the FIQ gives it the semaphore.
    xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);

    if(flag==1)
    {
    IO1SET |= LED;
    flag=0;
    }
    else
    {
    IO1CLR |= LED;
    flag=1;
    }

    // When finishing the routine, this task will return to blocked state
    // waiting for another semaphore which will be given again by the FIQ.

    portENABLE_INTERRUPTS();
  }
}

I defined the interrupt as edge sensitive, and its working okay now.

Thank you for your support!
Carlos**