80x160 TFT display with the nrf9160 on SPI and ST7735 driver

nRF connect SDK v 2.1.1 using visual studio code.

Hei  

A few busy weeks here, however I've finally tested some more in regards to previous question found under your guide on the forum: 
small-i2c-oled-displays-using-nrf-connect-sdk

To summarize the problem Im trying to get a 160x80 pixel (Width x Height) TFT display with the ST3375 driver to work with the nrf9160 over SPI. 
The specific display can be found here: TFT 160x80 SPI - RS components

Ive tested the TFT with an ESP32-C3 mini in the Arduino environment and confirmed that the display is working fine with same wiring as used for the nrf9160 setup.

For the nrf9160 setup, upon probing the SPI signals I see that MOSI, CLK, CS, RESET and CMD pins are giving nice 3v3 signals. (I had to switch the RESET and CMD pins from the previous gpio0 8 and gpio0 9 as these did not produce any output.

However the result I get on the TFT display is still just garbage.
Ive played around with lowering the SPI max frequency but without any luck.

I can see that the idle state of the data signal (MOSI)  is 3v3 as opposed to 0v in the ESP32 test. I don't think the problem lays here, but just as a note, what would be the best way to define CPOL/CPHA in code when using the ST7735 driver. Would this be done in the .overlay file?


TFT alive but not making sense.



The above is the first byte clocked in when writing to the TFT display. 



The above shows the command (CMD) signal in blue and the CLK signal in yellow around midways in a SPI write session. 

The .overlay code:

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

&arduino_spi {
	compatible = "nordic,nrf-spim";
	status = "okay";
	cs-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; /* D10 */
	pinctrl-0 = <&spi3_default>;
	pinctrl-1 = <&spi3_sleep>;
	pinctrl-names = "default", "sleep";

	st7735r_st7735r_ada_160x128: st7735r@0 {
		compatible = "sitronix,st7735r";
		spi-max-frequency = <250000>;
		reg = <0>;
		cmd-data-gpios = <&gpio0 31 GPIO_ACTIVE_LOW>;	
		reset-gpios = <&gpio0 20  GPIO_ACTIVE_LOW>;
		width = <160>;
		height = <80>;
		x-offset = <0>;
		y-offset = <0>;
		madctl = <0x60>;
		colmod = <0x55>;
		vmctr1 = <0x0e>;
		pwctr1 = [a2 02 84];
		pwctr2 = [c5];
		pwctr3 = [0a 00];
		pwctr4 = [8a 2a];
		pwctr5 = [8a ee];
		frmctr1 = [01 2c 2d];
		frmctr2 = [01 2c 2d];
		frmctr3 = [01 2c 2d 01 2c 2d];
		gamctrp1 = [02 1c 07 12 37 32 29 2d 29 25 2b 39 00 01 03 10];
		gamctrn1 = [03 1d 07 06 2e 2c 29 2d 2e 2e 37 3f 00 00 02 10];
	};
};

The prj.conf code:

CONFIG_LV_Z_MEM_POOL_NUMBER_BLOCKS=8
CONFIG_MAIN_STACK_SIZE=2048

CONFIG_DISPLAY=y
CONFIG_SPI=y

CONFIG_ST7735R=y
CONFIG_DISPLAY_LOG_LEVEL_ERR=y

CONFIG_LOG=y

CONFIG_LVGL=y
CONFIG_LV_MEM_CUSTOM=y
CONFIG_LV_USE_LOG=y
CONFIG_LV_USE_LABEL=y
CONFIG_LV_USE_BTN=y
CONFIG_LV_USE_IMG=y
CONFIG_LV_FONT_MONTSERRAT_14=y

CONFIG_LV_Z_BITS_PER_PIXEL=16

The main src: 

/*
 * Copyright (c) 2018 Jan Van Winkel <[email protected]>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/device.h>
#include <zephyr/drivers/display.h>
#include <lvgl.h>
#include <stdio.h>
#include <string.h>
#include <zephyr/zephyr.h>
#include <sys/printk.h>

#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(app);

void main(void)
{
	uint32_t count = 0U;
	char count_str[11] = {0};
	const struct device *display_dev;
	lv_obj_t *hello_world_label;
	lv_obj_t *count_label;

	display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
	if (!device_is_ready(display_dev)) {
		LOG_ERR("Device not ready, aborting test");
		printk("Could not get %s device\n", display_dev);
		return;
	}

	if (IS_ENABLED(CONFIG_LV_Z_POINTER_KSCAN)) {
		lv_obj_t *hello_world_button;

		hello_world_button = lv_btn_create(lv_scr_act());
		lv_obj_align(hello_world_button, LV_ALIGN_CENTER, 0, 0);
		hello_world_label = lv_label_create(hello_world_button);
	} else {
		hello_world_label = lv_label_create(lv_scr_act());
	}

	lv_label_set_text(hello_world_label, "Hello world!");
	lv_obj_align(hello_world_label, LV_ALIGN_CENTER, 0, 0);

	count_label = lv_label_create(lv_scr_act());
	lv_obj_align(count_label, LV_ALIGN_BOTTOM_MID, 0, 0);

	lv_task_handler();
	display_blanking_off(display_dev);

	while (1) {
		if ((count % 100) == 0U) {
			sprintf(count_str, "%d", count/100U);
			lv_label_set_text(count_label, count_str);
			printk("Updated display\n");
		}
		lv_task_handler();
		k_sleep(K_MSEC(10));
		++count;
	}
}


Any help leading to a working display would be highly appreciated.

Best regards 
Tor

Parents Reply Children
  • Hi again Håkon,

    I have the terminal printing "updated display" once every second. From main.c  we see that this is after we have a screen write/update. No errors printed.

    About the difference of the R and S version I have reached out to Sitronix for a comment.
    Some of the differences are listed here: https://www.sitronix.com.tw/en/products/aiot-device-ddi/

    In regards to the adafruit library Its a bit difficult to decipher if its written for a specific version. Code and comment are somewhat contradicting Adafruit-ST7735.

    However since the MCU seems to be writing data on the SPI bus, and we have something going on on the screen (prints different versions of garbage to the screen on the first few updates before turning blank - see first picture in post for what I refer to as garbage). I though to look into the format of the actual data written. 

    Ive compared the Adafruit library for the "INITR_MINI160x80" as this is the settings that works for the display using the ESP32. The .overlay file is changed slightly in order to replicate the Adafruit driver settings: Adafruit-ST7735

    The following is now in the .overlay file:

     / {
    	chosen {
    		zephyr,display = &st7735r_st7735r_ada_160x128;
    	};
    };
    
    &arduino_spi {
    	compatible = "nordic,nrf-spim";
    	status = "okay";
    	cs-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; /* D10 */
    	pinctrl-0 = <&spi3_default>;
    	pinctrl-1 = <&spi3_sleep>;
    	pinctrl-names = "default", "sleep";
    
    	st7735r_st7735r_ada_160x128: st7735r@0 {
    		compatible = "sitronix,st7735r";
    		spi-max-frequency = <1000000>;
    		reg = <0>;
    		cmd-data-gpios = <&gpio0 31 GPIO_ACTIVE_LOW>;	
    		reset-gpios = <&gpio0 20  GPIO_ACTIVE_LOW>;
    		width = <80>;
    		height = <160>;
    		x-offset = <0>;
    		y-offset = <0>;
    		madctl = <0xC0>;
    		colmod = <0x05>;
    		caset = [00 00 00 4F];
    		raset = [00 00 00 9F];
    		vmctr1 = <0x0e>;
    		invctr = <0x07>;
    		pwctr1 = [a2 02 84];
    		pwctr2 = [c5];
    		pwctr3 = [0a 00];
    		pwctr4 = [8a 2a];
    		pwctr5 = [8a ee];
    		frmctr1 = [01 2c 2d];
    		frmctr2 = [01 2c 2d];
    		frmctr3 = [01 2c 2d 01 2c 2d];
    		gamctrp1 = [02 1c 07 12 37 32 29 2d 29 25 2b 39 00 01 03 10];
    		gamctrn1 = [03 1d 07 06 2e 2c 29 2d 2e 2e 37 3f 00 00 02 10];
    	};
    };

    Ive added the:

    caset = [00 00 00 4F];
    raset = [00 00 00 9F];
    invctr = <0x07>;

    and changed

    madctl = <0xC0>;
    colmod = <0x05>;


    The display is however still printing garbage the first few updates before turning blank.  


  • Thank you for the update.

    Do you have any logic analyzer available? It would be interesting if you could record the startup sequence from both nRF9160 as well as the other board. You could then compare the two to look for differences.

    Tor Kaufmann said:
    About the difference of the R and S version I have reached out to Sitronix for a comment.

    It will be interesting to see if there are any significant differences.I found the datasheets, however, I have not yet compared the two in detail.

    https://www.crystalfontz.com/controllers/Sitronix/ST7735S

    https://www.crystalfontz.com/controllers/Sitronix/ST7735R


  • Hi again Helsing,

    The search and debugging in order to get this TFT working with the nrf9160 continues.


    I have an update from Sitronix,

    Quote: 

    "... ST7735R is an old IC. It has been EOL and has been replaced by ST7735S for supply.
    ST7735R needs to add capacitor parts on the FPC side, while ST7735S does not.
    Both commands are compatible, but the initial code needs to be updated according to its IC to get the best display effect."

    End quote.

    So the ST7735R driver "should" be able to work with the newer S-version. 

    I will most definitely do a logic comparison with the ESP32 board that has the TFT working with the Adafruit library to see if I can spot any difference in the physical signals on the SPI bus.

    I find it strange that the ST7735R library is not working when I can see the settings in the .overlay file matches the settings in the Adafruit library.

  • Thank you for the update.

    Tor Kaufmann said:
    but the initial code needs to be updated according to its IC to get the best display effect.
    Tor Kaufmann said:
    I will most definitely do a logic comparison with the ESP32 board that has the TFT working with the Adafruit library to see if I can spot any difference in the physical signals on the SPI bus.

    Yes, this will be interesting to see.

  • Would you be able to upload your current project? I could capture the startup sequence with a logic analyzer.

Related