SPI driver ST7789V1.3-inch LCD
The R128 platform provides the SPI TFT interface of SPI DBI, which has the following features:
- Supports DBI Type C 3 Line/4 Line Interface Mode
- Supports 2 Data Lane Interface Mode
- Supports data source from CPU or DMA
- Supports RGB111/444/565/666/888 video format
- Maximum resolution of RGB666 240 x 320@30Hz with single data lane
- Maximum resolution of RGB888 240 x 320@60Hz or 320 x 480@30Hz with dual data lane
- Supports tearing effect
- Supports software flexible control video frame rate
At the same time, the SPILCD driver framework is provided for use by SPI screens.
The SPI screen adapted this time is ZJY130S0800TG01
, which is driven by SPI.
The pin configuration is as follows:
R128 Devkit | TFT module |
---|---|
PA12 | CS |
PA13 | SCL |
PA18 | SDA |
PA9 | BLK |
PA20 | RES |
PA19 | DC |
3V3 | VCC |
GND | GND |
Loading plan
The development board we use is R128-Devkit and we need to develop C906 core applications, so the loading solution is r128s2_module_c906
$ source envsetup.sh $ lunch_rtos 1
Set up SPI driver
The screen uses the SPI driver, so you need to check the SPI driver and run mrtos_menuconfig
to enter the configuration page. Go to the address below to find SPI Devices
Drivers Options ---> soc related device drivers ---> SPI Devices ---> -*- enable spi driver
Configure SPI pins
Open your favorite editor and modify the file: board/r128s2/module/configs/sys_config.fex
. We don’t need to use the SPI HOLD and SPI WP pins here, just comment them out.
;------------------------------------------------- ---------------------------------- ;SPI controller configuration ;------------------------------------------------- ---------------------------------- ;Please configure spi in dts [spi1] spi1_used = 1 spi1_cs_number = 1 spi1_cs_bitmap = 1 spi1_cs0 = port:PA12<6><0><3><default> spi1_sclk = port:PA13<6><0><3><default> spi1_mosi = port:PA18<6><0><3><default> spi1_miso = port:PA21<6><0><3><default> ;spi1_hold = port:PA19<6><0><2><default> ;spi1_wp = port:PA20<6><0><2><default>
Set PWM driver
The screen backlight uses a PWM driver, so you need to check the PWM driver and run mrtos_menuconfig
to enter the configuration page. Go to the following address to find PWM Devices
Drivers Options ---> soc related device drivers ---> PWM Devices ---> -*- enable pwm driver
Configure PWM pins
Open your favorite editor, modify the file: board/r128s2/module/configs/sys_config.fex
, and add the PWM1 node
[pwm1] pwm_used=1 pwm_positive = port:PA9<4><0><3><default>
Set up SPI LCD driver
SPI LCD is managed by a dedicated driver. Run mrtos_menuconfig
to enter the configuration page. Go to the following address to find SPILCD Devices
, and be sure to check spilcd hal APIs test
at the same time to facilitate testing.
Drivers Options ---> soc related device drivers ---> [*] DISP Driver Support(spi_lcd) [*] spilcd hal APIs test
Writing SPI LCD display driver
Get screen initialization sequence
First ask the screen manufacturer to provide driver source code
Find the LCD’s initialization sequence code
Find the source code of screen initialization
The organized initialization code is as follows:
LCD_WR_REG(0x11); // Sleep out delay_ms(120); // Delay 120ms //*********** Start Initial Sequence **********// LCD_WR_REG(0x36); LCD_WR_DATA8(0x00); LCD_WR_REG(0x3A); LCD_WR_DATA8(0x05); LCD_WR_REG(0xB2); LCD_WR_DATA8(0x1F); LCD_WR_DATA8(0x1F); LCD_WR_DATA8(0x00); LCD_WR_DATA8(0x33); LCD_WR_DATA8(0x33); LCD_WR_REG(0xB7); LCD_WR_DATA8(0x35); LCD_WR_REG(0xBB); LCD_WR_DATA8(0x20); // 2b LCD_WR_REG(0xC0); LCD_WR_DATA8(0x2C); LCD_WR_REG(0xC2); LCD_WR_DATA8(0x01); LCD_WR_REG(0xC3); LCD_WR_DATA8(0x01); LCD_WR_REG(0xC4); LCD_WR_DATA8(0x18); // VDV, 0x20:0v LCD_WR_REG(0xC6); LCD_WR_DATA8(0x13); // 0x13:60Hz LCD_WR_REG(0xD0); LCD_WR_DATA8(0xA4); LCD_WR_DATA8(0xA1); LCD_WR_REG(0xD6); LCD_WR_DATA8(0xA1); //After sleep in, the gate output is GND LCD_WR_REG(0xE0); LCD_WR_DATA8(0xF0); LCD_WR_DATA8(0x04); LCD_WR_DATA8(0x07); LCD_WR_DATA8(0x04); LCD_WR_DATA8(0x04); LCD_WR_DATA8(0x04); LCD_WR_DATA8(0x25); LCD_WR_DATA8(0x33); LCD_WR_DATA8(0x3C); LCD_WR_DATA8(0x36); LCD_WR_DATA8(0x14); LCD_WR_DATA8(0x12); LCD_WR_DATA8(0x29); LCD_WR_DATA8(0x30); LCD_WR_REG(0xE1); LCD_WR_DATA8(0xF0); LCD_WR_DATA8(0x02); LCD_WR_DATA8(0x04); LCD_WR_DATA8(0x05); LCD_WR_DATA8(0x05); LCD_WR_DATA8(0x21); LCD_WR_DATA8(0x25); LCD_WR_DATA8(0x32); LCD_WR_DATA8(0x3B); LCD_WR_DATA8(0x38); LCD_WR_DATA8(0x12); LCD_WR_DATA8(0x14); LCD_WR_DATA8(0x27); LCD_WR_DATA8(0x31); LCD_WR_REG(0xE4); LCD_WR_DATA8(0x1D); //Use 240 gates (N + 1)*8 LCD_WR_DATA8(0x00); //Set the gate starting position LCD_WR_DATA8(0x00); // When gate is not used up, bit4(TMG) is set to 0 LCD_WR_REG(0x21); LCD_WR_REG(0x29);
Rewrite the SPI LCD driver with a ready-made driver
Just choose a ready-made SPI LCD to modify. Here, select the nv3029s.c
driver to modify.
Copy these two drivers and rename them to st7789v.c
First edit st7789v.h
and change nv3029s
to st7789v
#ifndef _ST7789V_H #define _ST7789V_H #include "panels.h" struct __lcd_panel st7789v_panel; #endif /*End of file*/
Edit st7789v.c
and change nv3029s
to st7789v
Write initialization sequence
First delete the initialization function in static void LCD_panel_init(unsigned int sel)
.
Then copy the initialization sequence provided by the screen manufacturer
Then rewrite the driver interface according to the interface of the spi_lcd
framework. The specific interface is as follows
Screen factory function | SPILCD framework interface |
---|---|
LCD_WR_REG |
sunxi_lcd_cmd_write |
LCD_WR_DATA8 |
sunxi_lcd_para_write |
delay_ms |
sunxi_lcd_delay_ms |
Can be replaced directly
After completion, it is as follows
Then modify the address
function according to the driver provided by the screen manufacturer
Make the following changes
static void address(unsigned int sel, int x, int y, int width, int height) {<!-- --> sunxi_lcd_cmd_write(sel, 0x2B); /* Set row address */ sunxi_lcd_para_write(sel, (y >> 8) & amp; 0xff); sunxi_lcd_para_write(sel, y & amp; 0xff); sunxi_lcd_para_write(sel, (height >> 8) & amp; 0xff); sunxi_lcd_para_write(sel, height & amp; 0xff); sunxi_lcd_cmd_write(sel, 0x2A); /* Set coloum address */ sunxi_lcd_para_write(sel, (x >> 8) & amp; 0xff); sunxi_lcd_para_write(sel, x & amp; 0xff); sunxi_lcd_para_write(sel, (width >> 8) & amp; 0xff); sunxi_lcd_para_write(sel, width & amp; 0xff); sunxi_lcd_cmd_write(sel, 0x2c); }
Complete the driver as follows
#include "st7789v.h" static void LCD_power_on(u32 sel); static void LCD_power_off(u32 sel); static void LCD_bl_open(u32 sel); static void LCD_bl_close(u32 sel); static void LCD_panel_init(u32 sel); static void LCD_panel_exit(u32 sel); #define RESET(s, v) sunxi_lcd_gpio_set_value(s, 0, v) #define power_en(sel, val) sunxi_lcd_gpio_set_value(sel, 0, val) static struct disp_panel_para info[LCD_FB_MAX]; static void address(unsigned int sel, int x, int y, int width, int height) {<!-- --> sunxi_lcd_cmd_write(sel, 0x2B); /* Set row address */ sunxi_lcd_para_write(sel, (y >> 8) & amp; 0xff); sunxi_lcd_para_write(sel, y & amp; 0xff); sunxi_lcd_para_write(sel, (height >> 8) & amp; 0xff); sunxi_lcd_para_write(sel, height & amp; 0xff); sunxi_lcd_cmd_write(sel, 0x2A); /* Set coloum address */ sunxi_lcd_para_write(sel, (x >> 8) & amp; 0xff); sunxi_lcd_para_write(sel, x & amp; 0xff); sunxi_lcd_para_write(sel, (width >> 8) & amp; 0xff); sunxi_lcd_para_write(sel, width & amp; 0xff); sunxi_lcd_cmd_write(sel, 0x2c); } static void LCD_panel_init(unsigned int sel) { if (bsp_disp_get_panel_info(sel, & amp;info[sel])) { lcd_fb_wrn("get panel info fail!\\ "); return; } sunxi_lcd_cmd_write(sel, 0x11); // Sleep out sunxi_lcd_delay_ms(120); // Delay 120ms //*********** Start Initial Sequence **********// sunxi_lcd_cmd_write(sel, 0x36); sunxi_lcd_para_write(sel, 0x00); sunxi_lcd_cmd_write(sel, 0x3A); sunxi_lcd_para_write(sel, 0x05); sunxi_lcd_cmd_write(sel, 0xB2); sunxi_lcd_para_write(sel, 0x1F); sunxi_lcd_para_write(sel, 0x1F); sunxi_lcd_para_write(sel, 0x00); sunxi_lcd_para_write(sel, 0x33); sunxi_lcd_para_write(sel, 0x33); sunxi_lcd_cmd_write(sel, 0xB7); sunxi_lcd_para_write(sel, 0x35); sunxi_lcd_cmd_write(sel, 0xBB); sunxi_lcd_para_write(sel, 0x20); // 2b sunxi_lcd_cmd_write(sel, 0xC0); sunxi_lcd_para_write(sel, 0x2C); sunxi_lcd_cmd_write(sel, 0xC2); sunxi_lcd_para_write(sel, 0x01); sunxi_lcd_cmd_write(sel, 0xC3); sunxi_lcd_para_write(sel, 0x01); sunxi_lcd_cmd_write(sel, 0xC4); sunxi_lcd_para_write(sel, 0x18); // VDV, 0x20:0v sunxi_lcd_cmd_write(sel, 0xC6); sunxi_lcd_para_write(sel, 0x13); // 0x13:60Hz sunxi_lcd_cmd_write(sel, 0xD0); sunxi_lcd_para_write(sel, 0xA4); sunxi_lcd_para_write(sel, 0xA1); sunxi_lcd_cmd_write(sel, 0xD6); sunxi_lcd_para_write(sel, 0xA1); // After sleep in, the gate output is GND sunxi_lcd_cmd_write(sel, 0xE0); sunxi_lcd_para_write(sel, 0xF0); sunxi_lcd_para_write(sel, 0x04); sunxi_lcd_para_write(sel, 0x07); sunxi_lcd_para_write(sel, 0x04); sunxi_lcd_para_write(sel, 0x04); sunxi_lcd_para_write(sel, 0x04); sunxi_lcd_para_write(sel, 0x25); sunxi_lcd_para_write(sel, 0x33); sunxi_lcd_para_write(sel, 0x3C); sunxi_lcd_para_write(sel, 0x36); sunxi_lcd_para_write(sel, 0x14); sunxi_lcd_para_write(sel, 0x12); sunxi_lcd_para_write(sel, 0x29); sunxi_lcd_para_write(sel, 0x30); sunxi_lcd_cmd_write(sel, 0xE1); sunxi_lcd_para_write(sel, 0xF0); sunxi_lcd_para_write(sel, 0x02); sunxi_lcd_para_write(sel, 0x04); sunxi_lcd_para_write(sel, 0x05); sunxi_lcd_para_write(sel, 0x05); sunxi_lcd_para_write(sel, 0x21); sunxi_lcd_para_write(sel, 0x25); sunxi_lcd_para_write(sel, 0x32); sunxi_lcd_para_write(sel, 0x3B); sunxi_lcd_para_write(sel, 0x38); sunxi_lcd_para_write(sel, 0x12); sunxi_lcd_para_write(sel, 0x14); sunxi_lcd_para_write(sel, 0x27); sunxi_lcd_para_write(sel, 0x31); sunxi_lcd_cmd_write(sel, 0xE4); sunxi_lcd_para_write(sel, 0x1D); // Use 240 gates (N + 1)*8 sunxi_lcd_para_write(sel, 0x00); //Set the starting point of gate sunxi_lcd_para_write(sel, 0x00); // When gate is not used up, bit4(TMG) is set to 0 sunxi_lcd_cmd_write(sel, 0x21); sunxi_lcd_cmd_write(sel, 0x29); if (info[sel].lcd_x < info[sel].lcd_y) address(sel, 0, 0, info[sel].lcd_x - 1, info[sel].lcd_y - 1); else address(sel, 0, 0, info[sel].lcd_y - 1, info[sel].lcd_x - 1); } static void LCD_panel_exit(unsigned int sel) { sunxi_lcd_cmd_write(sel, 0x28); sunxi_lcd_delay_ms(20); sunxi_lcd_cmd_write(sel, 0x10); sunxi_lcd_delay_ms(20); sunxi_lcd_pin_cfg(sel, 0); } static s32 LCD_open_flow(u32 sel) { lcd_fb_here; /* open lcd power, and delay 50ms */ LCD_OPEN_FUNC(sel, LCD_power_on, 50); /* open lcd power, than delay 200ms */ LCD_OPEN_FUNC(sel, LCD_panel_init, 200); LCD_OPEN_FUNC(sel, lcd_fb_black_screen, 50); /* open lcd backlight, and delay 0ms */ LCD_OPEN_FUNC(sel, LCD_bl_open, 0); return 0; } static s32 LCD_close_flow(u32 sel) { lcd_fb_here; /* close lcd backlight, and delay 0ms */ LCD_CLOSE_FUNC(sel, LCD_bl_close, 50); /* open lcd power, than delay 200ms */ LCD_CLOSE_FUNC(sel, LCD_panel_exit, 10); /* close lcd power, and delay 500ms */ LCD_CLOSE_FUNC(sel, LCD_power_off, 10); return 0; } static void LCD_power_on(u32 sel) { /* config lcd_power pin to open lcd power0 */ lcd_fb_here; power_en(sel, 1); sunxi_lcd_power_enable(sel, 0); sunxi_lcd_pin_cfg(sel, 1); RESET(sel, 1); sunxi_lcd_delay_ms(100); RESET(sel, 0); sunxi_lcd_delay_ms(100); RESET(sel, 1); } static void LCD_power_off(u32 sel) { lcd_fb_here; /* config lcd_power pin to close lcd power0 */ sunxi_lcd_power_disable(sel, 0); power_en(sel, 0); } static void LCD_bl_open(u32 sel) { sunxi_lcd_pwm_enable(sel); /* config lcd_bl_en pin to open lcd backlight */ sunxi_lcd_backlight_enable(sel); lcd_fb_here; } static void LCD_bl_close(u32 sel) { /* config lcd_bl_en pin to close lcd backlight */ sunxi_lcd_backlight_disable(sel); sunxi_lcd_pwm_disable(sel); lcd_fb_here; } /* sel: 0:lcd0; 1:lcd1 */ static s32 LCD_user_defined_func(u32 sel, u32 para1, u32 para2, u32 para3) { lcd_fb_here; return 0; } static int lcd_set_var(unsigned int sel, struct fb_info *p_info) { return 0; } static int lcd_set_addr_win(unsigned int sel, int x, int y, int width, int height) { address(sel, x, y, width, height); return 0; } static int lcd_blank(unsigned int sel, unsigned int en) { return 0; } struct __lcd_panel st7789v_panel = { /* panel driver name, must mach the name of lcd_drv_name in sys_config.fex */ .name = "st7789v", .func = { .cfg_open_flow = LCD_open_flow, .cfg_close_flow = LCD_close_flow, .lcd_user_defined_func = LCD_user_defined_func, .blank = lcd_blank, .set_var = lcd_set_var, .set_addr_win = lcd_set_addr_win, }, };
Docking driver framework
After completing the writing of the screen driver, we need to connect it to the SPILCD driver framework. First edit Kconfig
Add configuration of st7789v
config LCD_SUPPORT_ST7789V bool "LCD support st7789v panel" default n ---help--- If you want to support st7789v panel for display driver, select it.
Then edit panels.c
and add a reference to the st7789
driver in panel_array
As shown below
#ifdef CONFIG_LCD_SUPPORT_ST7789V &st7789v_panel, #endif
Then edit panels.h
and add the reference as well
As shown below
#ifdef CONFIG_LCD_SUPPORT_ST7789V extern struct __lcd_panel st7789v_panel; #endif
Finally edit the outer Makefile
to add compilation options
As follows
obj-${<!-- -->CONFIG_LCD_SUPPORT_ST7789V} + = panels/st7789v.o
Select ST7789V driver
In the SPILCD driver selection interface, you can see LCD_FB panels select
to select the SPI screen driver.
Enter the LCD_FB panels select
option
Select and check [*] LCD support st7789v panel
Configure SPI LCD pins
Open your favorite editor and modify the file: board/r128s2/module/configs/sys_config.fex
[lcd_fb0] lcd_used = 1 lcd_model_name = "spilcd" lcd_driver_name = "st7789v" ; Screen specification configuration lcd_x = 240 lcd_y = 240 lcd_width = 32 lcd_height = 32 ;SPI rate lcd_data_speed = 50 ; PWM backlight configuration item lcd_pwm_used = 1 lcd_pwm_ch = 1 lcd_pwm_freq = 5000 lcd_pwm_pol = 0 lcd_backlight = 100 ; Configure lcd_if = 1 for SPI mode, double buffering lcd_if = 0 fb_buffer_num = 2 ; The following configuration is invalid in SPI mode lcd_pixel_fmt = 11 lcd_dbi_fmt = 2 lcd_dbi_clk_mode = 1 lcd_dbi_te = 1 lcd_dbi_if = 4 lcd_rgb_order = 0 lcd_fps = 60 ; Use SPI1 as communication interface lcd_spi_bus_num = 1 lcd_frm = 2 lcd_gamma_en = 1 lcd_power_num = 0 lcd_gpio_regu_num = 0 lcd_bl_percent_num = 0 lcd_spi_dc_pin = port:PA19<1><0><3><0> ;RESET Pin lcd_gpio_0 = port:PA20<1><0><2><0>
Compile and package
Run the command mp
to compile and package. You can see that st7789v.o
is compiled.
Test
After the burning starts, the screen backlight starts, but the screen is completely black.
Enter test_spilcd
and the screen will display yellow.
Enter lv_examples 1
to display the lvgl
interface
FAQ
LVGL appears DMA Over Size
This is because LVGL is configured with LV_COLOR_DEPTH
as 32, but the SPI screen is configured as 16 bits. Please modify lv_conf.h
Part of the screen appears
- Check if the
address
function is correct - Check whether the
sys_config.fex
screen configuration resolution is correct