STM32, Arduino environment, ST7735 screen. Bugs and workarounds.

I was working with the ST7735 screen in a project at NTHUEE 822 Maker Space. We were controlling the screen with an STM32F103C8T6 Blue Pill and Adafruit's ST7735 & ST7789 library. And we ran into a very interesting bug, which was caused by the value of "pins" in the STM32 Arduino core. In this article, I will explain the cause of the bug and the workaround for current version of the libraries.

Brief introduction of the ST7735 screen

No, the "ST" in ST7735 does not mean ST Microelectronics. It is a display controller IC made by another company call Sitronix. It communicates with the microcontroller through SPI and some additional signals like data/command and reset. Since we are just using Adafruit's library, I'm not diving into the details of the ST7735. But please remember the reset signal, it will become one of today's focus.

I do have a complaint about the naming of the screen module's pins. Although the screen uses SPI, the communication pins on the module is named as SCL and SDA, which is for I2C. They should be named SCLK and MOSI, respectively.

STM32 Blue Pill & Arduino core

Since most members of our team never used an STM32 before, we decided to use the STM32 Arduino core and programme it with Arduino IDE. We were only making a game, so the trade off between performance and developing time was worth it. To upload the compiled binaries to the STM32, we used a Raspberry Pi Pico as an SWD adapter.

The example code from Adafruit's library

Adafruit wrote an excellent example code (graphicstest) to get us started. It includes a very detailed guide on different ways to initialise the screen and SPI. We can initialise the ST7735 with or without the reset and chip select signals, or use custom SPI pins. I highly recommend reading through all the comments in this example to learn about the library.

Adafruit example code in the ST7735 library.

In our initial testing, we used all signals and the default SPI pins on the STM32F103C8T6. The wiring between the Blue Pill and the screen is listed in the table below.

Screen STM32
GND GND
VDD 3.3V
SCL PA5
SDA PA7
RST PA4
DC PA3
CS PA2
BLK not connected

I modified the aforementioned example code to use the correct pins on the STM32.

After compiling and uploading, we were met with a white blank screen. If the example is running correctly, it should look like this (not my video). We recompiled the code for a Raspberry Pi Pico 2 running arduino-pico and the code worked without any issue. Something very weird went wrong.

Investigation

We used a logic analyser to look at the signals between the ST7735 and the STM32. We found two signals being abnormal. First, the chip select signal is always high, which means that the chip is never selected. Second, the reset signal never goes low during startup.

The waveform captured by the logic analyser.
The signals are CS, DC, RST, MOSI and SCLK from top to bottom.

Then, we looked into the source code of the Adafruit ST7735 to figure out what went wrong. The CS and RST pins on the ST7735 is optional in this library and the way to disable it is to set them to -1. However, the Adafruit library actually treats all "negative pin numbers" as disabled, as shown below. But, why will the pin number be negative?

Source code from Adafruit's ST7735 library.
The pin variable has to be greater than or equal to 0, otherwise the RST or CS pin will be treated as disabled.

To find out why, we need to look at the definitions in the STM32 Arduino core. What does the macro PA2 actually stand for? It turns out to be 0xC2 in hexadecimal, which is -62 in decimal if intepreted as 8-bit signed integer. Therefore, although we supplied a valid pin number for the CS pin, the library still determines that we are disabling this pin. The same story goes for the RST pin as well.

Multiple layers of definitions behind the "PA2" macro.

Workaround

If we look at the pin macro definitions in the STM32 Arduino core, we can find some interesting definitions. The pins that are connected to the ADC has an extra layer of macros, which ultimately lead to value between 0xC0 and 0xC9.

The macro definitions in the STM32 Arduino core source file.
Pins PA0 to PA9 has an extra layer of macros, which is the cause of the issue.

The simple workaround is to avoid PA0 to PA9 for the CS and RST signals. I moved my CS and RST signals to PB11 and PB10, respectively. The updated pin connection is listed below and you can download the code for STM32 with working pin assignment here. This way, the code will run without issue.

Screen STM32
GND GND
VDD 3.3V
SCL PA5
SDA PA7
RST PB10
DC PA3
CS PB11
BLK not connected