SSD1306 OLED display fails for nRF52840 boards using nRF Connect SDK 3.2.1

Hello,

I’ve been using SSD1306 128x64 OLED displays with nRF52840 boards for several years without any issues, building with nRF Connect SDK 2.5.x to 3.1.0. Recently, I've been attempting to move existing NCS 3.1.0 projects over to NCS 3.2.1. However, I’m unable to successfully get the SSD1306 OLED display to work with any of the nRF52840 boards using the latest nRF Connect SDK 3.2.1.

All the nRF52840 boards with SSD1306 display work fine when built with vanilla Zephyr 4.1.x and 4.2.x.

Also, the target for thingy53/nrf5340/cpuapp/ns failed to run successfully with both NCS 3.1.0 and NCS 3.2.1. The target thingy53/nrf5340/cpuapp ran fine with the SSD1306 OLED.

To try to isolate the issue, I’ve taken the Zephyr sample, “/opt/nordic/ncs/v3.2.1/zephyr/samples/subsys/display/cfb_custom_font” and built it for a couple of different nRF52840 boards (e.g. particle_xenon_nrf52840, promicro_nrf52840) plus thingy/nrf5340/cpuapp.

Built exactly the same sample "cfb_custom_font" main.c (modified to display board target and Zephyr version) & used the same devicetree overlays. All the builds compiled and linked without any issues.  However, only particle_xenon/nrf52840 using NCS 3.1.0 and thingy53/nrf5340/cpuapp using NCS 3.2.1 ran successfully with the SSD1306 displays.

I used the below board configuration and the overlays with the ssd1306 devicetree included in the I2C sections of the overlays. Default sysbuild option used from with the nRF Connect SDKs.

I don't see anything that is obvious that may be wrong. I would appreciate if you could determine the cause of the issue with the nRF52840 boards with SSD1306 when using  nRF Connect SDK 3.2.1.

Also, Is there any reason as to why the target for thingy53/nrf5340/ns doesn’t run successfully with the I2C SSD1306 display?

Thank you.

particle_xenon_nrf52840.overlay:

/ {
	chosen {
		zephyr,display = &ssd1306;
		zephyr,console = &uart0;
		zephyr,shell-uart = &uart0;
	};
};

&uart0 {
	compatible = "nordic,nrf-uarte";
	status = "okay";
	current-speed = <115200>;
	pinctrl-0 = <&uart0_default>;
	pinctrl-1 = <&uart0_sleep>;
	pinctrl-names = "default", "sleep";
};

&gpio0{
	status = "okay";
};

&i2c0 {
	compatible = "nordic,nrf-twi";
	status = "okay";
	pinctrl-0 = <&i2c0_default>;
	pinctrl-1 = <&i2c0_sleep>;
    pinctrl-names = "default", "sleep";
	ssd1306: ssd1306@3c {
		compatible = "solomon,ssd1306fb";
		reg = <0x3c>;
		width = <128>;
		height = <64>;
		segment-offset = <0>;
		page-offset = <0>;
		display-offset = <0>;
		multiplex-ratio = <63>;
		segment-remap;
		com-invdir;
		prechargep = <0x22>;
	};
};

thingy53_nrf5340_cpuapp.overlay

/ {
	chosen {
		zephyr,display = &ssd1306;
	};
};

&pinctrl {
	i2c0_default: i2c0_default {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 0, 5)>,
				<NRF_PSEL(TWIM_SCL, 0, 4)>;
		};
	};

	i2c0_sleep: i2c0_sleep {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 0, 5)>,
				<NRF_PSEL(TWIM_SCL, 0, 4)>;
		};
	};
};

&i2c0 {
	compatible = "nordic,nrf-twim";
	status = "okay";
	clock-frequency = <I2C_BITRATE_FAST>;
	zephyr,concat-buf-size = <4096>;
	pinctrl-0 = <&i2c0_default>;
	pinctrl-1 = <&i2c0_sleep>;
    pinctrl-names = "default", "sleep";
	ssd1306: ssd1306@3c {
		compatible = "solomon,ssd1306fb";
		reg = <0x3c>;
		width = <128>;
		height = <64>;
		segment-offset = <0>;
		page-offset = <0>;
		display-offset = <0>;
		multiplex-ratio = <63>;
		segment-remap;
		com-invdir;
		prechargep = <0x22>;
	};
};

