Interrupts cannot be used with mutex, an ISR should not be blocked by anything. Mutexes include a priority inheritance mechanism, which only makes sense when the mutex is given and taken from a task, not an interrupt. An ISR cannot block to wait for a resource guarded by a mutex to become available.

In this example mutex and interrupts are used, it is a solution to manage interruption and tasks controlled by mutex semaphore.

Code Example:

#include "bsp.h"

#define NONE_MSG 0                      /* There are no message in Queue */
#define TASK1    "Task1 execution"      /* String of Task 1 */
#define TASK2    "Task2 execution"      /* String of Task 2 */

SemaphoreHandle_t  xMutex;              /* Mutex semaphore */
QueueHandle_t QueueMsg;                 /* Queue handler */

void Task_Print( void *pvParameters );  /* Function reused for the Tasks */

void Print_on_Terminal(char *str);      /* Function used to print on terminal */

int main( void )
{
    HAL_Init();

    SEGGER_SYSVIEW_Conf( );
    SEGGER_SYSVIEW_Start( );

    HAL_NVIC_SetPriority( EXTI4_15_IRQn, 2, 0 );                    /* Set priority of interrupts Buttons */
    HAL_NVIC_EnableIRQ( EXTI4_15_IRQn );                            /* Enable the extenal interrupts */

    xMutex = xSemaphoreCreateMutex();                               /* Create Mutex Semaphore */
    QueueMsg = xQueueCreate( 5, sizeof(const char*) );              /* Create the Queue for messages from ISR */
    
    xTaskCreate( Task_Print, "Task1_PrintMsg", 240, TASK1, 1, NULL );   /* Create Task1 and pass a string as parameter */
    xTaskCreate( Task_Print, "Task2_PrintMsg", 240, TASK2, 1, NULL );   /* Create Task2 and pass a string as parameter */
    
    vTaskStartScheduler();                                          /* Init the Kernel */
}

void Task_Print( void *pvParameters )                               /* Function used for Tasks created */
{
    char *parameter = (char*) pvParameters;                         /* Cast the parameter to be used as a string */

    for(;;)
    {
        if( xSemaphoreTake( xMutex, 0 ) == pdTRUE )                 /* Check if Mutex is avasilable to take */
        {
            Print_on_Terminal( parameter );                         /* Call the function to print string on Terminal */
            xSemaphoreGive( xMutex );                               /* When function finishes, give the mutex semaphore */
        }    

        vTaskDelay(200);                                            /* Task period */
    }
}

void Print_on_Terminal( char *str )                                 /* Function used to print messages on Terminal */
{
    if( uxQueueMessagesWaiting(QueueMsg) > NONE_MSG )               /* Check if Queue has a message from ISR */
    {
        const char *string_ISR;                                     /* Create a variable to Store msg of ISR */
        xQueueReceive(QueueMsg, &string_ISR, portMAX_DELAY);        /* Read the Queue */
        SEGGER_SYSVIEW_PrintfHost( string_ISR );                    /* Print on Terminal the ISR message */
    }   
    
    SEGGER_SYSVIEW_PrintfHost( str );                               /* Print the String parameer of the task */
}

void HAL_GPIO_EXTI_Rising_Callback( uint16_t GPIO_Pin )             /* Callback of interrupt */
{
    SEGGER_SYSVIEW_RecordEnterISR();                                /* Record enter of IRS on SytemView timeline */
    
    const char* ISR_Msg = "ISR Msg";                                /* Create the variable that contain the Data to be send to Queue */
    xQueueOverwriteFromISR( QueueMsg, &ISR_Msg, 0 );                /* Overwrite on Queue buffer if it is full */

    SEGGER_SYSVIEW_RecordExitISR();                                 /* Record exit of IRS on SytemView timeline */
    portEND_SWITCHING_ISR( pdTRUE );                                /* Change context */
}

Observe the code, and notice that the tasks are using the same function Task_Print, each task will print different messages, and the parameter pvParameters passes this message to the function. Function Print_on_Terminal, first check if there are messages to read to be printed on terminal, after print the message of the Task parameter.

When the application runs first the task going to print their messages from the parameter pvParameters, which is send to the function Print_on_Terminal that will ignore the Reception of Queue message. when the button is pressed the ISR callback will send the message ISR to the Queue Buffer, when any of the 2 tasks execute again to call the Print_on_Terminal this time will read the Queue buffer and print the data of ISR. observe that Task is using mutex but ISR message is not affected by the semaphore.

SystemView Output

Observe on the terminal that both tasks are executing normally printing the message of the function parameter of each task, when the button is pressed interruption is generated and prints the ISR message on the terminal, notice that Task1 prints the ISR message and their message function parameter.

Document

Timeline:

Document

Tasks run normally, the first execution of the task just takes the mutex semaphore, prints the parameter function of each task and gives the mutex.

When the button is pressed, an ISR is generated which calls the ISR callback to send to the Queue buffer the ISR message.

The next execution of a task, will call the function "Print_on_Terminal" to check if there is a message on Queue, in this case function reads first the ISR message and prints it on the terminal, to next prints the parameter function of the task on the terminal.