USART interrupt handler is invoked only once

Hello Team,

I am seeing an issue when I use Free RTOS API inside USART interrupt handler.
I am suspecting the issue could be with setting priority, even though I set the value correctly, but not sure why interrupt handler called once and not getting invoked 2nd time onwards.

Let me provide few details about priority,

#define configPRIO_BITS 3
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 2
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))

So configMAX_SYSCALL_INTERRUPT_PRIORITY value is 64 (0x40) => That corresponds to logical priority level 2
I am using LPC55s69, and it uses 3 bits for priority level.
Valid priority values in hardware: 0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0

I set the interrupt priority value to 3 → NVIC_SetPriority(USART2, 3);

In the interrupt handler, I am using rtos API like this,

BaseType_t xHigherPriorityTaskWoken = pdFALSE;
						xSemaphoreGiveFromISR(rcvsemaphore, &xHigherPriorityTaskWoken);
						portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

in the task handler there is a wait call,
if(xSemaphoreTake(rcvsemaphore, portMAX_DELAY) == pdTRUE)

The task creation is like this,
xTaskCreate(ComRcvTask, (const char*) “ComRcv”, 1024, NULL, 4, NULL);

I am using FreeRTOS Kernel V10.5.1, I know there is a new version, but I am yet to migrate.

Observation:

  1. When the data arrives at USART RX, in the interrupt handler it is stored in a local buffer and notified to the task handler for further processing.

Issue:
In the 2nd iteration, when the data arrives the control never reaches interrupt handler?
Even I debugged the code, but I am not seeing anything in the stack (I can only see Thread 1 running)

Can someone please let me know, what is missing?

Regards,
San

So without calling FreeRTOS API the ISR is invoked as expected / each time data arrives ?
In other words the USART low level driver is working properly ?

we need to see more complete source code instead of pseudo code and fragments.

How to you initialize the UART? After successful reception of a character, do you correctly clear the interrupt source and reenable the interrupt? Do you check for transmission errors such as framing or parity error? Did you verify that the first interrupt invocation yields the expected character? Are you using HAL support or is that a barebone UART driver?

Did you make sure that you have not encountered Fault? How do you know task 1 (what is that?) is still running? Is your sys tick handler still invoked after that first interrupt?

Yes , that is correct. If FreeRTOS API is not invoked from interrupt handler, data is arriving.

To be clear, if you just comment the call to xSemaphoreGiveFromISR and not change anything else, the ISR is getting invoked correctly?

Yes, I even verified that. The data is arriving correctly.

Please find the complete source code,

void USART2_IRQHandler(void)
{
	uint8_t data;

	while ((kUSART_RxFifoNotEmptyFlag | kUSART_RxError | kUSART_RxFifoFullFlag) & USART_GetStatusFlags(USART2))
	{
		data = USART_ReadByte(USART2);
		
		
		if (((rxHeadIndex + 1) % BUFFER_SIZE) != rxTailIndex)
		{
			rxBuffer[rxHeadIndex] = data;
			rxHeadIndex++;			
			rxHeadIndex %= BUFFER_SIZE;
			
			if( '\r' == data )
			{
				//METHOD-1 (xSemaphoreGiveFromISR)
				BaseType_t xHigherPriorityTaskWoken = pdFALSE;
				xSemaphoreGiveFromISR(usarrcvDataSemaphore, &xHigherPriorityTaskWoken);
				portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
				
				
				//METHOD-2 (xTaskResumeFromISR) (Even tried this also)
				//BaseType_t xHigherPriorityTaskWoken = pdFALSE;						
				//xTaskNotifyFromISR(LMCScComRcvTask_handle, 0, eNoAction, &xHigherPriorityTaskWoken);

                                break;
			}
				
		}
	}
	SDK_ISR_EXIT_BARRIER;
}

void usart_init()
{
	USART_GetDefaultConfig(&usart2_config);
	usart2_config.baudRate_Bps = 115200U;
	usart2_config.enableTx = true;
	usart2_config.enableRx = true;
	usart2_config.parityMode = kUSART_ParityDisabled;
	usart2_config.stopBitCount = kUSART_OneStopBit;
	
	USART_Init(USART2, &usart2_config, USART2_CLK_FREQ);
	
	USART_EnableInterrupts(USART2, kUSART_RxLevelInterruptEnable | kUSART_RxErrorInterruptEnable);
	
	EnableIRQ(USART2_IRQn);
}


void usartRcvTask(void *voidPtrParameters)
{
	for(;;)
	{
		if(xSemaphoreTake(usarrcvDataSemaphore, portMAX_DELAY) == pdTRUE)
		{
			// process the received data
		}
	}
	return;
}

void usart_receiveTask_init()
{
	xTaskCreate(usartRcvTask, (const char*) "usartRcv", 1024, NULL, 4, NULL);
}

