espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.77k stars 7.31k forks source link

Image distortion on a 128x32 SSD1306 OLED screen (IDFGH-14007) #14830

Closed henkjannl closed 1 week ago

henkjannl commented 1 week ago

Answers checklist.

IDF version.

ESP-IDF 5.3.1

Espressif SoC revision.

ESP32

Operating System used.

Windows

How did you build your project?

VS Code IDE

If you are using Windows, please specify command line type.

None

Development Kit.

MH-ET minikit

Power Supply used.

USB

What is the expected behavior?

Display images on my OLED display

What is the actual behavior?

Images look distorted

Steps to reproduce.

I have an I²C controlled SSD1306 OLED display with 128x32 pixels. 07

I tried to get the /peripherals/lcd/i2c_oled/main/i2c_oled_example_main.c example working, but images are distorted and screen updates were very slow. It appeared from the logfile that the I²C clock frequency was too high, and I could not manage to get the clock frequency down. By switching back to the legacy I²C driver, I managed to get the clock frequency at 400.000 Hz as specified in the SSD1306 datasheet. I also tried lower frequencies to no avail.

I also switched back to a more basic example without LVGL: ../components/esp_lcd/test_apps/i2c_lcd/main/test_i2c_lcd_panel.c

When using the `esp_lcd_panel_draw_bitmap()` function, images still look distorted.

I modified the test program to clearify the type of distortions:

#include <stdio.h>
#include "driver/i2c.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_system.h"
#include "esp_lcd_io_i2c.h"
#include "esp_err.h"
#include "esp_log.h"

#define tag "main"

#define MN_LCD_H_RES          128
// #define MN_LCD_V_RES          32
#define MN_LCD_V_RES          64
#define MN_I2C_HOST_ID        I2C_NUM_0
#define MN_I2C_SDA_GPIO       21
#define MN_I2C_SCL_GPIO       22
#define MN_LCD_PIXEL_CLOCK_HZ (400 * 1000)
#define MN_I2C_DEV_ADDR       0x3C

void app_main() {

    const i2c_config_t conf = {
        .mode             = I2C_MODE_MASTER,
        .sda_io_num       = MN_I2C_SDA_GPIO,
        .scl_io_num       = MN_I2C_SCL_GPIO,
        .sda_pullup_en    = GPIO_PULLUP_ENABLE,
        .scl_pullup_en    = GPIO_PULLUP_ENABLE,
        .master.clk_speed = MN_LCD_PIXEL_CLOCK_HZ,
    };

    ESP_ERROR_CHECK(i2c_param_config(MN_I2C_HOST_ID, &conf));

    ESP_ERROR_CHECK(i2c_driver_install(MN_I2C_HOST_ID, I2C_MODE_MASTER, 0, 0, 0));

    esp_lcd_panel_io_handle_t io_handle = NULL;

    esp_lcd_panel_io_i2c_config_t io_config = {
        .dev_addr = MN_I2C_DEV_ADDR,
        .control_phase_bytes = 1,  // According to SSD1306 datasheet
        .dc_bit_offset       = 6,  // According to SSD1306 datasheet
        .lcd_cmd_bits        = 8,  // According to SSD1306 datasheet
        .lcd_param_bits      = 8,  // According to SSD1306 datasheet
    };

    esp_lcd_new_panel_io_i2c( (esp_lcd_i2c_bus_handle_t)MN_I2C_HOST_ID, &io_config, &io_handle);

    esp_lcd_panel_handle_t panel_handle = NULL;
    esp_lcd_panel_dev_config_t panel_config = {
        .bits_per_pixel = 1,
        .reset_gpio_num = -1,
    };

    esp_lcd_new_panel_ssd1306(io_handle, &panel_config, &panel_handle);
    esp_lcd_panel_reset(panel_handle);
    esp_lcd_panel_init(panel_handle);
    esp_lcd_panel_disp_on_off(panel_handle, true);

    uint8_t x;
    uint8_t y;
    uint8_t bitmap[8];

    // Clear the screen
    for(int i=0; i<8; i++) bitmap[i]=0;
    for(x=0; x<128; x+=8)
        for(y=0; y<64;y+=8)
            esp_lcd_panel_draw_bitmap(panel_handle,  x, y, x+8, y+8, bitmap);

    while(true) {
        for(uint8_t i=0; i<8; i++) {
            uint8_t byte = 0x01 << i;
            for(uint8_t j=0; j<8; j++) {
                for(uint8_t k=0; k<8; k++) {
                    bitmap[k] = (j==k) ? 0xFF : byte;
                } // for k

            ESP_LOGI(tag, "i=%d byte=%d j=%d", i, byte, j);
            for(uint8_t j=0; j<8; j++) {
                x=8*j; y=0;   esp_lcd_panel_draw_bitmap(panel_handle,  x, y, x+8, y+8, bitmap);
                x=8*j; y=8*j; esp_lcd_panel_draw_bitmap(panel_handle,  x, y, x+8, y+8, bitmap);
            } // for j

            vTaskDelay(pdMS_TO_TICKS(((i==0) && (j==0)) ? 1000 : 150));
            } // for j
        } // for i
    }

    // esp_lcd_panel_del(panel_handle);
    // esp_lcd_panel_io_del(io_handle);
    // i2c_driver_delete(MN_I2C_HOST_ID);
}

