{"id":3723,"date":"2025-08-29T09:21:24","date_gmt":"2025-08-29T09:21:24","guid":{"rendered":"https:\/\/blog.embeddedexpert.io\/?p=3723"},"modified":"2025-08-29T09:21:27","modified_gmt":"2025-08-29T09:21:27","slug":"porting-u8g2-graphics-library-to-stm32-part3-sh1106-spi-oled","status":"publish","type":"post","link":"https:\/\/blog.embeddedexpert.io\/?p=3723","title":{"rendered":"Porting U8G2 Graphics Library to STM32 Part3: SH1106 SPI OLED"},"content":{"rendered":"\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1536\" height=\"1024\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/IMG_7877.png\" alt=\"\" class=\"wp-image-3725\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/IMG_7877.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/IMG_7877-300x200.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/IMG_7877-1024x683.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/IMG_7877-768x512.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/IMG_7877-1150x767.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/IMG_7877-750x500.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/IMG_7877-400x267.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/IMG_7877-250x167.png 250w\" sizes=\"(max-width: 1536px) 100vw, 1536px\" \/><\/figure>\n\n\n\n<p>The\u00a0<strong>SH1106<\/strong>\u00a0is a popular monochrome OLED display driver, commonly used in small screens ranging from 0.96&#8243; to 1.3&#8243;, offering high contrast and low power consumption. It communicates via\u00a0<strong>SPI or I\u00b2C interfaces<\/strong>, making it suitable for microcontroller projects requiring compact graphical displays.<\/p>\n\n\n\n<p>In this guide, we shall cover the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>STM32CubeMX Configuration.<\/li>\n\n\n\n<li>Connection.<\/li>\n\n\n\n<li>Firmware Development.<\/li>\n\n\n\n<li>Results.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">1. STM32CubeMX Configuration:<\/h2>\n\n\n\n<p>From the previous project, open u8g2.ioc file as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"580\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-36-35-1024x580.jpg\" alt=\"\" class=\"wp-image-3726\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-36-35-1024x580.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-36-35-300x170.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-36-35-768x435.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-36-35-1536x871.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-36-35-1150x652.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-36-35-750x425.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-36-35-400x227.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-36-35-250x141.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-36-35.jpg 1884w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>STM32CubeMX window shall appears.<\/p>\n\n\n\n<p>Next, head to Clock Configuration and set the frequency to 100MHz (or what maximum can be handled by your STM32) as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"480\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-41-18-1024x480.jpg\" alt=\"\" class=\"wp-image-3727\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-41-18-1024x480.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-41-18-300x140.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-41-18-768x360.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-41-18-1536x719.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-41-18-2048x959.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-41-18-1150x539.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-41-18-750x351.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-41-18-400x187.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-41-18-250x117.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, from Pinout &amp; Configuration, select SPI1 and set to Master Transmit only (Display sends nothing back) as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"897\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-45-54-1024x897.jpg\" alt=\"\" class=\"wp-image-3728\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-45-54-1024x897.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-45-54-300x263.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-45-54-768x673.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-45-54-1536x1345.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-45-54-1150x1007.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-45-54-750x657.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-45-54-400x350.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-45-54-250x219.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-45-54.jpg 1578w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>This will automatically enable PA5 as SCK and PA7 as MOSI as shown below:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"1014\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-46-55-1024x1014.jpg\" alt=\"\" class=\"wp-image-3729\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-46-55-1024x1014.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-46-55-300x297.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-46-55-150x150.jpg 150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-46-55-768x761.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-46-55-1150x1139.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-46-55-750x743.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-46-55-400x396.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-46-55-250x248.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-46-55.jpg 1470w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, configure the SPI as follows:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Frame Format to Motorola.<\/li>\n\n\n\n<li>Data size to 8-bit.<\/li>\n\n\n\n<li>First bit is MSB first.<\/li>\n\n\n\n<li>Set prescaler to get between 20 and 25 Mbit\/s.<\/li>\n\n\n\n<li>Clock Polarity to low.<\/li>\n\n\n\n<li>Clock phase to 1 edge<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"876\" height=\"1024\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-50-36-876x1024.jpg\" alt=\"\" class=\"wp-image-3730\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-50-36-876x1024.jpg 876w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-50-36-257x300.jpg 257w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-50-36-768x898.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-50-36-1150x1345.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-50-36-750x877.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-50-36-400x468.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-50-36-250x292.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_11-50-36.jpg 1194w\" sizes=\"(max-width: 876px) 100vw, 876px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, Enable PA0 and PA1 and give a name of CS and DC respectively as shown below:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"880\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-00-42-1024x880.jpg\" alt=\"\" class=\"wp-image-3731\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-00-42-1024x880.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-00-42-300x258.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-00-42-768x660.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-00-42-1536x1320.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-00-42-1150x989.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-00-42-750x645.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-00-42-400x344.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-00-42-250x215.jpg 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-00-42.jpg 1766w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Thats all for STM32CubeMX configuration. Save the project and this will generate the code.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Connection:<\/h2>\n\n\n\n<p>The connection as follows:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>SH1106 SPI OLED Display<\/td><td>STM32F411 Nucleo-64<\/td><\/tr><tr><td>GND<\/td><td>GND<\/td><\/tr><tr><td>Vcc<\/td><td>5V<\/td><\/tr><tr><td>SDA<\/td><td>PA5 (D13 of Arduino pins)<\/td><\/tr><tr><td>SCL<\/td><td>PA7 (D11 of Arduino pins)<\/td><\/tr><tr><td>DC<\/td><td>PA1 (A1 of Arduino pins)<\/td><\/tr><tr><td>CS<\/td><td>PA0 (A0 of Arduino pins)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"643\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-06-29-1024x643.jpg\" alt=\"\" class=\"wp-image-3732\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-06-29-1024x643.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-06-29-300x188.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-06-29-768x482.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-06-29-1536x965.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-06-29-2048x1286.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-06-29-1150x722.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-06-29-750x471.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-06-29-400x251.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/2025-08-29_12-06-29-250x157.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Firmware Development:<\/h2>\n\n\n\n<p>We start off by defining the gpio and delay function as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)\n{\n\tswitch(msg)\n\t{\n\t\tcase U8X8_MSG_DELAY_MILLI:\n\t\t\tHAL_Delay(arg_int);\n\t\t\tbreak;\n\t\tcase U8X8_MSG_GPIO_CS:\n\t\t\tHAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, arg_int);\n\t\t\tbreak;\n\t\tcase U8X8_MSG_GPIO_DC:\n\t\t\tHAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, arg_int);\n\t\t\tbreak;\n\n\n\t}\n\treturn 1;\n}<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Purpose<\/h2>\n\n\n\n<p>This function serves as a bridge between the U8G2 graphics library and the STM32 hardware, handling GPIO control and timing delays.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Function Parameters<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>u8x8_t *u8x8<\/code>: U8G2 display structure pointer<\/li>\n\n\n\n<li><code>uint8_t msg<\/code>: Message type indicating what operation to perform<\/li>\n\n\n\n<li><code>uint8_t arg_int<\/code>: Integer argument (like pin state or delay time)<\/li>\n\n\n\n<li><code>void *arg_ptr<\/code>: Pointer argument (not used here)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Message Handling<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.&nbsp;<code>U8X8_MSG_DELAY_MILLI<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Implements millisecond delays<\/li>\n\n\n\n<li><strong>Implementation<\/strong>: Uses STM32&#8217;s HAL_Delay() function<\/li>\n\n\n\n<li><strong>Usage<\/strong>:\u00a0<code>arg_int<\/code>\u00a0contains the number of milliseconds to delay<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2.&nbsp;<code>U8X8_MSG_GPIO_CS<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Controls the Chip Select (CS) pin<\/li>\n\n\n\n<li><strong>Implementation<\/strong>: Uses HAL_GPIO_WritePin() to set CS pin state<\/li>\n\n\n\n<li><strong>Usage<\/strong>:\u00a0<code>arg_int<\/code>\u00a0determines pin state (0 = low, 1 = high)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3.&nbsp;<code>U8X8_MSG_GPIO_DC<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Controls the Data\/Command (DC) pin<\/li>\n\n\n\n<li><strong>Implementation<\/strong>: Uses HAL_GPIO_WritePin() to set DC pin state<\/li>\n\n\n\n<li><strong>Usage<\/strong>:\u00a0<code>arg_int<\/code>\u00a0determines pin state (0 = command mode, 1 = data mode)<\/li>\n<\/ul>\n\n\n\n<p>Next, spi communication as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">uint8_t u8x8_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)\n{\n\n\tswitch(msg)\n\t{\n\t\tcase U8X8_MSG_BYTE_SET_DC:\n\t\t\tHAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin, arg_int);\n\t\t\tbreak;\n\t\tcase U8X8_MSG_BYTE_SEND:\n\t\t\tHAL_SPI_Transmit(&amp;hspi1, (uint8_t *)arg_ptr, arg_int, 1000);\n\t\t\tbreak;\n\t\tcase U8X8_MSG_BYTE_START_TRANSFER:\n\t\t\tHAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);\n\t\t\tbreak;\n\t\tcase U8X8_MSG_BYTE_END_TRANSFER:\n\t\t\tHAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);\n\t\t\tbreak;\n\n\t\tdefault: break;\n\n\t}\n\treturn 1;\n}<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Purpose<\/h2>\n\n\n\n<p>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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Message Handling<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.&nbsp;<code>U8X8_MSG_BYTE_SET_DC<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Sets the Data\/Command (DC) pin state<\/li>\n\n\n\n<li><strong>Implementation<\/strong>: Uses\u00a0<code>HAL_GPIO_WritePin()<\/code>\u00a0to control the DC pin<\/li>\n\n\n\n<li><strong>Usage<\/strong>:\u00a0<code>arg_int<\/code>\u00a0determines the mode:\n<ul class=\"wp-block-list\">\n<li><code>0<\/code>: Command mode (sending display instructions)<\/li>\n\n\n\n<li><code>1<\/code>: Data mode (sending pixel data)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2.&nbsp;<code>U8X8_MSG_BYTE_SEND<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Transmits data over SPI<\/li>\n\n\n\n<li><strong>Implementation<\/strong>: Uses\u00a0<code>HAL_SPI_Transmit()<\/code>\u00a0to send data<\/li>\n\n\n\n<li><strong>Parameters<\/strong>:\n<ul class=\"wp-block-list\">\n<li><code>&amp;hspi1<\/code>: SPI handle (SPI1 peripheral)<\/li>\n\n\n\n<li><code>(uint8_t *)arg_ptr<\/code>: Pointer to the data buffer to transmit<\/li>\n\n\n\n<li><code>arg_int<\/code>: Number of bytes to transmit<\/li>\n\n\n\n<li><code>1000<\/code>: Timeout in milliseconds<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3.&nbsp;<code>U8X8_MSG_BYTE_START_TRANSFER<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Begins an SPI transaction<\/li>\n\n\n\n<li><strong>Implementation<\/strong>: Pulls CS (Chip Select) pin LOW to enable the display chip<\/li>\n\n\n\n<li><strong>Usage<\/strong>:\u00a0<code>GPIO_PIN_RESET<\/code>\u00a0sets the pin LOW (active)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4.&nbsp;<code>U8X8_MSG_BYTE_END_TRANSFER<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Ends an SPI transaction<\/li>\n\n\n\n<li><strong>Implementation<\/strong>: Pulls CS (Chip Select) pin HIGH to disable the display chip<\/li>\n\n\n\n<li><strong>Usage<\/strong>:\u00a0<code>GPIO_PIN_SET<\/code>\u00a0sets the pin HIGH (inactive)<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p>In main function, link the display to the defined functions as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">u8g2_Setup_sh1106_128x64_noname_f(&amp;myDisplay, U8G2_R0, u8x8_spi, u8x8_gpio_and_delay);<\/pre><\/div>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Sets up the display driver for a specific OLED model<\/li>\n\n\n\n<li><strong>Display<\/strong>: SH1106 controller, 128&#215;64 resolution<\/li>\n\n\n\n<li><strong>Variant<\/strong>:\u00a0<code>noname_f<\/code>\u00a0&#8211; typically refers to a common generic display module<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Parameters:<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.&nbsp;<code>&amp;myDisplay<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Type<\/strong>:\u00a0<code>u8g2_t<\/code>\u00a0structure pointer<\/li>\n\n\n\n<li><strong>Purpose<\/strong>: This is the main display object that will store all display state, buffer, and configuration information<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2.&nbsp;<code>U8G2_R0<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Sets the display rotation\/orientation<\/li>\n\n\n\n<li><strong>Options<\/strong>:\n<ul class=\"wp-block-list\">\n<li><code>U8G2_R0<\/code>: No rotation (0\u00b0)<\/li>\n\n\n\n<li><code>U8G2_R1<\/code>: 90\u00b0 rotation<\/li>\n\n\n\n<li><code>U8G2_R2<\/code>: 180\u00b0 rotation<\/li>\n\n\n\n<li><code>U8G2_R3<\/code>: 270\u00b0 rotation<\/li>\n\n\n\n<li><code>U8G2_MIRROR<\/code>: Flipped horizontally<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3.&nbsp;<code>u8x8_spi<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Pointer to your SPI communication function<\/li>\n\n\n\n<li><strong>Role<\/strong>: This is the function that handles actual data transmission over SPI (the one you showed earlier)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4.&nbsp;<code>u8x8_gpio_and_delay<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose<\/strong>: Pointer to your GPIO and delay function<\/li>\n\n\n\n<li><strong>Role<\/strong>: This function handles pin control (CS, DC) and timing delays<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, initialize the LCD:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\"> u8g2_InitDisplay(&amp;myDisplay); \/\/ send init sequence to the display, display is in sleep mode after this,<\/pre><\/div>\n\n\n\n<p>Then, wake up the display:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">u8g2_SetPowerSave(&amp;myDisplay, 0); \/\/ wake up display<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, draw some text and shape as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">  u8g2_ClearDisplay(&amp;myDisplay);\n  u8g2_SetFont(&amp;myDisplay, u8g2_font_ncenB14_tr);\n  u8g2_DrawStr(&amp;myDisplay, 0,15,&quot;Hello world&quot;);\n  u8g2_DrawCircle(&amp;myDisplay, 60, 30, 10, U8G2_DRAW_ALL);\n  u8g2_SendBuffer(&amp;myDisplay);<\/pre><\/div>\n\n\n\n<p>Thats all for the firmware. Save the project and run it on your MCU as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"34\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/07\/2025-07-03_17-48-35-1024x34.jpg\" alt=\"\" class=\"wp-image-3598\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/07\/2025-07-03_17-48-35-1024x34.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/07\/2025-07-03_17-48-35-300x10.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/07\/2025-07-03_17-48-35-768x25.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/07\/2025-07-03_17-48-35-1536x51.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/07\/2025-07-03_17-48-35-2048x68.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/07\/2025-07-03_17-48-35-1150x38.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/07\/2025-07-03_17-48-35-750x25.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/07\/2025-07-03_17-48-35-400x13.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/07\/2025-07-03_17-48-35-250x8.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Results:<\/h2>\n\n\n\n<p><\/p>\n\n\n\n<p>You should see the following on the display:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2025\/08\/IMG_7875-1.heic\" alt=\"\" class=\"wp-image-3733\" \/><\/figure>\n\n\n\n<p>We have successfully made it work with STM32 and display text and shape.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Happy coding \ud83d\ude09<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The\u00a0SH1106\u00a0is a popular monochrome OLED display driver, commonly used in small screens ranging from 0.96&#8243; to 1.3&#8243;, offering high contrast and low power consumption. It communicates via\u00a0SPI or I\u00b2C interfaces, making it suitable for microcontroller projects requiring compact graphical displays. In this guide, we shall cover the following: 1. STM32CubeMX Configuration: From the previous project, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,19,11,12],"tags":[],"class_list":["post-3723","post","type-post","status-publish","format-standard","hentry","category-embedded-systems","category-lcd","category-peripheral-drivers","category-stm32"],"_links":{"self":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/3723"}],"collection":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3723"}],"version-history":[{"count":1,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/3723\/revisions"}],"predecessor-version":[{"id":3734,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/3723\/revisions\/3734"}],"wp:attachment":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3723"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3723"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3723"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}