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!

  • Ok, I looked deeper into this issue and after I powered the nrf52 off and waited for at least an hour, the Display does not display the image anymore, after powering up again.

    I am guessing, that the SSD1306 driver from Adafruit for the Arduino does something, which the Zephyr Driver does not do, or does it in a different way. I don't think it has something to do with my application and the configuration.

    I already compared the initialization from both drivers quickly, but wasn't able to go deeper into this issue, because I ran out of time.

    Sadly I don't have a logic analyzer at home, which would make debugging a lot easier.

    Maybe I try out an earlier version of the SDK, most likely the v2.1.0 from this example  Printing to an I2C OLED display using nRF Connect SDK .

    If anyone has a hint, I am looking forward to hear it

  • Hello,

    As you mention, it could be interesting to get an logic analyzer trace of the i2c pins to see if anything stands out. I think the problem is there.

    One thing I can think of is that you may be missing pull-up resistors on the i2c pins, if there are none externally you can add bias-pull-up; to pinctrl.

    Kenneth

  • First, thanks for your reply!

    According to the datasheet of my 0.96" OLED, the pull-ups should be already on the board. Nevertheless I will give it a try.

    In the meanwhile I looked closer on the SSD1306 driver on the Zephyr Repo and there I found this issue https://github.com/zephyrproject-rtos/zephyr/pull/62049. This fix is already merged into Zephyr SDK 3.5, but the newest version of zephyr in NCS is 3.4.99, so this could probably solve my issue.

    I already tried to replace the SSD1306 driver from NCS with the driver from the Zephyr Repo, but I had no success, but I have to say, I did this really quick and dirty and had no time to investigate this further, because my child is ill...

    I also have the same display with SPI, and I will try this out.

  • Interesting, if that is the case you may just try to add a k_msleep(50); in start of ssd1306_init() in \zephyr\drivers\display\ssd1306.c for test. (I used 50 just to ensure there is good margin).

    Kenneth

  • It is really interesting.

    As I already said, that's one of the weirdest issues I  ever had, and I am programming microcontrollers since 15 years: "Connecting the display to the Arduino BLE and then connecting it to the nRF52840 DK and the display is working"... WTF?!

    I will try to make a video of it, because I think no one is believing me Sweat smile

    I will definitely retry to add the sleep at ssd1306_init().

    You know, I really want this to work with NCS/Zephyr and this issue is really annoying and frustrating.

    Actually I want to use this display as a mobile debug terminal (without using a PC) for other applications, which can redirect their log output to serial output and I could see what they are doing.

Related