How to configure and confirm CPU frequency on nRF5340? (LVGL performance concern with ST7789)

Hi,

I'm developing an embedded GUI application using LVGL on the nRF5340 application core with Zephyr RTOS.

The display I'm using is an ST7789 TFT panel (240×284 resolution), connected over SPI.
However, the performance is quite limited (e.g. UI response and animation framerate), and I suspect that CPU frequency may be lower than expected (possibly running at 64 MHz instead of 128 MHz).

Questions:

  1. What is the default CPU clock frequency for the nRF5340 application core in Zephyr?

    • Is it 64 MHz or 128 MHz by default?

    • Where is this defined or configured?

  2. Is it possible and recommended to manually increase the CPU frequency (e.g. from 64 MHz to 128 MHz)?

    • If yes, how can I configure this in Zephyr?

    • Are there any sample codes or safe register-level operations?

  3. How can I confirm the actual running frequency of the application core at runtime?

    • Is there a way to read a register or measure it?

    • Any Zephyr APIs or debug tools available for this?

My goal is to improve LVGL performance by maximizing CPU clock frequency in a safe and supported way.

Development Details:

  • Board: Custom nRF5340 board

  • SDK Version: V3.0.2

  • Screen: ST7789 SPI panel (240×284)

  • LVGL: Use Official Sample

  • Interface: SPI (no DMA currently)

  • Toolchain Version: v3.0.2

Device Tree Snippet:

/dts-v1/;
#include <nordic/nrf5340_cpuappns_qkaa.dtsi>
#include "elena_nrf5340-pinctrl.dtsi"

/ {
	model = "Elena (CPUAPP Non-Secure)";
	compatible = "Sab1e,elena-nrf5340-cpuapp-ns";
	chosen {
		zephyr,sram = &sram0_ns;
		zephyr,flash = &flash0;
		zephyr,code-partition = &slot0_ns_partition;
		zephyr,console = &uart1;
		zephyr,shell-uart = &uart1;
		zephyr,display = &st7789v;
	};
	mipi-dbi-spi {
		compatible = "zephyr,mipi-dbi-spi";
		st7789v: st7789v@0 {
			compatible = "sitronix,st7789v";
			reg = <0x0>;
			label = "ST7789V";
			status = "okay";
			width = <240>;
			height = <284>;
			x-offset = <0>;
			y-offset = <0>;
			vcom = <0x32>;
			gctrl = <0x35>;
			vrhs = <0x15>;
			mdac = <0x00>;
			gamma = <0x01>;
			colmod = <0x55>;
			lcm = <0x2c>;
			porch-param = [ 0c 0c 00 33 33  ];
			cmd2en-param = [ 5a 69 02 01  ];
			pwctrl1-param = [ a4 a1  ];
			pvgam-param = [ d0 08 0e 09 09 05 31 33 48 17 14 15 31 34  ];
			nvgam-param = [ d0 08 0e 09 09 15 31 33 48 17 14 15 31 34  ];
			ram-param = [ 00 f0  ];
			rgb-param = [ cd 08 14  ];
			vdvs = <0x20>;
			mipi-mode = "MIPI_DBI_MODE_SPI_4WIRE";
			mipi-max-frequency = <15000000>;
		};


		dc-gpios = <&gpio0 12 0>;
		reset-gpios = <&gpio0 19 GPIO_ACTIVE_LOW>;
		#address-cells = <1>;
		#size-cells = <0>;
		spi-dev = <&spi0>;
	};
	pointer {
		compatible = "zephyr,lvgl-pointer-input";
		input = <&cst816s>;
		invert-y;
	};
	

};

&clock {
	clocks = <&hfxo>;
};


#include "elena_nrf5340-cpuapp_partitioning.dtsi"
#include "elena_nrf5340-shared_sram.dtsi"

&gpio0 {
	status = "okay";
};

&gpio1 {
	status = "okay";
};

&gpiote {
	status = "okay";
};

&spi0 {
	status = "okay";
	cs-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
	pinctrl-0 = <&spi0_default>;
	pinctrl-names = "default";
	max-frequency = <32000000>;
};

&pinctrl {
	spi0_default: spi0_default {
		group1 {
			psels = <NRF_PSEL(SPIM_SCK, 0, 8)>, <NRF_PSEL(SPIM_MOSI, 0, 9)>;
		};
	};

	uart1_default: uart1_default {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 20)>, <NRF_PSEL(UART_RX, 0, 22)>;
		};
	};

	i2c2_default: i2c2_default {
		group1 {
			psels = <NRF_PSEL(TWIM_SCL, 1, 3)>, <NRF_PSEL(TWIM_SDA, 1, 2)>;
		};
	};
};

