Idle task not yielding immediately after ISR until next tick when preemption enabled

Hi all, I have run into an interesting issue that seems to be related to the scheduler. I’m on an STM32F4 (using CubeMX to generate HAL and FreeRTOS code). For the purposes of this discussion let’s say I have 1 running task (for gathering read data from a uart). Here is the task code: ~~~ for (;;) { /* unmask receive data register not empty interrupt / SET_BIT(stx->uart->Instance->CR1, USART_CR1_RXNEIE); / unmask receive parity error interrupt */ SET_BIT(stx->uart->Instance->CR1, USART_CR1_PEIE); /* wait for receive data register not empty interrupt */ rstat = xSemaphoreTake(stx->rxSemaphore, portMAX_DELAY); assert(rstat == pdPASS); /* receive byte */ if (stx->uart->Init.Parity == UART_PARITY_NONE) { buff = (char)(stx->uart->Instance->DR & 0x000000FF); } else { buff = (char)(stx->uart->Instance->DR & 0x0000007F); } /* place byte in read queue */ rstat = xQueueSendToBack(stx->rxFIFO, &buff, portMAX_DELAY); assert(rstat == pdPASS); } ~~~ Here is the relevant part of the receive byte ISR: ~~~ if ( ((statusreg & USARTSRRXNE) != RESET) && ((cr1 & USARTCR1RXNEIE) != RESET) ) { /* rx data register byte available */ /* mask receive data register not empty interrupt */ CLEARBIT(stx->uart->Instance->CR1, USARTCR1RXNEIE); /* unblock receive task */ rstat = xSemaphoreGiveFromISR(stx->rxSemaphore, NULL); assert(rstat == pdPASS); } ~~~ Here is the source of error / confusion. Using this code, sending any more than 1 byte in a short period of time causes a uart overrun error interrupt. I have traced the code and the problem appears to be that the idle task does not yield properly. Here is the sequence of events: – read task is blocked on xSemaphoreTake() – ISR is called and reaches xSemaphoreGiveFromISR() – after this call, the read task is put in the ready state (according to Atollic TrueStudio FreeRTOS Task List tool, which displays all running tasks, state, stack size, etc.) – the ISR exits and we return to line 3258 in tasks.c (inside the idle task): ~~~ (3258) if( listCURRENTLISTLENGTH( &( pxReadyTasksLists[ tskIDLEPRIORITY ] ) ) > ( UBaseTypet ) 1 ) (3259) { (3260) taskYIELD(); (3261) } ~~~ – idle task now appears to loop between lines 3258 and 3235 (the if statement on 3258 evaluates to FALSE and does not taskYIELD() ) (3235) prvCheckTasksWaitingTermination(); – the uart read task only resumes at the next TICK event The overrun error then occurs because by the time the uart read task starts running, another byte has already been received in the uart. Obviously the desired behavior is for the idle task to immediately yield to the uart read tasks. Now, if I disable configUSE_PREEMPTION, everything works and the idle task yields immediately (line 3243 in tasks.c). Am I misunderstanding what preemption is supposed to do? Thanks, Jacob

Idle task not yielding immediately after ISR until next tick when preemption enabled

Your ISR is ignoring the Higher Priority Task was woken signal (and in fact passing NULL pointers there, so you are not invoking premption. Fix that and you will fix your problem.

Idle task not yielding immediately after ISR until next tick when preemption enabled

Ah. Failure to read documentation :). Appreciate it, thanks! Jacob