Code gets hung up in interrupt handler for LPC55s69

Am using LPC55s69 Evk board
I want to implement interrupt handler for USART receive. When I use baremetal instead of FreeRtos interrupt handler works fine, but in the case of FreeRtos there seems a problem.

This is my handler

void FLEXCOMM0_IRQHandler(void) {
    BaseType_t yield = xTaskResumeFromISR(xISRTaskHandle);
    portYIELD_FROM_ISR(yield);
}

where

void ISRTask(void *pvParameters) {
    while(1) {
        vTaskSuspend(xISRTaskHandle);  // Suspend as soon as scheduled
        FC0_Uart_Send("IRQ");
    }
}

ISRTask is created along with all other tasks and has same priority as all other taskss
This is the USART Init function

void FC0_Uart_Init() {
  usart_config_t config;
  
  /*
    * config.baudRate_Bps = 115200U;
    * config.parityMode = kUSART_ParityDisabled;
    * config.stopBitCount = kUSART_OneStopBit;
    * config.loopback = false;
    * config.enableTxFifo = false;
    * config.enableRxFifo = false;
    */
  USART_GetDefaultConfig(&config);
  config.baudRate_Bps = FC0_UART_BAUD_RATE;
  config.enableTx     = true;
  config.enableRx     = true;

  USART_Init(USART0, &config, FC0_UART_CLOCK_FREQ);

  USART_EnableInterrupts(USART0, kUSART_RxLevelInterruptEnable | kUSART_RxErrorInterruptEnable);
  NVIC_SetPriority(FLEXCOMM0_IRQn, FC0_UART_NVIC_PRIO);
  NVIC_EnableIRQ(FLEXCOMM0_IRQn);

}

Now whenever I receive a UART interrupt the code goes into the interrupt handler but gets stuck there. It should go to ISRTask but it isnt.

Which FreeRTOS port are you using?

Can you break the code in debugger and see what is it doing when it appears stuck?

Am using Arm Cortex M33 NTZ Non_secure port

Basically,
yield = xTaskResumeFromISR(xISRTaskHandle); this 1 when executed for the first time
After FLEXCOMM0_IRQHandler is done implementing it jumps to

portTASK_FUNCTION
and back to FLEXCOMM0_IRQHandler
and this cycle continues indefinitely

Which task is portTASK_FUNCTION? Also can you check the ready list in the debugger and verify that the ISRTask is ready?

By portTASK_FUNCTION I mean this

/*
 * -----------------------------------------------------------
 * The Idle task.
 * ----------------------------------------------------------
 *
 * The portTASK_FUNCTION() macro is used to allow port/compiler specific
 * language extensions.  The equivalent prototype for this function is:
 *
 * void prvIdleTask( void *pvParameters );
 *
 */
static portTASK_FUNCTION( prvIdleTask, pvParameters );

This piece of code is in tasks.c in kernel. Looks like this is idle task

And to answer the other question, Yes ISRTask is in ready state.

Better don’t (ab)use task suspend/resume for task synchronization !
Use task notification or a (binary) semaphore etc. designed for that.
Besides other implications resuming a non-suspended or not yet suspended task does nothing.
This is a potential source of a race condition.

The next thing to check is if PendSV_Handler is getting called after you return from the ISR. PendSV_Handler should call vTaskSwitchContext which should select the correct task.

No, PendSV_Handler is not getting called after ISR done executing.

I am not familiar with this particular CPU/platform, but is there anything that needs to be done in the ISR handler to acknowledge or ‘clear’ the pending interrupt? This almost seems like the moment the ISR handler exits, it is called again because the interrupt is still pending.

2 Likes

Can you share your project?

Hi @aggarg

As am a new user I cannot upload files
Please send me your email
Thanks

You should be able to upload files now.

Thanks PFA project zip
lpcxpresso55s69_freertos_usart.zip (717.7 KB)

