Porting U8G2 Graphics Library to STM32 Part3: SH1106 SPI OLED

The SH1106 is a popular monochrome OLED display driver, commonly used in small screens ranging from 0.96″ to 1.3″, offering high contrast and low power consumption. It communicates via SPI or I²C interfaces, making it suitable for microcontroller projects requiring compact graphical displays.

In this guide, we shall cover the following:

  • STM32CubeMX Configuration.
  • Connection.
  • Firmware Development.
  • Results.

1. STM32CubeMX Configuration:

From the previous project, open u8g2.ioc file as follows:

STM32CubeMX window shall appears.

Next, head to Clock Configuration and set the frequency to 100MHz (or what maximum can be handled by your STM32) as follows:

Next, from Pinout & Configuration, select SPI1 and set to Master Transmit only (Display sends nothing back) as follows:

This will automatically enable PA5 as SCK and PA7 as MOSI as shown below:

Next, configure the SPI as follows:

  • Frame Format to Motorola.
  • Data size to 8-bit.
  • First bit is MSB first.
  • Set prescaler to get between 20 and 25 Mbit/s.
  • Clock Polarity to low.
  • Clock phase to 1 edge

Next, Enable PA0 and PA1 and give a name of CS and DC respectively as shown below:

Thats all for STM32CubeMX configuration. Save the project and this will generate the code.

2. Connection:

The connection as follows:

SH1106 SPI OLED DisplaySTM32F411 Nucleo-64
GNDGND
Vcc5V
SDAPA5 (D13 of Arduino pins)
SCLPA7 (D11 of Arduino pins)
DCPA1 (A1 of Arduino pins)
CSPA0 (A0 of Arduino pins)

3. Firmware Development:

We start off by defining the gpio and delay function as follows:

uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
	switch(msg)
	{
		case U8X8_MSG_DELAY_MILLI:
			HAL_Delay(arg_int);
			break;
		case U8X8_MSG_GPIO_CS:
			HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, arg_int);
			break;
		case U8X8_MSG_GPIO_DC:
			HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, arg_int);
			break;


	}
	return 1;
}

Purpose

This function serves as a bridge between the U8G2 graphics library and the STM32 hardware, handling GPIO control and timing delays.

Function Parameters

  • u8x8_t *u8x8: U8G2 display structure pointer
  • uint8_t msg: Message type indicating what operation to perform
  • uint8_t arg_int: Integer argument (like pin state or delay time)
  • void *arg_ptr: Pointer argument (not used here)

Message Handling

1. U8X8_MSG_DELAY_MILLI

  • Purpose: Implements millisecond delays
  • Implementation: Uses STM32’s HAL_Delay() function
  • Usagearg_int contains the number of milliseconds to delay

2. U8X8_MSG_GPIO_CS

  • Purpose: Controls the Chip Select (CS) pin
  • Implementation: Uses HAL_GPIO_WritePin() to set CS pin state
  • Usagearg_int determines pin state (0 = low, 1 = high)

3. U8X8_MSG_GPIO_DC

  • Purpose: Controls the Data/Command (DC) pin
  • Implementation: Uses HAL_GPIO_WritePin() to set DC pin state
  • Usagearg_int determines pin state (0 = command mode, 1 = data mode)

Next, spi communication as follows:

uint8_t u8x8_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{

	switch(msg)
	{
		case U8X8_MSG_BYTE_SET_DC:
			HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, arg_int);
			break;
		case U8X8_MSG_BYTE_SEND:
			HAL_SPI_Transmit(&hspi1, (uint8_t *)arg_ptr, arg_int, 1000);
			break;
		case U8X8_MSG_BYTE_START_TRANSFER:
			HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);
			break;
		case U8X8_MSG_BYTE_END_TRANSFER:
			HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
			break;

		default: break;

	}
	return 1;
}

Purpose

This function handles the low-level SPI communication between the STM32 microcontroller and the display, managing data/command mode, data transmission, and chip select control.

Message Handling

1. U8X8_MSG_BYTE_SET_DC

  • Purpose: Sets the Data/Command (DC) pin state
  • Implementation: Uses HAL_GPIO_WritePin() to control the DC pin
  • Usagearg_int determines the mode:
    • 0: Command mode (sending display instructions)
    • 1: Data mode (sending pixel data)

2. U8X8_MSG_BYTE_SEND

  • Purpose: Transmits data over SPI
  • Implementation: Uses HAL_SPI_Transmit() to send data
  • Parameters:
    • &hspi1: SPI handle (SPI1 peripheral)
    • (uint8_t *)arg_ptr: Pointer to the data buffer to transmit
    • arg_int: Number of bytes to transmit
    • 1000: Timeout in milliseconds

3. U8X8_MSG_BYTE_START_TRANSFER

  • Purpose: Begins an SPI transaction
  • Implementation: Pulls CS (Chip Select) pin LOW to enable the display chip
  • UsageGPIO_PIN_RESET sets the pin LOW (active)

4. U8X8_MSG_BYTE_END_TRANSFER

  • Purpose: Ends an SPI transaction
  • Implementation: Pulls CS (Chip Select) pin HIGH to disable the display chip
  • UsageGPIO_PIN_SET sets the pin HIGH (inactive)

In main function, link the display to the defined functions as follows:

u8g2_Setup_sh1106_128x64_noname_f(&myDisplay, U8G2_R0, u8x8_spi, u8x8_gpio_and_delay);
  • Purpose: Sets up the display driver for a specific OLED model
  • Display: SH1106 controller, 128×64 resolution
  • Variantnoname_f – typically refers to a common generic display module

Parameters:

1. &myDisplay

  • Typeu8g2_t structure pointer
  • Purpose: This is the main display object that will store all display state, buffer, and configuration information

2. U8G2_R0

  • Purpose: Sets the display rotation/orientation
  • Options:
    • U8G2_R0: No rotation (0°)
    • U8G2_R1: 90° rotation
    • U8G2_R2: 180° rotation
    • U8G2_R3: 270° rotation
    • U8G2_MIRROR: Flipped horizontally

3. u8x8_spi

  • Purpose: Pointer to your SPI communication function
  • Role: This is the function that handles actual data transmission over SPI (the one you showed earlier)

4. u8x8_gpio_and_delay

  • Purpose: Pointer to your GPIO and delay function
  • Role: This function handles pin control (CS, DC) and timing delays

Next, initialize the LCD:

 u8g2_InitDisplay(&myDisplay); // send init sequence to the display, display is in sleep mode after this,

Then, wake up the display:

u8g2_SetPowerSave(&myDisplay, 0); // wake up display

Next, draw some text and shape as follows:

  u8g2_ClearDisplay(&myDisplay);
  u8g2_SetFont(&myDisplay, u8g2_font_ncenB14_tr);
  u8g2_DrawStr(&myDisplay, 0,15,"Hello world");
  u8g2_DrawCircle(&myDisplay, 60, 30, 10, U8G2_DRAW_ALL);
  u8g2_SendBuffer(&myDisplay);

Thats all for the firmware. Save the project and run it on your MCU as follows:

4. Results:

You should see the following on the display:

We have successfully made it work with STM32 and display text and shape.

Happy coding 😉

Add Comment

Your email address will not be published. Required fields are marked *