nRF52840-DK - SSD1306 - Display

Hi,

currently I am working on a private project where I need a small OLED Display.
Therefore I bought a 0.96" OLED Display (128x64) with a SSD1306 IC Driver on it.

First I started with this tutorial  Printing to an I2C OLED display using nRF Connect SDK but sadly the Display showed nothing and I also worked through this Post Zephyr + nRF5240 DK + lvgl + SSD1306 but still the display showed nothing.
In my first approaches I had a  0.91" OLED Display (128x32) already at home but I had no luck at all.
After that, I tried a really simple example which was done for an ESP32 (https://www.youtube.com/watch?v=ddZ-04IVrak) and modified it to work with a nRF52840-DK, but still no luck.

After that, I tried both displays with an Arduino Nano BLE Sense and tried out the Adafruit Example code to verify that both displays works and they are working with the Arduino.
Now the really funny and disturbing thing happened: I reconnected the 128x64 display back to the nRF52840-DK board and powered it up and suddenly the display showed my example image!
Even after powering the nRF52840-DK board on and off a couple of times and minutes, the display is still working. I verified this behavior with a second 128x64 OLED Display.
So I thought that this can't be true and I looked deeper and further into this issue, but sadly I found nothing, which can explain this kind of strange behavior.

I even tried it with a third display and it's behaving the same way.

I am guessing that something must be set permanently in the display by the Arduino code, but actually I found nothing.
To be honest this is one of the strangest behaviors I ever had with a µC and electronics.

I really hope that somebody is able to help, because I really want to know how this could happen.

The LOG-Output actually indicates that everything works fine:

*** Booting nRF Connect SDK v2.5.1 ***
[00:00:00.252,868] <inf> display: x_resolution: 128
[00:00:00.252,899] <inf> display: y_resolution: 64
[00:00:00.252,899] <inf> display: supported pixel formats: 4
[00:00:00.252,899] <inf> display: screen_info: 1
[00:00:00.252,929] <inf> display: current_pixel_format: 4
[00:00:00.252,929] <inf> display: current_orientation: 0
[00:00:00.253,082] <dbg> ssd1306: ssd1306_write: x 0, y 0, pitch 128, width 128, height 64, buf_len 1024

The code which I am used is this one:

#include <stdint.h>
#include <zephyr/device.h>
#include <zephyr/drivers/display.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>

#include "logo_image.h"

#define DISPLAY_BUFFER_PITCH 128

LOG_MODULE_REGISTER(display);

static const struct device *display = DEVICE_DT_GET(DT_NODELABEL(ssd1306));

void main(void)
{
    if (display == NULL)
    {
        LOG_ERR("device pointer is NULL");
        return;
    }

    if (!device_is_ready(display))
    {
        LOG_ERR("display device is not ready");
        return;
    }

    struct display_capabilities capabilities;
    display_get_capabilities(display, &capabilities);

    const uint16_t x_res = capabilities.x_resolution;
    const uint16_t y_res = capabilities.y_resolution;

    LOG_INF("x_resolution: %d", x_res);
    LOG_INF("y_resolution: %d", y_res);
    LOG_INF("supported pixel formats: %d", capabilities.supported_pixel_formats);
    LOG_INF("screen_info: %d", capabilities.screen_info);
    LOG_INF("current_pixel_format: %d", capabilities.current_pixel_format);
    LOG_INF("current_orientation: %d", capabilities.current_orientation);

    display_blanking_off(display);

    const struct display_buffer_descriptor buf_desc = {
        .width = x_res, .height = y_res, .buf_size = x_res * y_res, .pitch = DISPLAY_BUFFER_PITCH};

    if (display_write(display, 0, 0, &buf_desc, logo_buf) != 0)
    {
        LOG_ERR("could not write to display");
    }

    if (display_set_contrast(display, 0) != 0)
    {
        LOG_ERR("could not set display contrast");
    }
    size_t ms_sleep = 5;

    while (true)
    {
        // Increase brightness
        for (size_t i = 0; i < 255; i++)
        {
            display_set_contrast(display, i);
            k_sleep(K_MSEC(ms_sleep));
        }

        // Decrease brightness
        for (size_t i = 255; i > 0; i--)
        {
            display_set_contrast(display, i);
            k_sleep(K_MSEC(ms_sleep));
        }
    }
}

My CMakeLists.txt:

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

project(oled_128x64)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE src/main.c)

My nrf52840dk_nrf52840.overlay file:

&pinctrl {
    i2c0_default: i2c0_default {
        group1 {
            psels = <NRF_PSEL(TWIM_SDA, 0, 26)>, // GPIO P0.26
            <NRF_PSEL(TWIM_SCL, 0, 27)>;     // GPIO P0.27
        };
    };

    i2c0_sleep: i2c0_sleep {
        group1 {
            psels = <NRF_PSEL(TWIM_SDA, 0, 26)>, // GPIO P0.26
            <NRF_PSEL(TWIM_SCL, 0, 27)>;     // GPIO P0.27
        };
    };
};

&i2c0 {
    compatible = "nordic,nrf-twim";
    status = "okay";
    pinctrl-0 = <&i2c0_default>;
    pinctrl-1 = <&i2c0_sleep>;
    pinctrl-names = "default", "sleep";
    zephyr,concat-buf-size = <4096>;

    ssd1306: ssd1306@3c {
        compatible = "solomon,ssd1306fb";
        reg = <0x3c>;
        // label = "SSD1306";
        height = <64>;
        width = <128>;
        segment-offset = <0>;
        page-offset = <0>;
        display-offset = <0>;
        multiplex-ratio = <63>;
        prechargep = <0x22>;
        com-invdir;
        segment-remap;
    };
};

My prj.conf:

CONFIG_MAIN_STACK_SIZE=2048

CONFIG_I2C=y

CONFIG_DISPLAY=y
CONFIG_DISPLAY_LOG_LEVEL_DBG=y

CONFIG_SSD1306=y
CONFIG_SSD1306_DEFAULT_CONTRAST=128

CONFIG_LOG=y
CONFIG_SHELL=y

The whole test project can be seen here https://github.com/DBS06/test_zephyr_oled  for anyone who is interested.

Thanks a lot for your time!

Related