I'm moving to 2.9.1 and trying to get mipi working with a st7789 display. I have it working with 2.6.1 without mipi, but can't seem to get it working with mipi on a nrf5340dk. It is building and the backlight is working on both. Here is the relevant overlay configuration:
#include <zephyr/dt-bindings/mipi_dbi/mipi_dbi.h>
&pinctrl {
spi4_default: spi4_default {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 0, 6)>,
<NRF_PSEL(SPIM_MOSI, 0, 25)>;
};
};
spi4_sleep: spi4_sleep {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 0, 6)>,
<NRF_PSEL(SPIM_MOSI, 0, 25)>;
low-power-enable;
};
};
};
&spi4 {
status = "okay";
cs-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
};
/ {
chosen {
zephyr,display = &st7789v_st7789v_tl019fqv01;
};
mipi_dbi {
compatible = "zephyr,mipi-dbi-spi";
spi-dev = <&spi4>;
dc-gpios = < &gpio1 11 GPIO_ACTIVE_HIGH>;
reset-gpios = < &gpio1 10 GPIO_ACTIVE_LOW>;
write-only;
#address-cells = <1>;
#size-cells = <0>;
st7789v_st7789v_tl019fqv01: st7789v@0 {
compatible = "sitronix,st7789v";
mipi-max-frequency = <20000000>;
reg = <0>;
width = <240>;
height = <280>;
x-offset = <0>;
y-offset = <20>;
vcom = <0x19>;
gctrl = <0x35>;
vrhs = <0x12>;
vdvs = <0x20>;
mdac = <0x00>;
gamma = <0x01>;
colmod = <0x05>;
lcm = <0x2c>;
porch-param = [0c 0c 00 33 33];
cmd2en-param = [5a 69 02 01];
pwctrl1-param = [a4 a1];
pvgam-param = [D0 04 0D 11 13 2B 3F 54 4C 18 0D 0B 1F 23];
nvgam-param = [D0 04 0C 11 13 2C 3F 44 51 2F 1F 1F 20 23];
ram-param = [00 F0];
rgb-param = [CD 08 14];
mipi-mode = <MIPI_DBI_MODE_SPI_4WIRE>;
};
};
};
The configuration which was working for 2.6.1:
&pinctrl {
spi4_default: spi4_default {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 0, 6)>,
<NRF_PSEL(SPIM_MOSI, 0, 25)>;
};
};
spi4_sleep: spi4_sleep {
group1 {
psels = <NRF_PSEL(SPIM_SCK, 0, 6)>,
<NRF_PSEL(SPIM_MOSI, 0, 25)>;
low-power-enable;
};
};
};
&spi4 {
compatible = "nordic,nrf-spim";
status = "okay";
pinctrl-0 = <&spi4_default>;
pinctrl-1 = <&spi4_sleep>;
pinctrl-names = "default", "sleep";
cs-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
st7789v_st7789v_tl019fqv01: st7789v@0 {
compatible = "sitronix,st7789v";
spi-max-frequency = <20000000>;
reg = <0>;
cmd-data-gpios = < &gpio1 11 GPIO_ACTIVE_LOW>;
reset-gpios = < &gpio1 10 GPIO_ACTIVE_LOW>;
width = <240>;
height = <280>;
x-offset = <0>;
y-offset = <20>;
vcom = <0x19>;
gctrl = <0x35>;
vrhs = <0x12>;
vdvs = <0x20>;
mdac = <0x00>;
gamma = <0x01>;
colmod = <0x05>;
lcm = <0x2c>;
porch-param = [0c 0c 00 33 33];
cmd2en-param = [5a 69 02 01];
pwctrl1-param = [a4 a1];
pvgam-param = [D0 04 0D 11 13 2B 3F 54 4C 18 0D 0B 1F 23];
nvgam-param = [D0 04 0C 11 13 2C 3F 44 51 2F 1F 1F 20 23];
ram-param = [00 F0];
rgb-param = [CD 08 14];
};
};
/ {
chosen {
zephyr,display = &st7789v_st7789v_tl019fqv01;
};
};
The code is very simple, these are the key calls:
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/pwm.h>
#include <zephyr/logging/log.h>
#include <lvgl.h>
#include <app_version.h>
#include <zephyr/drivers/display.h>
#include <zephyr/pm/device.h>
LOG_MODULE_REGISTER(app, LOG_LEVEL_DBG);
#define BUTTON1_NODE DT_NODELABEL(button1)
static const struct device *display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
static const struct gpio_dt_spec button1 = GPIO_DT_SPEC_GET_OR(BUTTON1_NODE, gpios, {0});
static struct gpio_callback button1_cb_data;
/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS 2000
static const struct pwm_dt_spec screen_backlight = PWM_DT_SPEC_GET(DT_ALIAS(screenblk));
bool pwm_on = true;
int pwm_value = 0;
static lv_obj_t *screen_helloworld;
int display_manager_init(void)
{
LOG_DBG("Initializing display manager");
if (!device_is_ready(display_dev)) {
LOG_ERR("Device not ready, aborting test");
return 0;
}
display_blanking_off(display_dev);
return 0;
}
static void screen_backlight_pulse(uint8_t percent) {
LOG_DBG("Backlight pwm set to %d%%", percent);
int ret;
uint32_t step = screen_backlight.period / 100;
uint32_t pulse_width = step * percent;
if (!device_is_ready(screen_backlight.dev)) {
LOG_ERR("Screen backlight is not ready");
return;
}
ret = pwm_set_pulse_dt(&screen_backlight, pulse_width);
if (ret < 0) {
LOG_ERR("Failed to set pulse width: %i", ret);
return;
}
}
//static void button_isr_callback(const struct device *port,struct gpio_callback *cb,uint32_t pins)
void button1_pressed(const struct device *dev, struct gpio_callback *cb,uint32_t pins)
{
LOG_DBG("Button pressed at %" PRIu32, k_cycle_get_32());
ARG_UNUSED(dev);
ARG_UNUSED(cb);
ARG_UNUSED(pins);
LOG_DBG("Button pressed");
if (pwm_on) {
pwm_value = !pwm_value;
LOG_DBG("pwm_value: %d", pwm_value);
if (pwm_value) {
screen_backlight_pulse(100);
} else {
screen_backlight_pulse(0);
}
}
}
int button_manager_init(void)
{
LOG_DBG("Initializing button manager");
int ret;
if (!gpio_is_ready_dt(&button1)) {
LOG_ERR("Error: button device %s is not ready",button1.port->name);
return 0;
}
ret = gpio_pin_configure_dt(&button1, GPIO_INPUT);
if (ret != 0) {
LOG_ERR("Error %d: failed to configure %s pin %d",
ret, button1.port->name, button1.pin);
return 0;
}
ret = gpio_pin_interrupt_configure_dt(&button1,GPIO_INT_EDGE_TO_ACTIVE);
if (ret != 0) {
LOG_ERR("Error %d: failed to configure interrupt on %s pin %d",
ret, button1.port->name, button1.pin);
return 0;
}
gpio_init_callback(&button1_cb_data, button1_pressed, BIT(button1.pin));
gpio_add_callback(button1.port, &button1_cb_data);
LOG_DBG("Set up button at %s pin %d", button1.port->name, button1.pin);
return 0;
}
void screen_helloworld_init()
{
LOG_DBG("Starting...");
static lv_obj_t *helloworld_label;
screen_helloworld = lv_obj_create(NULL);
LOG_DBG("Setting text and back color");
lv_obj_set_style_bg_color(screen_helloworld, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_text_color(screen_helloworld, lv_color_white(), LV_PART_MAIN);
LOG_DBG("Creating label");
helloworld_label = lv_label_create(screen_helloworld);
lv_label_set_text(helloworld_label, "Hello World");
lv_obj_align(helloworld_label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_text_font(helloworld_label, &lv_font_montserrat_16, LV_PART_MAIN);
}
void display_test(){
LOG_DBG("Display test");
if (!device_is_ready(display_dev)) {
LOG_ERR("Device not ready, aborting test");
return;
}
display_blanking_off(display_dev);
}
int main(void)
{
int count = 0;
LOG_DBG("Display Hello World! %s (%s)", CONFIG_BOARD, APP_VERSION_EXTENDED_STRING);
button_manager_init();
if (!device_is_ready(screen_backlight.dev)) {
LOG_WRN("Screen backlight pwm device is not ready");
return -ENODEV;
}
display_manager_init();
if (pwm_on) {
screen_backlight_pulse(100);
} else {
screen_backlight_pulse(0);
}
screen_helloworld_init();
LOG_DBG("calling lv_task_handler");
lv_task_handler();
LOG_DBG("Starting loop");
while (1) {
count++;
LOG_DBG("count = %d", count);
lv_task_handler();
k_msleep(SLEEP_TIME_MS);
if (count > 1000) {
count = 0;
}
}
}
I've based it off code I've seen in github and. the migration guide. I've also seen this post on the lvgl forum, but it didn't have any additional information. I also read this DevZone post Inquiry on ST7789V MIPI-DBI Driver Configuration in NCS 2.9.0 but it did not give me any clues.