Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSD1309 128x64 with STM32F103C8 #2558

Closed
pman92 opened this issue Dec 30, 2024 · 5 comments
Closed

SSD1309 128x64 with STM32F103C8 #2558

pman92 opened this issue Dec 30, 2024 · 5 comments

Comments

@pman92
Copy link

pman92 commented Dec 30, 2024

I'm attempting to use an STM32F103C8 (mounted on my own custom PCB) with an aliexpress 2.42" SSD1309 128x64 SPI OLED (https://www.aliexpress.com/item/1005007602340232.html).
I think I have got everything setup and working correctly, and the display lights up and works, but whenever I try and draw anything
on the display, all I get is what seems like random pixels.

Display

I am using STM32Cube framework in Platformio / VScode. I am starting with pre-generated code from STM32CubeMX.
I am using hardware SPI1, configured as transmit only master, MSB first, CPOL (polarity) low, CPHA 1 edge, 4.5MBits/s

Here are the callback functions I am using:

u8g2_t u8g2;

// Assuming SPI1 is used
extern SPI_HandleTypeDef hspi1;

// SPI communication callback
uint8_t u8x8_byte_stm32_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
    switch (msg) {
        case U8X8_MSG_BYTE_INIT:
            // SPI initialization is handled in MX_SPI1_Init()
            break;

        case U8X8_MSG_BYTE_SEND:
            // Transmit data using SPI
            HAL_SPI_Transmit(&hspi1, (uint8_t *)arg_ptr, arg_int, HAL_MAX_DELAY);
            break;

        case U8X8_MSG_BYTE_START_TRANSFER:
            // Assert chip select (CS) pin low
            HAL_GPIO_WritePin(DISPLAY_CS_GPIO_Port, DISPLAY_CS_Pin, GPIO_PIN_RESET);
            break;

        case U8X8_MSG_BYTE_END_TRANSFER:
            // Deassert chip select (CS) pin high
            HAL_GPIO_WritePin(DISPLAY_CS_GPIO_Port, DISPLAY_CS_Pin, GPIO_PIN_SET);
            break;
        
        default:
            return 0;
    }
    return 1;
}

// GPIO and delay callback
uint8_t u8x8_gpio_and_delay_stm32(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
    switch (msg) {
        case U8X8_MSG_GPIO_AND_DELAY_INIT:
            // GPIO initialization is handled in MX_GPIO_Init()
            break;

        case U8X8_MSG_DELAY_100NANO:
            // Delay for ~100 nanoseconds (7 NOPs = ~98 ns at 72 MHz)
            for (volatile uint32_t i = 0; i < arg_int; i++) {
                __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
            }
            break;
            
        case U8X8_MSG_DELAY_10MICRO:
            // Delay for 10 microseconds
            for (uint32_t i = 0; i < arg_int; i++) {
                // Approximately 10 us delay at 72 MHz
                for (volatile uint32_t j = 0; j < 720; j++) {
                    __NOP();
                }
            }
            break;
        
        case U8X8_MSG_DELAY_MILLI:
            HAL_Delay(arg_int); // Millisecond delay
            break;

        case U8X8_MSG_GPIO_RESET:
            // Control reset pin if you have one (update pin if applicable)
            HAL_GPIO_WritePin(DISPLAY_RST_GPIO_Port, DISPLAY_RST_Pin, arg_int ? GPIO_PIN_SET : GPIO_PIN_RESET);
            break;

        case U8X8_MSG_GPIO_CS:
            // Chip Select pin is handled in SPI byte functions
            break;

        case U8X8_MSG_GPIO_DC:
            // Data/Command pin
            HAL_GPIO_WritePin(DISPLAY_DC_GPIO_Port, DISPLAY_DC_Pin, arg_int ? GPIO_PIN_SET : GPIO_PIN_RESET);
            break;
        
        default:
            // Default return value
            u8x8_SetGPIOResult(u8x8, 1);			
            break;
    }
    return 1;
}

void display_init() {
    // Setup function for SSD1309 in SPI mode
    u8g2_Setup_ssd1309_128x64_noname0_f(
        &u8g2, 
        U8G2_R0, 
        u8x8_byte_stm32_hw_spi, 
        u8x8_gpio_and_delay_stm32
    );

    // Initialize display
    u8g2_InitDisplay(&u8g2);

    // Wake up display
    u8g2_SetPowerSave(&u8g2, 0);
}