int main()
{
	NVIC_SetPriority(USART2_IRQn, 3);
	
	usart_init();
	
	usart_receiveTask_init();
	
	// Start the scheduler so the created tasks start executing
	vTaskStartScheduler();
	
	/* The control never reaches here as the scheduler will now be running.
       If the control reaches here, then it is likely that there was an
       insufficient heap available for the idle task to be created. */
	for(;;);
	
	return 0;	
}

I guess that your applications stucks in your configASSERT macro/function which is usually a for-ever loop with interrupts disabled. Using a debugger and halting/stopping the target should give a hint wether the program hangs there or not.
The reason could be that

seems wrong. Do you mean 3 << (8-3) with logical interrupt prio 3 and 3 prio bits ?
Btw. I’d recommend to use task notifications for ISR to post-processing task signaling as you also tried.

Check your implementation of NVIC_SetPriority and see if it shits the priority value or it expects a shifted value. As @hs2 suggested, break your code in the debugger and see what it is doing.

Ok sure, I will check that again.
Can I share the FreeRTOSConfig.h to see if something is missing?

Sure. You’ll probably still need to do the others things we suggested.

I already tried, using the debugger,
when I comment the free rtos API in interrupt handler, I can see the new data.
But when I include the free RTOS api in the interrupt handler, the control is stuck in the free rtos task handler, in the 2nd iteration (even though there is a new data arrived on the USART RX), and interrupt handler never invoked.

if (xTaskNotifyWait(0x00,          // Don't clear any bits on entry
													0xFFFFFFFF,    // Clear all bits on exit
													&ulNotificationValue, // Store the notification value
													portMAX_DELAY) == pdTRUE)

Please find the FreeRTOSConfig.h

/*
 * FreeRTOS Kernel V10.5.1
 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 *
 */

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#include "LPC55S69_cm33_core0.h"

#if defined(__ICCARM__)||defined(__CC_ARM)||defined(__GNUC__)
    /* Clock manager provides in this variable system core clock frequency */
    #include <stdint.h>
    extern uint32_t SystemCoreClock;
#endif

/*-----------------------------------------------------------
 * Application specific definitions.
 *
 * These definitions should be adjusted for your particular hardware and
 * application requirements.
 *
 * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
 * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
 *
 * See http://www.freertos.org/a00110.html.
 *----------------------------------------------------------*/
#ifndef configENABLE_FPU
  #define configENABLE_FPU                      0
#endif
#ifndef configENABLE_MPU
  #define configENABLE_MPU                      0
#endif
#ifndef configENABLE_TRUSTZONE
  #define configENABLE_TRUSTZONE                0
#endif
#ifndef configRUN_FREERTOS_SECURE_ONLY
  #define configRUN_FREERTOS_SECURE_ONLY        1
#endif

#define configUSE_PREEMPTION                    1 // preemptive RTOS scheduler
//#define configUSE_PREEMPTION                    0 // Co-operative RTOS Scheduler
#define configUSE_TICKLESS_IDLE                 0
#define configCPU_CLOCK_HZ                      (SystemCoreClock)
#define configTICK_RATE_HZ                      ((TickType_t)200)
//#define configTICK_RATE_HZ                      ((TickType_t)1000)
#define configMAX_PRIORITIES                    5
//#define configMINIMAL_STACK_SIZE                ((unsigned short)90)
#define configMINIMAL_STACK_SIZE				( ( unsigned short ) 100 )
#define configMAX_TASK_NAME_LEN                 20
#define configUSE_16_BIT_TICKS                  0
#define configIDLE_SHOULD_YIELD                 1
#define configUSE_TASK_NOTIFICATIONS            1
#define configUSE_MUTEXES                       1
#define configUSE_RECURSIVE_MUTEXES             1
#define configUSE_COUNTING_SEMAPHORES           1
#define configUSE_ALTERNATIVE_API               0 /* Deprecated! */
#define configQUEUE_REGISTRY_SIZE               8
#define configUSE_QUEUE_SETS                    0
#define configUSE_TIME_SLICING                  0
#define configUSE_NEWLIB_REENTRANT              0
#define configENABLE_BACKWARD_COMPATIBILITY     1
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
#define configUSE_APPLICATION_TASK_TAG          0

/* Memory allocation related definitions. */
#define configSUPPORT_STATIC_ALLOCATION         0
#define configSUPPORT_DYNAMIC_ALLOCATION        1
#define configTOTAL_HEAP_SIZE                   ((size_t)(10240))
#define configAPPLICATION_ALLOCATED_HEAP        0

/* Hook function related definitions. */
#define configUSE_IDLE_HOOK                     0
#define configUSE_TICK_HOOK                     0
#define configCHECK_FOR_STACK_OVERFLOW          0
#define configUSE_MALLOC_FAILED_HOOK            0
#define configUSE_DAEMON_TASK_STARTUP_HOOK      0

/* Run time and task stats gathering related definitions. */
#define configGENERATE_RUN_TIME_STATS           0
#define configUSE_TRACE_FACILITY                1
#define configUSE_STATS_FORMATTING_FUNCTIONS    0

/* Co-routine related definitions. */
#define configUSE_CO_ROUTINES                   0
#define configMAX_CO_ROUTINE_PRIORITIES         2

/* Software timer related definitions. */
#define configUSE_TIMERS                        1
#define configTIMER_TASK_PRIORITY               (configMAX_PRIORITIES - 1)
#define configTIMER_QUEUE_LENGTH                10
#define configTIMER_TASK_STACK_DEPTH            (configMINIMAL_STACK_SIZE * 2)

/* Define to trap errors during development. */
#define configASSERT(x) if((x) == 0) {taskDISABLE_INTERRUPTS(); for (;;);}

/* Optional functions - most linkers will remove unused functions anyway. */
#define INCLUDE_vTaskPrioritySet                1
#define INCLUDE_uxTaskPriorityGet               1
#define INCLUDE_vTaskDelete                     1
#define INCLUDE_vTaskSuspend                    1
#define INCLUDE_vTaskDelayUntil                 1
#define INCLUDE_vTaskDelay                      1
#define INCLUDE_xTaskGetSchedulerState          1
#define INCLUDE_xTaskGetCurrentTaskHandle       1
#define INCLUDE_uxTaskGetStackHighWaterMark     0
#define INCLUDE_xTaskGetIdleTaskHandle          0
#define INCLUDE_eTaskGetState                   0
#define INCLUDE_xTimerPendFunctionCall          1
#define INCLUDE_xTaskAbortDelay                 0
#define INCLUDE_xTaskGetHandle                  0
#define INCLUDE_xTaskResumeFromISR              1

/* This value is defined also by CMSIS macro __NVIC_PRIO_BITS, but it cannot
be used here because of conflict in *.h inclussion into assembler. */
#define configPRIO_BITS 3 /* 8 priority levels */

/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY ((1U << (configPRIO_BITS)) - 1)

/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 2

/* Interrupt priorities used by the kernel port layer itself.  These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))

/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler


/* Map the FreeRTOS printf() to the logging task printf. */
#define configPRINTF( x )          vLoggingPrintf x

/* Map the logging task's printf to the board specific output function. */
#define configPRINT_STRING

/* Sets the length of the buffers into which logging messages are written - so
 * also defines the maximum length of each log message. */
#define configLOGGING_MAX_MESSAGE_LENGTH            256

/* Set to 1 to prepend each log message with a message number, the task name,
 * and a time stamp. */
#define configLOGGING_INCLUDE_TIME_AND_TASK_NAME    1

#endif /* FREERTOS_CONFIG_H */

NVIC_SetPriority is mapped to __NVIC_SetPriority

If you see below implementation the priority value is getting shifted.

 NVIC->IPR[((uint32_t)IRQn)]               = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);

What exactly do you mean by this?
Where is the processor running?
How is it “stuck” in that code?

My guess is that your processor is stuck in an assert somewhere, disabling interrupts, or in some badly written critical section that isn’t ending.

It may have nothing to do with the ISR, but what the task does when it gets that first message that causes the problem (or another task that gets a message from that task).

It seems like the control is getting stuck here,

May I know what is wrong with this,
NVIC_SetPriority(USART2_IRQn, 3);

As per my understanding these are the possible priority values in hardware,
0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0

3 << (8-3) → Yields to 96 (0x60 in hex) => Which is mapping to index 3 in the above mentioned values.

Can you which exact line? Even better if you can share the debugger image showing callstack and everything.

No problem with this.

It looks like that is asserting on a bad value for the uxIndexToWait value, if that is where the code is stopping. Since it seems you are not using the Index version of the macro, that should be a constant 0, and since you don’t define configTASK_NOTIFICATION_ARRAY_ENTRIES, it should have been defaulted in FreeRTOS.h to 1, so it should have passed.

You might check that you haven’t gotten version of files mixed (version before and after adding the indexed functions)

As per my understanding the control is getting stuck here,

In this file → freertos\freertos-kernel\tasks.c, in below function


#if ( INCLUDE_vTaskSuspend == 1 )

    void vTaskSuspend( TaskHandle_t xTaskToSuspend )


if( xSchedulerRunning != pdFALSE )
        {
            /* Reset the next expected unblock time in case it referred to the
             * task that is now in the Suspended state. */
            taskENTER_CRITICAL();    **======> The control is stuck here**
            {
                prvResetNextTaskUnblockTime();
            }
            taskEXIT_CRITICAL();
        }

In the call stack I can see only this,
image

Sorry, I didn’t get you what you meant by above statement.

If you somehow got a 10.1 FreeRTOS.h header file mixed with a 10.5 FreeRTOS source set, it could leave some symbols not defined properly.

As to your point you are pointing at, I can’t see how it woucl be “stuck” there, as there is no loop to stick.

Your call stack also doesn’t show you in vTaskSuspend(), so I don’t know why you think you are stuck there.