There were multiple issues in the project -

  1. It was using USART_WriteBlocking API and was expecting USART ISR to fire. The correct API to use is USART_TransferSendNonBlocking.
  2. It was first suspending the task and then calling UART send which would never get called as that task is suspended. The correct sequence should be - first invoke the send operation and then wait for a signal from the ISR.
  3. If InitTask is not needed anymore, it should delete itself.
  4. As @hs2 mentioned, it is never a good idea to use suspend/resume for synchronization and therefore, I updated the code to use semaphore instead.

Here is the updated code -
lpcxpresso55s69_freertos_usart.zip (770.3 KB)

Thanks for the input
But I need to use FLEXCOMM0_IRQHandler to get more involved in the code and not depend on driver code( ie task_handle)
Another thing is there is a slight misunderstanding, I want ISRTask to suspend when created and only wakeup when ISR jumps there
That means that, when recv interrupt occurs it should jump and execute remaining ISRTask

Regarding point-2 from your above reply, I found this style of execution here

(Which actually kinda makes sense, because firstly the ISRTask is suspended/semaphore_wait, then when ISR is executed it resumes ISRTask to send out the string and as it is while(1) it suspends again waiting for ISR , please let me know if I got this understanding correct)

Thanks

Obviously there are also pretty bad tutorials in Youtube :wink:
Try to avoid to follow wrong advices and trust the experts like @aggarg

2 Likes

Sure - you can do that. Look at the implementation of USART_TransferSendNonBlocking for what you need to do to kick off the transmission and look at the implementation of USART_TransferHandleIRQ for what all you need to do in your ISR. You cannot expect to call a blocking API and expect the ISR to fire.

The blocking APIs returns after the operation is complete -

                        Time spent inside the API
        <------------------------------------------------------------------------->

        +-------------------------------------------------------------------------+
        ^                                                                         ^
        |                                                                         |
        |                                                                         |
        |                                                                         |
Start Blocking Operation                             The blocking API returns after
                                                     the operation is complete.
        <------------------------------------------------------------------------->
                        Time consumed in the operation

The non-blocking API, on the other hand, just starts the operation and returns. Later, when the operation is complete, the corresponding interrupt fires.

        Time spent inside
        the API
        <----------------->

        +--------------------------------------------------------------------------+
        ^                 ^                                                        ^
        |                 |                                                        |
        |                 |                                                        |
        |                 |                                                        |
Start Non-Blocking     The API returns               Operation complete interrupt
Operation              after starting the            when the operation is complete.
                       operation
        <------------------------------------------------------------------------->
                        Time consumed in the operation

As should be clear from the above, the recv interrupt will not fire until you kick off the receive operation.

This person uses a button pressed ISR which will fire whenever you press the button and that is why their solution works. I’d still recommend to use semaphore or task notification instead of suspend/resume.

The point to understand is what will cause the ISR to fire.

Also, you are sharing UART between two tasks without any synchronization which is not right. This post has very good discussion about that - Synchronizing UART TX

1 Like

Hi @aggarg
Thanks for all the info

I am able to implement my own interrupt handler and it works fine for Tx and Rx.
This is the case where I implemented all my code in handler itself.

But when am trying to implement the ISRTask(Basically jump from interrupt handler to this task and implement the handler code inside it), it still doesnt jump to the ISRTask.

Can you compare your code to the one that I shared in this post and try to sopt what is wrong?

The way you phrase this question implies that you still don’t understand the control flow well enough. There is no such thing as an “ISR task,” and the ISR will never “jump” to a task. ISRs and tasks are completly disjoint and independent threads of control flow. ISRs and tasks can interact with each other unidirectionally via the mechanisms that the OS provides, and that is well documented. They can also be closely coupled in that when an ISR does its fast pre-processing of I/O, the ISR may request that the corresponding post processing task gets scheduled as soon as possible after the ISR has finished. I recommend you familiarize yourself with this (admittedly not always intuitively clear) control flow sequence to get a better understanding of the OS does foryou.

1 Like