/* Example Usage:
    u8g2_ClearBuffer(&u8g2);              // Clear the display buffer
    u8g2_DrawBox(&u8g2, 20, 20, 40, 40); // Draw a square with top-left corner at (20, 20) and size 40x40
    u8g2_SendBuffer(&u8g2);              // Send the buffer to the display
*/

The seemingly random pixels are always just about the same whenever I reset the device. If I change what I'm drawing I get a different set of seemingly random pixels.

I have tried both "u8g2_Setup_ssd1309_128x64_noname0_f" and "u8g2_Setup_ssd1309_128x64_noname2_f" with the same result for both.

Does anyone have any clues where to start looking? I have tried everything I can think of.

The only thing I can think is the display SCK / MOSI lines should be 5v logic, and I am using 3.3v logic directly from the STM32. However everything I can find online says that should be correct, and the display is designed for 3.3v signals (although it is powered from 5v).

@pman92
Copy link
Author

pman92 commented Dec 30, 2024

I have checked the signals to the display using a logic analyzer.

I am sending the following every second:

u8g2_ClearBuffer(&u8g2);              // Clear the display buffer
u8g2_DrawBox(&u8g2, 20, 20, 40, 40); // Draw a square with top-left corner at (20, 20) and size 40x40
u8g2_SendBuffer(&u8g2);              // Send the buffer to the display

And measure the following with the logic analyzer:

Trace

I noticed straight away the DC (data command) line remains low the entire time.
When I regenerate the STM32 code and set the DC output pin to default to a "high" state in cubeMX, I get exactly the same result on my logic analyzer, except DC remains high (the display also doesn't light up anymore, as its interpreting all spi transmission as data).

It seems u8g2 is not controlling the DC line as it should.

I have implemented the DC item into the GPIO & delay callback as follows:

case U8X8_MSG_GPIO_DC:
            // Data/Command pin
            HAL_GPIO_WritePin(DISPLAY_DC_GPIO_Port, DISPLAY_DC_Pin, arg_int ? GPIO_PIN_SET : GPIO_PIN_RESET);
            break;

I am able to toggle the DC pin myself using the following in the main loop, and verify on the logic analyzer:
HAL_GPIO_TogglePin(DISPLAY_DC_GPIO_Port, DISPLAY_DC_Pin);
Confirming that the pin is correctly configured as output and working

Can anyone help?

@pman92
Copy link
Author

pman92 commented Dec 30, 2024

I have used a breakpoint to confirm the gpio_and_delay callback function does get called in general (eg by u8x8_d_helper_display_init())
However a breakpoint on the U8X8_MSG_GPIO_DC case is never hit.

I think I have pretty well confirmed it is a configuration problem with u8g2, but don't know where to start looking to resolve it.

@pman92
Copy link
Author

pman92 commented Dec 30, 2024

I have found a workaround that gets the display working correctly:

DisplayWorking

case U8X8_MSG_BYTE_SEND:

            if (arg_int > 1) {
                // Likely sending pixel data, set DC high
                HAL_GPIO_WritePin(DISPLAY_DC_GPIO_Port, DISPLAY_DC_Pin, GPIO_PIN_SET);
            } else {
                // Likely sending a command, set DC low
                HAL_GPIO_WritePin(DISPLAY_DC_GPIO_Port, DISPLAY_DC_Pin, GPIO_PIN_RESET);
            }

            // Transmit data using SPI
            HAL_SPI_Transmit(&hspi1, (uint8_t *)arg_ptr, arg_int, HAL_MAX_DELAY);
            break;

I found using printf there that u8g2 sends 3 single bytes (commands), followed by 128 bytes (data).

However this doesn't really seem to be the correct way to do things

@pman92
Copy link
Author

pman92 commented Jan 6, 2025

Turns out I was missing the set DC handler in the byte callback function.

@pman92 pman92 closed this as completed Jan 6, 2025
@olikraus
Copy link
Owner

olikraus commented Jan 7, 2025

Thanks for your posts. Always helpful to learn from such porting activities :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants