Skip to main content
Visitor II
August 30, 2025
Question

Looking for STM32 SPI example for SSD1322-based OLED (NHD-2.7-12864WDW3)

  • August 30, 2025
  • 1 reply
  • 743 views

Hi,

I'm trying to get a few lines of text displayed on an OLED screen using 4-wire SPI on an STM32H755 board, but I haven't succeeded yet.

I've looked into the sample code provided by the manufacturer (which is Arduino-based), but it hasn’t helped much so far.

Has anyone worked with this display or with the SSD1322 controller before?

I’m mainly looking for example code for the STM32 platform.
I’ll be uploading my current progress to my GitHub repo.

 

Thanks in advance!

    This topic has been closed for replies.

    1 reply

    Super User
    August 30, 2025

    Hi,

    you set the SPI mode in Cube ?  correct for ssd1322 ? did you check it with a scope ?

    +

    There are many libs, just look at another lib, copy it + adapt and try .

    maybe try:

    https://github.com/MartyMacGyver/OLED_SSD1322/blob/master/Arduino/SSD1322_NHD_256x64_mono_demo/SSD1322_NHD_256x64_mono_demo.ino

    or this, for stm32F4 :

    https://github.com/wjklimek1/SSD1322_OLED_library

     

     

     

    durnaAuthor
    Visitor II
    September 1, 2025

    Hi @AScha.3 ,

    I've spent a long time trying to get it working, but I still can't achieve a clean and proper image. The display behaves as if it's 256x64, not 128x64 — only the left half shows readable content, while the right side is filled with garbage.

     

    In fact, I had to manually offset text by 57 pixels from the left just to make it display properly. Without that, even the left part of the screen would appear distorted. :(

     

    Here’s what I’ve tried so far:

    • Reviewed the Arduino example code provided by the display manufacturer.
    • Examined two GitHub repos written for SSD1322, but they both target 256x64 panels.
    • Even tried some AI tools for troubleshooting, but none of the suggestions resolved the issue.

    I'm now wondering — maybe someone here has experience using an SSD1322 display with a physical resolution of 128x64, not 256x64?

    Could the issue be caused by incorrect column address setup, remap configuration, or GDDRAM write pattern?

    I’ll share my code if needed.

    /* oled_ssd1322.c */
    
    #include <string.h>
    #include <stdio.h>
    #include "oled_ssd1322.h"
    #include "font6x8.h"
    
    /* If your logo is large, get it as extern */
    extern const uint8_t NHD_Logo[];
    
    /* Inline control helpers */
    static inline void CS_LOW (void) { HAL_GPIO_WritePin(SSD1322_CS_Port, SSD1322_CS_Pin, GPIO_PIN_RESET); }
    static inline void CS_HIGH(void) { HAL_GPIO_WritePin(SSD1322_CS_Port, SSD1322_CS_Pin, GPIO_PIN_SET); }
    static inline void DC_CMD (void) { HAL_GPIO_WritePin(SSD1322_DC_Port, SSD1322_DC_Pin, GPIO_PIN_RESET); }
    static inline void DC_DAT (void) { HAL_GPIO_WritePin(SSD1322_DC_Port, SSD1322_DC_Pin, GPIO_PIN_SET); }
    static inline void DEBUG_TOGGLE(void) { HAL_GPIO_TogglePin(DEBUG_PIN_PORT, DEBUG_PIN_PIN); }
    static inline void DEBUG_HIGH(void) { HAL_GPIO_WritePin(DEBUG_PIN_PORT, DEBUG_PIN_PIN, GPIO_PIN_SET); }
    static inline void DEBUG_LOW(void) { HAL_GPIO_WritePin(DEBUG_PIN_PORT, DEBUG_PIN_PIN, GPIO_PIN_RESET); }
    
    /* Transmit with SPI (retry) */
    static HAL_StatusTypeDef ssd1322_spi_tx(const uint8_t *data, uint16_t len)
    {
     HAL_StatusTypeDef ret;
     for (int attempt = 0; attempt < SSD1322_SPI_RETRY_MAX; ++attempt) {
     ret = HAL_SPI_Transmit(&hspi2, (uint8_t*)data, len, 100);
     if (ret == HAL_OK) return HAL_OK;
     HAL_Delay(1);
     }
     return ret;
    }
    
    
    void SSD1322_EntireDisplayOn(void) {
     SSD1322_SendCommand(0xA5); // Entire display ON (all pixels white)
    }
    
    void SSD1322_EntireDisplayOff(void) {
     SSD1322_SendCommand(0xA4); // Entire display OFF (normal)
    }
    
    
    /* Send command */
    void SSD1322_SendCommand(uint8_t cmd)
    {
     DC_CMD();
     CS_LOW();
     ssd1322_spi_tx(&cmd, 1);
     CS_HIGH();
    }
    
    /* Command + data */
    void SSD1322_SendCommandWithData(uint8_t cmd, const uint8_t *data, uint16_t len)
    {
     DC_CMD();
     CS_LOW();
     ssd1322_spi_tx(&cmd, 1);
     if (len) {
     DC_DAT();
     ssd1322_spi_tx(data, len);
     }
     CS_HIGH();
    }
    
    /* Reset pulse */
    static void SSD1322_Reset(void)
    {
     HAL_GPIO_WritePin(SSD1322_RST_Port, SSD1322_RST_Pin, GPIO_PIN_RESET);
     HAL_Delay(150);
     HAL_GPIO_WritePin(SSD1322_RST_Port, SSD1322_RST_Pin, GPIO_PIN_SET);
     HAL_Delay(150);
    }
    
    /* Column/row settings */
    void SSD1322_SetColumn(uint8_t a, uint8_t b)
    {
     SSD1322_SendCommandWithData(0x15, (uint8_t[]){a, b}, 2);
    }
    
    void SSD1322_SetRow(uint8_t a, uint8_t b)
    {
     SSD1322_SendCommandWithData(0x75, (uint8_t[]){a, b}, 2);
    }
    
    /* Display ON/OFF */
    void SSD1322_DisplayOnOff(bool on)
    {
     if (on) SSD1322_SendCommand(0xAF);
     else SSD1322_SendCommand(0xAE);
    }
    
    /* Initialization sequence */
    void SSD1322_Init(void)
    {
     SSD1322_Reset();
    
     SSD1322_DisplayOnOff(false);
    
     SSD1322_SendCommandWithData(0xFD, (uint8_t[]){0x12},1); // Command Lock
     SSD1322_SendCommandWithData(0xB3, (uint8_t[]){0x91},1); // Display Clock
     SSD1322_SendCommandWithData(0xCA, (uint8_t[]){0x3F},1); // MUX Ratio
     SSD1322_SendCommandWithData(0xA2, (uint8_t[]){0x00},1); // Display Offset
     SSD1322_SendCommandWithData(0xAB, (uint8_t[]){0x01},1); // Function Select (internal VDD)
     SSD1322_SendCommandWithData(0xA1, (uint8_t[]){0x00},1); // Start Line
    
     SSD1322_SendCommandWithData(0xA0, (uint8_t[]){0x16,0x11},2); // Remap
    
     SSD1322_SendCommandWithData(0xC7, (uint8_t[]){0x0F},1); // Master Contrast
     SSD1322_SendCommandWithData(0xC1, (uint8_t[]){0x9F},1); // Contrast
    
     SSD1322_SendCommandWithData(0xB1, (uint8_t[]){0x72},1); // Phase Length
     SSD1322_SendCommandWithData(0xBB, (uint8_t[]){0x1F},1); // Precharge Voltage
     SSD1322_SendCommandWithData(0xB4, (uint8_t[]){0xA0,0xFD},2);// Display Enhancement A (VSL)
     SSD1322_SendCommandWithData(0xBE, (uint8_t[]){0x04},1); // VCOMH
    
     SSD1322_SendCommand(0xA6); // Normal Display
     SSD1322_SendCommand(0xA9); // Exit Partial
     SSD1322_SendCommandWithData(0xD1, (uint8_t[]){0xA2,0x20},2); // Display Enhancement B
     SSD1322_SendCommandWithData(0xB5, (uint8_t[]){0x00},1); // GPIO
     SSD1322_SendCommand(0xB9); // Default Grayscale
     SSD1322_SendCommandWithData(0xB6, (uint8_t[]){0x08},1); // 2nd Precharge
    
     SSD1322_DisplayOnOff(true);
    }
    
    /* Framebuffer: 2-bit grayscale (0..3), 64 rows x 128 columns */
     uint8_t framebuf[64][128];
    
    /* 2-bit -> byte mapping */
    static inline uint8_t gray2byte(uint8_t g) {
     switch (g & 0x03) {
     case 0: return 0x00;
     case 1: return 0x55;
     case 2: return 0xAA;
     case 3: return 0xFF;
     default: return 0x00;
     }
    }
    
    /* Writes framebuffer to GDDRAM */
    void SSD1322_RefreshFromFramebuffer(void)
    {
     SSD1322_SetColumn(0x00, 0x7F);
     SSD1322_SetRow(0x00, 0x3F);
     SSD1322_SendCommand(0x5C); // Write RAM
    
     uint8_t linebuf[256];
     for (int row = 0; row < 64; row++) {
     for (int col = 0; col < 128; col++) {
     uint8_t b = gray2byte(framebuf[row][col]);
    // linebuf[col * 2 + 0] = b;
    // linebuf[col * 2 + 1] = b;
     linebuf[col] = b;
     }
     DC_DAT();
     CS_LOW();
     ssd1322_spi_tx(linebuf, sizeof(linebuf));
     CS_HIGH();
     }
    }
    
    void DrawText(const char *s, int y)
    {
     int len = strlen(s);
     int x0 = 57; // leaves 57 pixels of space from the left
    
     for (int i = 0; i < len; i++) {
     SSD1322_DrawChar(x0 + i * 6, y, s[i]);
     }
    }
    
    
    /* Simple character drawing (6x8) */
    void SSD1322_DrawChar(int x, int y, char c)
    {
     if (c < 32 || c > 127) return;
     const uint8_t *glyph = Font6x8[c - 32];
    
     for (int col = 0; col < 6; col++) {
     int fx = x + col;
     if (fx < 0 || fx >= 128) continue;
     uint8_t column_bits = glyph[col];
     for (int row = 0; row < 8; row++) {
     int fy = y + row;
     if (fy < 0 || fy >= 64) continue;
     uint8_t pixel_on = (column_bits >> row) & 0x01;
     framebuf[fy][fx] = pixel_on ? 3 : 0;
     }
     }
    }
    
    
    /* Clears the display via framebuffer */
    void SSD1322_Clear(void)
    {
     for (int r=0; r<64; r++)
     for (int c=0; c<128; c++)
     framebuf[r][c] = 0;
     SSD1322_RefreshFromFramebuffer();
    }
    
    
    /* Centered string (single line) */
    void SSD1322_DrawStringCentered(const char *s)
    {
     int len = 0;
     for (const char *p = s; *p; ++p) len++;
     int total_width = len * 6 + (len - 1) * 1;
     int x0 = (128 - total_width) / 2;
     int y0 = (64 - 8) / 2;
    
     /* clear */
     for (int r=0;r<64;r++)
     for (int c=0;c<128;c++)
     framebuf[r][c]=0;
    
     for (int i = 0; i < len; i++) {
     int x = x0 + i * (6 + 1);
     SSD1322_DrawChar(x, y0, s[i]);
     }
     SSD1322_RefreshFromFramebuffer();
    }
    
    /* Draws a scrolling string with offset (horizontal scroll) */
    void SSD1322_DrawStringAtOffset(const char *s, int y, int offset)
    {
     // Clear only that line
     for (int row = y; row < y + 8; row++)
     for (int col = 0; col < 128; col++)
     if (row >= 0 && row < 64)
     framebuf[row][col] = 0;
    
     int x = -offset;
     for (int i = 0; s[i]; i++) {
     SSD1322_DrawChar(x, y, s[i]);
     x += 7; // 6px + 1 space
     }
    }
    
    /* Scroll line structure and management */
    void ScrollLine_Init(scrolling_line_t *line, const char *fmt, int y)
    {
     snprintf(line->text, sizeof(line->text), fmt);
     int len = 0;
     for (const char *p = line->text; *p; ++p) len++;
     line->text_pixel_width = len * 7 - 1;
     line->offset = 0;
     line->direction = 1;
     line->y = y;
    }
    
    void ScrollLine_Tick(scrolling_line_t *line)
    {
     if (line->text_pixel_width <= 128) {
     int pad = (128 - line->text_pixel_width) / 2;
     SSD1322_DrawStringAtOffset(line->text, line->y, -pad);
     } else {
     SSD1322_DrawStringAtOffset(line->text, line->y, line->offset);
     line->offset += line->direction;
     if (line->offset + 128 >= line->text_pixel_width) line->direction = -1;
     if (line->offset <= 0) line->direction = 1;
     }
    }
    
    /* Helper to clear the framebuffer */
    void SSD1322_ClearFramebuffer(void)
    {
     // Since static framebuf is defined in this file, we can access it directly
     for (int r = 0; r < 64; r++) {
     for (int c = 0; c < 128; c++) {
     framebuf[r][c] = 0;
     }
     }
    }
    
    // Set a single pixel
    void SSD1322_SetPixel(int x, int y, uint8_t gray)
    {
     if (x < 0 || x >= 128 || y < 0 || y >= 64) return;
     framebuf[y][x] = gray & 0x03; // 0..3
    }

    error_oled.jpeg

    Super User
    September 1, 2025

    1. GOOD - so far, some success.

    2. >issue be caused by incorrect column address setup, remap configuration

    Yes, this looks typical for wrong address/pixel count/offset etc. , or a missing/puzzled data line.

    So : is manufact.exaple correct ? there should be the matching values for the init-values.

     

    Size of screen is set here, so check values for display size here:

    /* Column/row settings */
    void SSD1322_SetColumn(uint8_t a, uint8_t b)
    {
     SSD1322_SendCommandWithData(0x15, (uint8_t[]){a, b}, 2);
    }
    
    void SSD1322_SetRow(uint8_t a, uint8_t b)
    {
     SSD1322_SendCommandWithData(0x75, (uint8_t[]){a, b}, 2);
    }

    you dont show your code...so i only can guess.