Why does it display white screen after removing SysTick_Handler?
What happens if there is no stm32f4xx_it.c
?
If you tested the program without stm32f4xx_it.c
, you won’t get error, but you will see only white screen. Why?
In my stm32f4xx_it.c
, there is only one function with actual declaration:
1
2
3
4
void SysTick_Handler(void)
{
HAL_IncTick();
}
Now, try building and uploading the program with stm32f4xx_it.c
but the SysTick_Handler
part commented out. Although stm32f4xx_it.c
exists, it still displays white screen. Now we know SysTick_Handler
is the matter.
What does SysTick_Handler
do?
In the code above, SysTick_Handler
does one job: call HAL_IncTick()
. HAL_IncTick
increments a global variable “uwTick” used as application time base. Without it, it will somehow block display on screen.
The HAL and many library functions rely on the SysTick interrupt for the system tick (HAL tick). In the HAL implementation, SysTick increments the millisecond counter via
HAL_IncTick()
and this is what makesHAL_Delay()
and other timing functions work.
In short, SysTick_Handler()
is invoked every time HAL_Delay()
is called.
Let’s look at main.c
, line 249:
1
2
3
4
5
6
7
8
9
// Line 249
void LCD_Init(void)
{
HAL_Delay(120);
// Software reset
LCD_WriteCommand(0x01);
HAL_Delay(120);
// ...
And in line 74:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// line 74
while (1) {
// LED blink pattern depends on clock source
if (clock_source == 0) {
// HSE: fast double blink
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_SET);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_SET);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(700);
// ...
As seen, HAL_Delay()
is everywhere. And if you debug it, you can see in the call stack:

that somehow SysTick_Handler
is invoked by HAL_Delay
.
Now it’s more clear. If SysTick_Handler
not defined, it will prevent HAL_Delay
from operating normally, and that’s probably why we can only see the blank screen. But how does it work in detail? Or before, if SysTick_Handler
plays such a crucial role, how did it even build and run the program without it?
Startup File has a weak symbol to SysTick_Handler
Let’s go into ~/.platformio/packages/framework-cmsis-stm32f4/Source/Templates/gcc
. There is a file named startup_stm32f429xx.s
.
In line 143:
.word SysTick_Handler
And in line 272-273:
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
- Line 272: places the value (address) of the symobl
SysTick_Handler
in the vector table. - Line 273:
SysTick_Handler
will be resolved to a weak alias “Default_Handler”.
Thanks to this startup file, the program does not return error even if there is no SysTick_Handler
explicitly defined by user. It will instead implicitly link SysTick_Handler
to a weak alias, which is Default_Handler
. What does Default_Handler
do?
In line 112 of the same startup file:
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
It runs an infinite loop. Now everything seems clear!
When stm32f4xx_it.c was removed, there was no explicit SysTick_Handler
defined, thus the compiler linked SysTick_Handler
to its weak alias Default_Handler
, and Default_Handler
ran an infinite loop, which made the program stuck in HAL_Delay()
forever.
Now, when you define your own SysTick_Handler, the program will refer to the strong link of SysTick_Handler
and increment tick correctly. On the other hand, if you don’t define it, it won’t return any compile error because it refers to the weak link of SysTick_Handler
, which is aliased to Default_Handler
, but Default_Handler
does not do anything except infinitely running a loop. That is why you only see blank white screen.
Summary
- When you define your own SysTick_Handler, the program will refer to the strong link of
SysTick_Handler
and increment tick correctly. - On the other hand, if you don’t define it, it won’t return any compile error because it refers to the weak link of
SysTick_Handler
, which is aliased toDefault_Handler
. - But
Default_Handler
doesn’t do anything except infinitely running a loop. This is why you only see blank white screen.
What is startup file for?
The startup file sets up the vector table, which is an array of pointers. This table tells the processor the memory addresses of the interrupt service routines (ISRs) and exception handlers, enabling the MCU to respond to hardware interrupts and errors.
It’s worthwhile to read this article: https://microdigisoft.com/understanding-the-boot-process-and-startup-file/
References
In fact, this must have been quite a common bug for those using HAL_Delay()
method in their program:
- https://stackoverflow.com/questions/46062122/delay-in-hal-library-hal-delay
- https://community.st.com/t5/stm32-mcus-products/hal-delay/td-p/62617
These are very good articles explaining how SysTick_Handler
works:
- https://community.platformio.org/t/stm32-hal-project-dependent-on-definition-location-of-systick-handler-in-an-unexpected-way/38620
- https://community.st.com/t5/stm32-mcus-products/understanding-systick-handler/td-p/345143