01
program

The Cause of the Issue
The cause of the issue originated from my self-made STM32H5 board, where I successfully ported the ST7789 driver and optimized it using DMA. I am very satisfied with the refresh rate of this screen.

So I thought, why not arrange a GUI interface for the screen? (This is also why I chose a touchscreen) I planned to install the TouchGFX framework.
While installing TouchGFX, I also enabled FreeRTOS for easier task management.
However, after installing the FreeRTOS framework, the program crashed! (Mainly because I hadn’t used FreeRTOS for a while, and I encountered many pitfalls this time). So I spent an entire night troubleshooting~
02
First

The Annoying System Timer
Through debugging, I found that the program would hang at HAL_Delay after FreeRTOS completed initialization. However, if I called the screen display before FreeRTOS initialization, there was no problem. Clearly, the HAL_Delay function was causing the hang.
Here, we will explain the running logic of the HAL_Delay function.
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ /* USER CODE BEGIN Callback 0 */ /* USER CODE END Callback 0 */ if (htim->Instance == TIM12) { HAL_IncTick(); } /* USER CODE BEGIN Callback 1 */ /* USER CODE END Callback 1 */}
When we use the timing base function (the default is the system Systick timer, which I replaced with a hardware timer), when the system timer triggers an interrupt, it increments the count value.
__weak void HAL_Delay(uint32_t Delay){ uint32_t tickstart = HAL_GetTick(); uint32_t wait = Delay; /* Add a freq to guarantee minimum wait */ if (wait < HAL_MAX_DELAY) { wait += (uint32_t)(uwTickFreq); } while ((HAL_GetTick() - tickstart) < wait) { }
The purpose of Delay is to wait in a busy loop until the count value reaches the expected value. It is evident that this is a very inefficient method and is not suitable for the FreeRTOS framework.
The FreeRTOS framework provides a vTaskDelay to implement non-blocking delays.
Moreover, the most critical point is that it is not FreeRTOS that disables the HAL_Delay function; it is actually FreeRTOS’s own mechanism.
The FreeRTOS scheduler itself does not directly manage hardware interrupt priorities but relies on the underlying hardware platform (to implement interrupt priority handling). Certain interrupts must have a priority higher than FreeRTOS’s interrupt nesting priority to prevent the task scheduler from being affected. This should be where the FreeRTOS scheduling issue occurred.
FreeRTOS will set a “maximum syscall interrupt priority” based on the macro configMAX_SYSCALL_INTERRUPT_PRIORITY. Any priority lower than this (the higher the number, the lower the priority) is considered a critical interrupt. The BaseTime priority is by default 15, the lowest priority (this is also why the HAL_Delay cannot be used in the interrupt function, because the priority is too low and will be interrupted), so we can solve this problem by raising the priority of the system timer.

When we modify the priority of the TimeBase task to above 5, we can solve this problem by raising the priority.
However, this is only a temporary solution; the correct approach should be to modify the screen driver and change HAL_Delay to a non-blocking delay vTaskDelay to meet the needs of FreeRTOS.
02
Another

Just When One Wave Calms, Another Rises
We just solved the HAL_Delay problem, and the screen initialization can proceed normally, but when it comes to refreshing the screen, a new problem arises.
while(sent_bytes < total_bytes) { chunk_size = ((total_bytes - sent_bytes) > 65534) ? 65534 : (total_bytes - sent_bytes); HAL_Delay(1); HAL_SPI_Transmit_DMA(&ST7789_SPI_PORT, (uint8_t*)data + sent_bytes, chunk_size); while(!dma_transfer_done) { // Wait for transmission to complete } dma_transfer_done = 0; sent_bytes += chunk_size; HAL_Delay(1); }
Because we used polling to wait for the transmission to complete, this flag is defined by us and assigned in the SPI_DMA callback function. However, an abstract issue occurred again.
The DMA interrupt also failed!

The DMA transmission interrupt happens to be 5, which happens to be in a critical interrupt, leading to this interrupt failure. This caused the screen transmission to be unusable. However, in CubeMX, this priority cannot be modified to a higher priority. The reason is the macro definitions set in front of FreeRTOS; priorities 0-4 can interrupt FreeRTOS, which affects FreeRTOS’s real-time performance.
Therefore, in CubeMX, we can only set priorities 5-15.

However, to let the program run, we can modify the configuration file to change the priority limit to 6, allowing our DMA to trigger interrupts.
03
End

Reflection
In fact, the method of modifying the interrupt above is not advisable; there are some issues in my program that cause FreeRTOS to hang in certain places. FreeRTOS itself does not directly disable low-priority interrupts, but low-priority interrupts may be affected in certain situations, especially when the priorities of these interrupts conflict with FreeRTOS’s task scheduling mechanism or SysTick interrupts. There may be a priority inversion issue occurring in certain places, preventing various function interrupts from entering, and I have not yet found the root cause of the problem.
However, everyone needs to have a clear understanding of these basic knowledge that may lead to potential problems while writing code, so as not to be unable to judge the situation when problems arise.
However, after careful study, although this approach can temporarily solve the problem, the actual reason causing this issue might be something else, possibly related to the selected heap.