prj.conf:

CONFIG_HEAP_MEM_POOL_SIZE=16384

CONFIG_DISPLAY=y
CONFIG_SSD1306=y

CONFIG_CHARACTER_FRAMEBUFFER=y
CONFIG_CHARACTER_FRAMEBUFFER_USE_DEFAULT_FONTS=n

CONFIG_LOG=y

# Preferred SHELL options
CONFIG_SHELL=y
CONFIG_DEVICE_SHELL=y
CONFIG_INIT_STACKS=y
CONFIG_KERNEL_SHELL=y
CONFIG_SHELL_STATS=n
CONFIG_I2C_SHELL=y

# Enable RTT to replace UART
CONFIG_UART_CONSOLE=y
CONFIG_USE_SEGGER_RTT=n
CONFIG_SHELL_BACKEND_RTT=n

CONFIG_LOG_BACKEND_UART=y
CONFIG_SHELL_LOG_BACKEND=y

Console Output 

NCS 3.2.1 build  - particle_xenon_nrf52840 with  SSD1306 (particle_xenon/nrf52840):

*** Booting nRF Connect SDK v3.2.1-d8887f6f32df ***
*** Using Zephyr OS v4.2.99-ec78104f1569 ***

Display device not ready

Failed to turn off display blanking

NCS 3.1.0 build  - particle_xenon_nrf52840 with  SSD1306 (particle_xenon/nrf52840):

*** Booting nRF Connect SDK v3.1.0-6c6e5b32496e ***
*** Using Zephyr OS v4.1.99-1612683d4010 ***
Zephyr v4.1.99
Board target: particle_xenon/nrf52840

NCS 3.2.1 build  - thingy53_nrf5340_cpuapp with  SSD1306:

*** Booting nRF Connect SDK v3.2.1-d8887f6f32df ***
*** Using Zephyr OS v4.2.99-ec78104f1569 ***
[00:00:00.035,614] <inf> udc_nrf: Initialized
Zephyr v4.2.99
Board target: thingy53/nrf5340/cpuapp

NCS 3.1.0 build  - thingy53_nrf5340_cpuapp with  SSD1306:

[Disconnected]
[Connected]
*** Booting nRF Connect SDK v3.1.0-6c6e5b32496e ***
*** Using Zephyr OS v4.1.99-1612683d4010 ***
Zephyr v4.1.99
Board target: thingy53/nrf5340/cpuapp

Vanilla Zephyr 4.2.x from terminal command line: SSD1306 displays works fine.

