This situation is quite amusing; it started as a problem with the color screen flickering, but after investigation, we ended up looking into GPIO configurations, which was unexpected.
At that time, we were developing a high-end coffee machine equipped with a 3.5-inch TFT color display. The resolution wasn’t high, but the UI was custom-designed, requiring smooth interface performance. The screen was connected to the STM32 via SPI, and the interface was specifically selected, with the main controller being the STM32F407.
Initially, everything was fine during debugging; the marquee, menus, and animations worked without issues, achieving frame rates of over 20 FPS. However, when we reached the client’s site, problems arose. The screen would occasionally ‘tear,’ sometimes displaying horizontal stripes or garbled text, as if the video memory was being pulled.
We initially suspected unstable power supply, spending three days analyzing the power waveform and trying three different DC-DC converters, but the problem persisted. We then suspected the SPI timing and began adjusting the prescaler incrementally:
// SPI1, APB2 84MHz, prescaler settings
SPI1->CR1 |= SPI_BAUDRATEPRESCALER_4; // 21MHz
We adjusted the prescaler from 2 to 256, and by the end, we were almost in tears as the screen ‘tearing’ still occurred sporadically, sometimes good, sometimes bad.
Strangely, some boards never had issues, while others displayed artifacts immediately upon power-up. We even suspected there might be a problem with the screen batch and tried switching to parallel drive… but the issue was still not completely resolved.
In the end, an old colleague stared at the waveform for half an hour and suddenly said:
“Have you configured the GPIO speed for these SPI pins?”
We were taken aback; GPIO speed configuration?
The Problem Was in OSPEEDR — GPIO Output Speed
STM32’s GPIO has speed grade configurations, which do not refer to the speed of the levels but rather to the driving capability of the rising/falling edges, affecting the steepness of the signal edges.
At that time, we had configured the SPI’s SCK, MOSI, and NSS using AF functionality, and the GPIO configuration was as follows:
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
This essentially told the chip: “Take your time switching these pins, no rush.”
But what is SPI? It’s a clock running at tens of MHz, requiringclean and sharp edge transitions. If you let it switch slowly, the edges become sluggish, leading to signal ghosting and reflections, especially with a display that is particularly sensitive to timing, causing immediate issues.
We changed the speed of the relevant pins to High:
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
Recompiled, flashed, and powered on — the display was clean, smooth, and the tearing was completely gone.
At that moment, the entire office fell silent as everyone pondered: how many years had we overlooked this?
After Adjusting SPI Prescaler for So Long, It Wasn’t Its Fault
The SPI baud rate prescaler we had been fiddling with was actually fine. Running at 21MHz, the screen could handle it perfectly; it was just that the GPIO edges weren’t ‘snappy’ enough, causing the signal boundaries to be blurred.
If you slow down the prescaler, the sluggish edges might mask the problem; only when you speed it up does the issue become apparent. The root cause wasn’t the baud rate but rather whether the signal could be transmitted effectively.
The NSS Pin Also Added Some Confusion
There was also a small episode regarding NSS (chip select) management. STM32’s SPI can let the NSS pin operate in hardware mode or be manually controlled. Initially, we opted for convenience and enabled hardware NSS:
SPI1->CR2 |= SPI_CR2_SSOE;
However, the screen initialization occasionally failed.
Why? Because NSS is active low, and in hardware mode, NSS is only valid when SPI is enabled and data is being transmitted. Sometimes, our SPI wasn’t enabled yet, and NSS was already high, causing the screen to determine “communication error” immediately.
In the end, we disabled hardware NSS and controlled it manually using GPIO:
// Pull down chip select
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
// Send data
HAL_SPI_Transmit(&hspi1, buf, len, timeout);
// Pull up chip select
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
This method was a bit rudimentary, but it worked.
Later, we summarized this experience into a phrase and posted it on the project whiteboard:
“The speed of SPI not only depends on the registers but also on whether your GPIO is willing to cooperate.”
Now, whenever a newcomer asks me, “How do I troubleshoot SPI display issues?” I always start by asking, “Did you adjust the GPIO speed?”