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!

Parents
  • 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

  • DBS06 said:
    I thought that the "chosen {..." just defines/declares an alias for a device, (in this case the display)?

    Yes, or at least it defines the default chosen configuration. But it should not really matter in your case no. 

    Looking at your TWIM_LOG it seems like everything is working, so then it is more likely some missing configuration (hopefully the logic analyzer will help) or some timing issue (hopefully the logic analyzer will help there also).

    Kenneth

  • Hi, I made it!

    I checked the I2C Bus with a logic analyzer (which looked okay, no timing issues or else) and compared it with the Arduino, nothing really different, except some minor differences, which shouldn't matter.

    I still had no clue what causes these strange behavior. Then I rewrote the SSD1306_init() function to get the same initialization procedure as the Arduino, but without success and I still had no Open mouthue, but I knew, it must be a small issue, which I didn't thought about. I was near to give up, but I am not a person who gives up easily and I had nights were I layed in the bed and thought about it. Open mouthDash

    The Zephyr driver wasn't the problem, which didn't actually surprised me anymore. The error or solution was quite easy, but not really obvious on the first or 100th look Wink and I am feeling a little bit stupid Disappointed‍, that I haven't carefully checked the board first.

    I am using the BMD-340 DK from Rigado (now UBlox) and the "nRF only mode" switch was set somehow to LV and not to HV on my board... The switch, especially the description/labels on the board, differs from the nRF52840-DK from Nordic, so it wasn't easy to see. I switched the switch to the correct position and the display worked.

    Then I was interested why these strange behavior occured and used an oscilloscope and made some measurements. The display did work after connecting and disconnecting from the Arduino to the BMD-340 DK, because it was parasitically powered by the I2C Bus and the onboard capacitos from the display were still charged from the Arduino. Without using the Arduino beforehand, the display had to less current for powering up initially and to load the capacitors. Who had thought about this?

    Nevertheless, this issue had something quite good too. I studied the SSD1306 datasheet a couple of times and now I know how the SSD1306 IC works in detail. Furthermore, I got a lot of knowledge how the device tree concept works (I am new to Zephyr), how the layering works (awesome btw), how drivers are implemented in Zephyr and many little things more.

    I wrote you my journey to the solution of my problem, because I think it could be helpful for you or others, if someone else will ran in some similar problem and didn't checked, if the "nrf only switch" is in the "correct" position.

    So thanks for your help and time!
    Kind regards

  • Thank you for providing all the details of debugging and problem description, I am sure this will help others down the line!

    Kenneth

  • Imho It's always good to give something back to the community.

    I really went down into a deep rabbit hole Joy I was really happy when it worked.

    My thoughts when I saw the switch in the wrong position: "f*** my life, this can't be true..." Flushed

  • I am facing issue when tring to interface 1.3" ssd1306 oled with nrf52832 with above given overlay, prj and main file but there is showing "#include "logo_image.h". please help

Reply Children
  • Sorry for the late reply, I just saw that you asked a question.

    Of course it does not work, because it is a custom written code by myself specifically for a 0.96" OLED Display (128x64), but my code is available on github as already mentioned in my original post, but here is the link again  https://github.com/DBS06/test_zephyr_oled .

    For your display, you most likely need to adapt the overly, prj and main file to fit four your display, but you need to do this by your own, if you still have questions you should raise a new ticket, because this one is closed

Related