cancel
Showing results for 
Search instead for 
Did you mean: 

[SOLVED] FreeRTOS mutex issue in ISR with stm32 HAL and UART DMA TX

KShim.1738
Associate III

I tried to make a formatted print function, which would not be interrupted by another call to HAL_UART_Transmit_DMA(). I have two threads which freely calls HAL_UART_Transmit_DMA(). If it's not synchronized, the message would be mixed or lost.

So I adopted FreeRTOS mutex, guSyncUARTTX. to synchronize the printings on the terminal. xSemaphoreTake() are called before printing. The threads which call the formatted print function dbg_printf() has to wait until the mutex is released by HAL_UART_TxCpltCallback() in the DMA ISR context.

I made it sure that guSyncUARTTX is initialized before xSemaphoreTake(), xSemaphoreGiveFromISR() is called. So if the line calling xSemaphoreCreateMutex() is commented out, no mutex related functions will be executed. And I have no big problem in this case. messages are simply mixed or lost if unlucky.

But with the mutex initialization routine xSemaphoreCreateMutex(), it freezes. I found out F7 is looping around in vPortRaiseBASEPRI() by debugging with openocd.

Could someone point out what is wrong with the following code? I'm new to FreeRTOS and not so familar with stm32 HAL libraries either.

static SemaphoreHandle_t guSyncUARTTX = NULL;
 
void taskA(void *pvParameters)
{
    uint32_t reg1, reg2;
    guSyncUARTTX = xSemaphoreCreateMutex();
 
    HAL_UART_Receive_DMA(&huart1, rxbuffer, 1);
 
    while(1)
    {
        vTaskDelay(1000);
        reg1 = READ_REG(huart1.Instance->CR1);
        reg2 = READ_REG(huart1.Instance->ISR);
 
        dbg_printf("shell running 0x%08x 0x%08x!!!\r\n", reg1, reg2);
    }
}
 
void taskB(void *pvParameters)
{
    while(1)
    {
        vTaskDelay(1000);
 
        dbg_printf("taskB running!!!\r\n");
    }
}
 
 
void uart_tx_wait(void) {
    /* dbg_printf() can be called before guSyncUARTTX is initialized */
    if(guSyncUARTTX != NULL)
        xSemaphoreTake(guSyncUARTTX, portMAX_DELAY);
}
 
__attribute__((__aligned__(32))) static char rt_log_buf[CONSOLEBUF_SIZE];
void dbg_printf(const char *fmt, ...)
{
    va_list args;
    size_t length;
 
    uart_tx_wait();
 
    va_start(args, fmt);
    length = vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
    va_end(args);
 
    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)rt_log_buf, length);
}
 
 
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    static BaseType_t xHigherPriorityTaskWoken;
 
    xHigherPriorityTaskWoken = pdFALSE;
 
    /* ISR can be called before guSyncUARTTX is initialized */
    if(guSyncUARTTX != NULL)
        xSemaphoreGiveFromISR(guSyncUARTTX, &xHigherPriorityTaskWoken);
}
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    unsigned char c = huart->Instance->RDR;
 
    HAL_UART_Receive_DMA(huart, rxbuffer, 1);
    BSP_LED_Toggle(LED_GREEN);
}
 

5 REPLIES 5
alister
Lead

The mutex is created as taken. It must be given it before it can be taken.

Suggestion: if this code can really work safely before the mutex is created, it doesn't need the mutex. And have you considered the possibility the mutex could be created after dbg_printf is called and before it completes? If the mutex is really necessary, it must be created before dbg_printf can be called.

KShim.1738
Associate III

You are right!

I read the manual page of xSemaphoreCreateMutex() and it says, "Mutexes cannot be used in interrupt service routines." Which means that I must use xSemaphoreCreateBinary().

About the Binary semaphore,

The semaphore is created in the ’empty’ state, meaning the semaphore must first be given using the xSemaphoreGive() API function before it can subsequently be taken (obtained) using the xSemaphoreTake() function.

I also arranged the functions so that dbg_printf() is not called before guSyncUARTTX is initialized.

But the situation doesn't change much.

I appreciate your help.

now the code is

static SemaphoreHandle_t guSyncUARTTX = NULL;
 
void taskA(void *pvParameters)
{
    uint32_t reg1, reg2;
    guSyncUARTTX = xSemaphoreCreateBinary();
    xSemaphoreGive(guSyncUARTTX);
 
    HAL_UART_Receive_DMA(&huart1, rxbuffer, 1);
 
    while(1)
    {
        vTaskDelay(1000);
        reg1 = READ_REG(huart1.Instance->CR1);
        reg2 = READ_REG(huart1.Instance->ISR);
 
        dbg_printf("shell running 0x%08x 0x%08x!!!\r\n", reg1, reg2);
    }
}
 
void taskB(void *pvParameters)
{
    while(1)
    {
        vTaskDelay(1000);
 
        dbg_printf("taskB running!!!\r\n");
    }
}
 
 
void uart_tx_wait(void) {
    /* dbg_printf() isn't called before guSyncUARTTX is initialized */
    xSemaphoreTake(guSyncUARTTX, portMAX_DELAY);
}
 
__attribute__((__aligned__(32))) static char rt_log_buf[CONSOLEBUF_SIZE];
void dbg_printf(const char *fmt, ...)
{
    va_list args;
    size_t length;
 
    uart_tx_wait();
 
    va_start(args, fmt);
    length = vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
    va_end(args);
 
    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)rt_log_buf, length);
}
 
 
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    static BaseType_t xHigherPriorityTaskWoken;
 
    xHigherPriorityTaskWoken = pdFALSE;
 
    /* ISR isn't called before guSyncUARTTX is initialized */
    xSemaphoreGiveFromISR(guSyncUARTTX, &xHigherPriorityTaskWoken);
}
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    unsigned char c = huart->Instance->RDR;
 
    HAL_UART_Receive_DMA(huart, rxbuffer, 1);
    BSP_LED_Toggle(LED_GREEN);
}
 

Nikita91
Lead II

Mutex created by xSemaphoreCreateMutex can't be released by xSemaphoreGiveFromISR: mutexes can't be used from ISR.

Use a binary semaphore instead of mutex.

Remember to give the semaphore after the creation and before use.

xSemaphoreGiveFromISR() has something to do with the FreeRTOS timer daemon. So I think the timer shall be initialized before using xSemaphoreGiveFromISR(). For the timer initialization, some macros should be added to FreeRTOSConfig.h.

/* Software timer related definitions. */

#define configTIMER_SERVICE_TASK_NAME      "timer"

#define configUSE_TIMERS            1

#define configTIMER_TASK_PRIORITY        (configMAX_PRIORITIES - 1)

#define configTIMER_QUEUE_LENGTH        10

#define configTIMER_TASK_STACK_DEPTH      configMINIMAL_STACK_SIZE

#define INCLUDE_xTimerPendFunctionCall    1 

Still there was another problem to me. CPU was looping around configASSERT( ucCurrentPriority >= ucMaxSysCallPriority); inside vPortValidateInterruptPriority(). I think it's related to interrupt priority settings. One of the recommendations I found on the internet was to comment out the line, which is worse than the proper settings for configMAX_SYSCALL_INTERRUPT_PRIORITY.

Anyway the code above works with those changes above.

[edit] Regarding configASSERT above, don't leave NVIC preemtion priority of the external interrupts 0, which is default.

with STM32 CubeMX, Pinout & Configuration >.> System Core >> NVIC >> NVIC

change the preemption priority to appropriate value.

Thank you for your comment.