&uart1 {
	status = "okay";
	compatible = "nordic,nrf-uarte";
	pinctrl-0 = <&uart1_default>;
	pinctrl-names = "default";
	current-speed = <115200>;
};

&i2c2 {
	status = "okay";
	pinctrl-0 = <&i2c2_default>;
	pinctrl-names = "default";
	cst816s: cst816s@15 {
		compatible = "hynitron,cst816s";
		reg = <0x15>;
		rst-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>;
	};
};

prj.conf:

# GPIO
CONFIG_GPIO=y

# Display peripherals
CONFIG_SPI=y
CONFIG_MIPI_DBI_SPI=y
CONFIG_SPI_ASYNC=y

# Display configuration
CONFIG_DISPLAY=y
CONFIG_DISPLAY_LOG_LEVEL_ERR=y

# Touch
CONFIG_I2C=y
CONFIG_INPUT=y
CONFIG_INPUT_CST816S_INTERRUPT=n

# Log
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_CONSOLE=y
CONFIG_PRINTK=y
CONFIG_UART_CONSOLE=y
CONFIG_LOG=y
CONFIG_SHELL=y

# LVGL
CONFIG_LV_Z_POINTER_INPUT=y
CONFIG_LV_Z_MEM_POOL_SIZE=65536
CONFIG_LV_Z_SHELL=y

CONFIG_MAIN_STACK_SIZE=8192

CONFIG_LVGL=y

CONFIG_LV_USE_ST7789=y

CONFIG_LV_USE_LOG=y
CONFIG_LV_USE_LABEL=y
CONFIG_LV_USE_ARC=y
CONFIG_LV_USE_MONKEY=y
CONFIG_LV_FONT_MONTSERRAT_14=y

# LVGL Color depth RGB565
CONFIG_LV_COLOR_DEPTH_16=y
CONFIG_LV_COLOR_16_SWAP=y

CONFIG_LV_FONT_MONTSERRAT_12=y
CONFIG_LV_FONT_MONTSERRAT_14=y
CONFIG_LV_FONT_MONTSERRAT_16=y
CONFIG_LV_FONT_MONTSERRAT_18=y
CONFIG_LV_FONT_MONTSERRAT_24=y

# Benchmark Demo
CONFIG_LV_USE_FONT_COMPRESSED=y

CONFIG_LV_Z_DEMO_BENCHMARK=y

CONFIG_LV_USE_SYSMON=y
CONFIG_LV_USE_PERF_MONITOR=y

CONFIG_CLOCK_CONTROL_LOG_LEVEL_DBG=y

Any guidance on how to verify and increase the CPU clock safely, and whether it may improve ST7789 + LVGL rendering performance would be greatly appreciated!

Thanks in advance!

Best regards,

Sab1e

  • Hi all,

    I was trying to improve the performance of LVGL on the nRF5340 by increasing the application core clock speed. I’m using an ST7789 display with a resolution of 240×284, and I noticed rendering was slow, so I wanted to verify and potentially increase the running frequency.

    After some investigation and DWT (Data Watchpoint and Trace) measurement, it appeared that my application core was running at 64 MHz rather than the expected 128 MHz.

    Eventually, I found that the solution is to explicitly set the HFCLK divider to 1 using the nrfx_clock_divider_set API. This feature is available when CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT is defined.

    Here is the working fix I applied:

    #include <nrfx_clock.h>
    
    int main(){
    ...
    #if NRFX_CLOCK_ENABLED && (defined(CLOCK_FEATURE_HFCLK_DIVIDE_PRESENT) || NRF_CLOCK_HAS_HFCLK192M)
        nrfx_clock_divider_set(NRF_CLOCK_DOMAIN_HFCLK, NRF_CLOCK_HFCLK_DIV_1);
    #endif
    ...
        return 0;
    }

    This successfully configured the HFCLK to 1x divider, giving me 128 MHz, and my LVGL performance improved significantly.

    The solution was originally provided in this thread.

    Additionally, the standard SPIM modules only support speeds up to 16 Mbps, whereas SPIM4 can support up to 32 Mbps when the application core is running at 128 MHz.

    Hope this helps others facing the same issue!

    Best regards,

    Sab1e

Related