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

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

  • I ordered today a cheap logic analyzer, which should arrive on Thursday.

    The TWIM Log looks actually pretty good, I get always a Success. The I2C connection itself seems to work, otherwise the content of the display wouldn't change between the Arduino and the nrf52840-DK. But nevertheless I will provide a Log as soon as possible.

    Btw thanks a lot for your time and effort!

  • Here is the TWIM_LOG without the constant change in my while loop:

    [00:00:00.252,838] <wrn> NRFX_TWIM: Function: nrfx_twim_xfer, error code: NRFX_SUCCESS.
    --- 2 messages dropped ---
    [00:00:00.253,845] <wrn> NRFX_TWIM: Function: nrfx_twim_xfer, error code: NRFX_SUCCESS.
    [00:00:00.254,302] <inf> NRFX_TWIM: Transfer type: XFER_TX.
    [00:00:00.254,333] <inf> NRFX_TWIM: Transfer buffers length: primary: 3, secondary: 0.
    [00:00:00.254,364] <wrn> NRFX_TWIM: Function: nrfx_twim_xfer, error code: NRFX_SUCCESS.
    [00:00:00.254,821] <inf> NRFX_TWIM: Transfer type: XFER_TX.
    [00:00:00.254,852] <inf> NRFX_TWIM: Transfer buffers length: primary: 3, secondary: 0.
    [00:00:00.254,882] <wrn> NRFX_TWIM: Function: nrfx_twim_xfer, error code: NRFX_SUCCESS.
    [00:00:00.255,340] <inf> NRFX_TWIM: Transfer type: XFER_TX.
    [00:00:00.255,371] <inf> NRFX_TWIM: Transfer buffers length: primary: 3, secondary: 0.
    [00:00:00.255,432] <wrn> NRFX_TWIM: Function: nrfx_twim_xfer, error code: NRFX_SUCCESS.
    [00:00:00.255,889] <inf> NRFX_TWIM: Transfer type: XFER_TX.
    [00:00:00.255,889] <inf> NRFX_TWIM: Transfer buffers length: primary: 2, secondary: 0.
    [00:00:00.255,950] <wrn> NRFX_TWIM: Function: nrfx_twim_xfer, error code: NRFX_SUCCESS.
    *** Booting nRF Connect SDK v2.5.2 ***
    [00:00:00.256,347] <inf> display: x_resolution: 128
    [00:00:00.256,347] <inf> display: y_resolution: 64
    [00:00:00.256,378] <inf> display: supported pixel formats: 4
    [00:00:00.256,378] <inf> display: screen_info: 1
    [00:00:00.256,408] <inf> display: current_pixel_format: 4
    [00:00:00.256,408] <inf> display: current_orientation: 0
    [00:00:00.256,439] <inf> NRFX_TWIM: Transfer type: XFER_TX.
    [00:00:00.256,469] <inf> NRFX_TWIM: Transfer buffers length: primary: 2, secondary: 0.
    [00:00:00.256,530] <wrn> NRFX_TWIM: Function: nrfx_twim_xfer, error code: NRFX_SUCCESS.
    [00:00:00.256,866] <dbg> ssd1306: ssd1306_write: x 0, y 0, pitch 128, width 128, height 64, buf_len 1024
    [00:00:00.256,896] <inf> NRFX_TWIM: Transfer type: XFER_TX.
    [00:00:00.256,927] <inf> NRFX_TWIM: Transfer buffers length: primary: 9, secondary: 0.
    [00:00:00.256,988] <wrn> NRFX_TWIM: Function: nrfx_twim_xfer, error code: NRFX_SUCCESS.
    [00:00:00.258,117] <inf> NRFX_TWIM: Transfer type: XFER_TX.
    [00:00:00.258,148] <inf> NRFX_TWIM: Transfer buffers length: primary: 1025, secondary: 0.
    [00:00:00.258,209] <wrn> NRFX_TWIM: Function: nrfx_twim_xfer, error code: NRFX_SUCCESS.
    [00:00:00.360,046] <inf> NRFX_TWIM: Transfer type: XFER_TX.
    [00:00:00.360,046] <inf> NRFX_TWIM: Transfer buffers length: primary: 3, secondary: 0.
    [00:00:00.360,107] <wrn> NRFX_TWIM: Function: nrfx_twim_xfer, error code: NRFX_SUCCESS.

  • 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

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

Children
Related