*** Booting Zephyr OS build v4.2.0-717-g85e135362445 ***
Zephyr v4.2.99
Board target: particle_xenon/nrf52840
uart:~$ 

  • Hi Vidar,

    I had another look at the Thingy53 NS builds for non-BT applications.

    After making the following Kconfig change, the non-BT applications now run on the Thing53.

    # Set TF-M to share the default console
    CONFIG_TFM_SECURE_UART0=y
    CONFIG_TFM_SECURE_UART_SHARE_INSTANCE=y


    The Thingy53 uses the USB cdc_acm_uart for the application console and UART0 by default is disabled.

    It seems that although UART0 is disabled, TF-M logging still requires UART0 configuration and to share TF-M's UART instance with the NS application.

    Earlier, without the above configuration,  NS applications on Thingy53 worked only if the TF-M logging was disabled via "CONFIG_TFM_LOG_LEVEL_SILENCE=y".

    I've verified the resolution using the Zephyr sample "/zephyr/samples/subsys/display/cfb" with 
    TF-M Configurable builds.

    TF-M size set to 96kB using static partitioning file "pm_static_thingy53_nrf5340_cpuapp_ns.yml" in the project folder.

    thingy53_nrf5340_cpuapp_ns.conf:

    # TF-M profile has to be properly configured to be able to run
    # the Bluetooth stack which uses PSA crypto API.
    # The following configuration is a minimal set of options required.
    CONFIG_TFM_PROFILE_TYPE_NOT_SET=y
    CONFIG_TFM_PARTITION_PLATFORM=y
    CONFIG_TFM_PARTITION_CRYPTO=y
    CONFIG_TFM_PARTITION_INTERNAL_TRUSTED_STORAGE=y
    CONFIG_TFM_PARTITION_PROTECTED_STORAGE=n
    CONFIG_TFM_PARTITION_INITIAL_ATTESTATION=n
    
    # This Board implies building Non-Secure firmware
    CONFIG_TRUSTED_EXECUTION_NONSECURE=y
    
    # Set TF-M to share the default console
    CONFIG_TFM_SECURE_UART0=y
    CONFIG_TFM_SECURE_UART_SHARE_INSTANCE=y

    Console output:

    [Disconnected]
    [Connected]
    Initialized ssd1306@3c
    font width 10, font height 16
    font width 15, font height 24
    font width 20, font height 32
    x_res 128, y_res 64, ppt 8, rows 8, cols 128
    
    [00:00:00.000,885] <inf> udc_nrf: Preinit
    *** Booting nRF Connect SDK v3.2.1-d8887f6f32df ***
    *** Using Zephyr OS v4.2.99-ec78104f1569 ***
    [00:00:00.012,756] <inf> udc_nrf: Initialized
    [00:00:00.013,153] <dbg> cfb: cfb_framebuffer_init: number of fonts 3
    [00:00:00.016,967] <inf> udc_nrf: SUSPEND state detected
    [00:00:00.114,532] <inf> udc_nrf: Reset
    [00:00:00.114,562] <inf> udc: ep 0x80 is not halted|disabled
    [00:00:00.114,593] <inf> udc_nrf: RESUMING from suspend


    Also, I've ran the sample NS  build of sample "tfm_hello_world" on the Thingy53. I noticed an unexpected behaviour.

    Initially, I got the following console output below. Some messages seem to have been dropped for some reason.

    [Connected]
    84--- 16 messages dropped ---
    dfde2b22388088bd36ccfbcdd9aab068e63105303997eb0d5a
    Example finished successfully!
    [00:00:00.005,279] <inf> udc_nrf: SUSPEND state detected
    [00:00:00.103,698] <inf> udc_nrf: Reset
    [00:00:00.103,729] <inf> udc: ep 0x80 is not halted|disabled
    [00:00:00.103,759] <inf> udc_nrf: RESUMING from suspend



    If I set "CONFIG_SHELL=y", then the console output is:

    [Connected]
    *** Booting nRF Connect SDK v3.2.1-d8887f6f32df ***
    *** Using Zephyr OS v4.2.99-ec78104f1569 ***
    Hello World! thingy53/nrf5340/cpuapp/ns
    Reading some secure memory that NS is allowed to read
    Approximate IPC overhead us: 174
    FICR->INFO.FLASH: 0x00000400
    Generating random number
    0xf1663a5d54a3d23924674a231407b1e44908755aad87a0f8a8c364e5986025ca
    Hashing 'Hello World! thingy53/nrf5340/cpuapp/ns'
    SHA256 digest:
    0x8d062694d91c7504ce428d05b015fb1bfcb3437cf5a2fab6ec5db63830751c1e
    Example finished successfully!
    


    I don't understand why the SHELL configuration resolves the dropped messages.

    Can you please run the "tfm_hello_world" sample on a Thingy53 to see if you get the same behaviour. Thank you.

    Best Regards,
    Ravi

  • HI Vidar,

    I've found out that the TF-M configuration and logging is slightly different on the nRF5340-DK.

    According to log output from TF-M on the nRF5340-DK, one option is to wire the secure and non-secure UART peripherals to the same pins, i.e. physically wire together the pins P0.25 and P0.26 to P0.20 and P0.22, respectively.

    After wiring together P.025 -> P0.20 & P0.26 -> P0.22, plus using the following TF-M configuration (same as what was used on the Thingy53) worked for NS applications.

    # TF-M profile has to be properly configured to be able to run
    # the Bluetooth stack which uses PSA crypto API.
    # The following configuration is a minimal set of options required.
    CONFIG_TFM_PROFILE_TYPE_NOT_SET=y
    CONFIG_TFM_PARTITION_PLATFORM=y
    CONFIG_TFM_PARTITION_CRYPTO=y
    CONFIG_TFM_PARTITION_INTERNAL_TRUSTED_STORAGE=y
    CONFIG_TFM_PARTITION_PROTECTED_STORAGE=n
    CONFIG_TFM_PARTITION_INITIAL_ATTESTATION=n
    
    # This Board implies building Non-Secure firmware
    CONFIG_TRUSTED_EXECUTION_NONSECURE=y
    
    # Set TF-M to share the default console
    CONFIG_TFM_SECURE_UART0=y
    CONFIG_TFM_SECURE_UART_SHARE_INSTANCE=y


    Console output (shared by application and TF-M):

    All pins have been configured as non-secure
    Booting TF-M v2.2.0
    [Sec Thread] Secure image initializing!
    TF-M Float ABI: Hard
    Lazy stacking enabled
    
    *** Booting nRF Connect SDK v3.2.1-d8887f6f32df ***
    *** Using Zephyr OS v4.2.99-ec78104f1569 ***
    [00:00:05.421,661] <inf> app: Board target: nrf5340dk/nrf5340/cpuapp/ns
    
    [00:00:05.421,691] <inf> app: bme688x_iaq_ssd1306 - App started
    [00:00:05.421,691] <inf> app: Init ssd1306
    [00:00:05.421,875] <dbg> cfb: cfb_framebuffer_init: number of fonts 2
    [00:00:05.449,005] <inf> oled: Index[0] font dimensions  5x8
    [00:00:05.449,035] <inf> oled: Index[1] font dimensions  8x8
    [00:00:05.449,035] <inf> oled: Selected font: index[0]
    [00:00:05.449,066] <inf> oled: x_res 128, y_res 64, ppt 8, rows 8, cols 128


    Do you know where it is specified that TF-M uses P.25 & P.26 on the nRF5340DK for logging? I couldn't find any reference to it in the nRF5340-DK Hardware Guide or the nRF5340-DK devicetree.

    Also, What pins are used on the Thingy53 for TF-M logging, as I do not see the shared TF-M / application outputs in the Thing53 console output as in nRF5340-DK?

    Thank you.

    Best Regards,
    Ravi

  • Hi Ravi,

    I have tried with the TF-M hello world sample as well now and I did not manage to reproduce the behaviour you observed. I did however have to  add "k_msleep(1000);" at the beginning of main to allow the USB to attach before the messages were printed. 

    Best regards,

    Vidar

  • HI Vidar,

    I'm a little unclear what you were not able to reproduce.

    Did you try running the sample "cfb" with a SSD1306 & TF-M size 96kB on a Thingy53 (and also a nRF5340-DK),  with the TF-M configuration that I used below?

    I used a Thingy53 and also a nRF4340-DK, sample "cfb" with a SSD1306 & TF-M size  96kB. The NS "cfb" application ran sucessfully provided the following configuration gets used. TF-M logging shared with application console.

    # TF-M profile has to be properly configured to be able to run
    # the Bluetooth stack which uses PSA crypto API.
    # The following configuration is a minimal set of options required.
    CONFIG_TFM_PROFILE_TYPE_NOT_SET=y
    CONFIG_TFM_PARTITION_PLATFORM=y
    CONFIG_TFM_PARTITION_CRYPTO=y
    CONFIG_TFM_PARTITION_INTERNAL_TRUSTED_STORAGE=y
    CONFIG_TFM_PARTITION_PROTECTED_STORAGE=n
    CONFIG_TFM_PARTITION_INITIAL_ATTESTATION=n
    
    # This Board implies building Non-Secure firmware
    CONFIG_TRUSTED_EXECUTION_NONSECURE=y
    
    CONFIG_TFM_SECURE_UART0=y
    CONFIG_TFM_SECURE_UART_SHARE_INSTANCE=y


    Thingy53 console output:

    [Connected]
    Initialized ssd1306@3c
    font width 10, font height 16
    font width 15, font height 24
    font width 20, font height 32
    x_res 128, y_res 64, ppt 8, rows 8, cols 128
    
    [00:00:00.000,885] <inf> udc_nrf: Preinit
    *** Booting nRF Connect SDK v3.2.1-d8887f6f32df ***
    *** Using Zephyr OS v4.2.99-ec78104f1569 ***
    [00:00:00.012,542] <inf> udc_nrf: Initialized
    [00:00:00.012,969] <dbg> cfb: cfb_framebuffer_init: number of fonts 3
    [00:00:00.016,784] <inf> udc_nrf: SUSPEND state detected
    [00:00:00.115,142] <inf> udc_nrf: Reset
    [00:00:00.115,203] <inf> udc: ep 0x80 is not halted|disabled
    [00:00:00.115,203] <inf> udc_nrf: RESUMING from suspend

    nRF4350-DK output:

    Writing random Hardware Unique Keys to the KMU.
    Success
    All pins have been configured as non-secure
    Booting TF-M v2.2.0
    [Sec Thread] Secure image initializing!
    Initialized ssd1306@3c
    
    *** Booting nRF Connect SDK v3.2.1-d8887f6f32df ***
    *** Using Zephyr OS v4.2.99-ec78104f1569 ***
    [00:00:00.412,933] <dbg> cfb: cfb_framebuffer_init: number of fonts 3
    uart:~$ font width 10, font height 16
    font width 15, font height 24
    font width 20, font height 32
    x_res 128, y_res 64, ppt 8, rows 8, cols 128


    If TF-M logging is disabled with "CONFIG_TFM_LOG_LEVEL_SILENCE=y", then the NS applications also run successfully.

    Regarding "tfm_hello_world" sample with Thingy53:

    "k_msleep(1000);" doesn't quite work of rme. Instead, "CONFIG_SHELL=y" worked consistently without the delay.

    Questions:

    1. Why is it that for the Thingy53, we do not observe the "Booting TF-M v2.2.0" TF-M messages similar to what is observed with nRF5340-DK output when both TF-M and application share the same output device?

    Is it because the TF_M logs get output before the Thingy53 USB gets attached?

    How do I get the TF-M messages to show up on the Thingy53?

    2. For the nRF5340-DK v2.0.0 , the TF-M Logging docs state that to get the UART output from the TF-M, one can either physically connect pins P0.25/P0.26 to P0.20/P0.22 or change the pins used by TF-M. 

    Where is it defined that TF-M logging output uses pins P0.25 and P0.26? I cannot find it anywhere, other than the brief mention in the "TF_M Logging" docs. I would like to try using the nRF5340-DK UART1 to output TF-M logs.

    Thank you.

    Best Regards,
    Ravi

  • Hi Ravi,

    I only tried the TF-M Hello World sample. I was looking for one specific sample and exact steps to reproduce the problem.

    "k_msleep(1000);" doesn't quite work of rme. Instead, "CONFIG_SHELL=y" worked consistently without the delay.

    When you enable the logger module with or without the shell, the logs will not be printed immediately (provided LOG_MODE_IMMEDIATE is not selected), but instead placed in a buffer and processed in a separate logger thread that is run once every second. The original sample is only using console output and prints everything immediately.

    1. Why is it that for the Thingy53, we do not observe the "Booting TF-M v2.2.0" TF-M messages similar to what is observed with nRF5340-DK output when both TF-M and application share the same output device?

    Because it does not have UART logging. USB logging is not available in TF-M. Supporting a USB stack in TF-M is entirely different from supporting a UART driver.

    2. For the nRF5340-DK v2.0.0 , the TF-M Logging docs state that to get the UART output from the TF-M, one can either physically connect pins P0.25/P0.26 to P0.20/P0.22 or change the pins used by TF-M. 

    Where is it defined that TF-M logging output uses pins P0.25 and P0.26? I cannot find it anywhere, other than the brief mention in the "TF_M Logging" docs. I would like to try using the nRF5340-DK UART1 to output TF-M logs.

    TF-M is getting the pin assignments from the devicetree except for the nRF5340DK and other nordic boards where it is hardcoded here: https://github.com/nrfconnect/sdk-trusted-firmware-m/commit/70abd66a1225a89312a8e6775c09d18873ff0579. Anyway, logging on the nRF5340 DK is now using UART1. So when you are not using the network core, you will have one com port available just for the TF-M and another for the application so the application and TF-M does not need to share the same uart instance. 

Related