The aim of the example is to draw 8x8 bitmaps showing a cross-hair driven by the i and j parameters. The example loops i and j from 0 to 7 with short time intervals, and waits longer when the loops are repeated.

The overall screen looks like this: display

Since 16 bitmaps fit horizontally and 8 bitmaps fit vertically, it appears the bitmaps are 8 pixels wide and 4 pixels high.

Each bitmap progresses like this as parameters i and j are looped: analysis

The bitmaps are compressed to a 8x4 bitmap, odd rows are shown and even rows are skipped.

I tried modifying the following parameters: `c .control_phase_bytes = 1, // According to SSD1306 datasheet .dc_bit_offset = 6, // According to SSD1306 datasheet .lcd_cmd_bits = 8, // According to SSD1306 datasheet .lcd_param_bits = 8, // According to SSD1306 datasheet ` but no variations produced better results. I also changed vertical resolution of the screen (32 or 64 pixels), but this does not make a difference.

Help is appreciated.

Debug Logs.

ets Jun  8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:7176
load:0x40078000,len:15564
ho 0 tail 12 room 4
load:0x40080400,len:4
load:0x40080404,len:3904
entry 0x40080638
I (30) boot: ESP-IDF 5.3.1 2nd stage bootloader
I (30) boot: compile time Nov  5 2024 12:55:28
I (30) boot: Multicore bootloader
I (34) boot: chip revision: v1.0
I (38) boot.esp32: SPI Speed      : 40MHz
I (43) boot.esp32: SPI Mode       : DIO
I (47) boot.esp32: SPI Flash Size : 4MB
I (52) boot: Enabling RNG early entropy source...
I (57) boot: Partition Table:
I (61) boot: ## Label            Usage          Type ST Offset   Length
I (68) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (76) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (83) boot:  2 factory          factory app      00 00 00010000 00100000
I (91) boot: End of partition table
I (95) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=0b3f0h ( 46064) map
I (119) esp_image: segment 1: paddr=0001b418 vaddr=3ffb0000 size=02378h (  9080) load
I (123) esp_image: segment 2: paddr=0001d798 vaddr=40080000 size=02880h ( 10368) load
I (129) esp_image: segment 3: paddr=00020020 vaddr=400d0020 size=168bch ( 92348) map
I (165) esp_image: segment 4: paddr=000368e4 vaddr=40082880 size=0b628h ( 46632) load
I (190) boot: Loaded app from partition at offset 0x10000
I (190) boot: Disabling RNG early entropy source...
I (202) cpu_start: Multicore app
I (211) cpu_start: Pro cpu start user code
I (211) cpu_start: cpu freq: 240000000 Hz
I (211) app_init: Application information:
I (214) app_init: Project name:     Bedclock_IDF_V11
I (219) app_init: App version:      1
I (224) app_init: Compile time:     Nov  5 2024 12:54:46
I (230) app_init: ELF file SHA256:  91c7437b6...
I (235) app_init: ESP-IDF:          5.3.1
I (240) efuse_init: Min chip rev:     v0.0
I (244) efuse_init: Max chip rev:     v3.99 
I (249) efuse_init: Chip rev:         v1.0
I (254) heap_init: Initializing. RAM available for dynamic allocation:
I (262) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (267) heap_init: At 3FFB2C20 len 0002D3E0 (180 KiB): DRAM
I (274) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (280) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (286) heap_init: At 4008DEA8 len 00012158 (72 KiB): IRAM
I (294) spi_flash: detected chip: generic
I (297) spi_flash: flash io: dio
W (301) i2c: This driver is an old driver, please migrate your application code to adapt `driver/i2c_master.h`
I (312) main_task: Started on CPU0
I (322) main_task: Calling app_main()
I (492) main: i=0 byte=1 j=0
I (1502) main: i=0 byte=1 j=1
I (1652) main: i=0 byte=1 j=2
I (1802) main: i=0 byte=1 j=3
I (1952) main: i=0 byte=1 j=4
I (2102) main: i=0 byte=1 j=5
I (2252) main: i=0 byte=1 j=6
I (2402) main: i=0 byte=1 j=7
I (2552) main: i=1 byte=2 j=0
I (2702) main: i=1 byte=2 j=1
I (2852) main: i=1 byte=2 j=2
I (3002) main: i=1 byte=2 j=3
I (3152) main: i=1 byte=2 j=4
I (3302) main: i=1 byte=2 j=5

More Information.

The hardware is working well when using the U8G2 driver in the Arduino programming environment, so I suspect something is wrong with the driver settings.

henkjannl commented 1 week ago

Just for information:

suda-morris commented 1 week ago

I remember SSD1306 of different resolution may need slightly different initialization: https://github.com/espressif/esp-idf/pull/12450

henkjannl commented 1 week ago

Thanks!! It indeed has to do with the difference in initialisation between 128x64 and 128x32 displays.

I modified the code in this way:

    esp_lcd_panel_ssd1306_config_t esp_lcd_panel_ssd1306_config = {
        .height = 32,
    };

    esp_lcd_panel_dev_config_t panel_config = {
        .bits_per_pixel = 1,
        .reset_gpio_num = -1,
        .vendor_config = &esp_lcd_panel_ssd1306_config,
    };

and now it is showing the 8x8 bitmaps correctly. This helped a lot!