ST7789VI and STM32

Description

I have a display connected to an STM32H7 using the parallel port (SPI I haven’t tried yet).
The display in question is: 2.4 inch IPS TFT LCD without Touchscreen (newhavendisplay.com)
Using function to only fill the buffer with specific colors works fine.
When using LVGL it doesn’t work properly. I have managed to get it to show something logical as shown below, but it is still not functional.
Partial rendering is not working, it just spits out random data, but the spinner can be seen to change something.
I got away with just taking the whole buffer and sending it to the display, but it is not fully working as shown in the image.

What MCU/Processor/Board and compiler are you using?

STM32H745 with STM32CubeIDE.

What LVGL version are you using?

Master - Latest

What do you want to achieve?

To display correctly.

What have you tried so far?

Code to reproduce

init stuff

#define DISPLAY_WIDTH 320
#define DISPLAY_HEIGHT 240
#define BYTE_PER_PIXEL (LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565A8)) /*will be 2 for RGB565 */
#define BUFF_SIZE (DISPLAY_WIDTH * DISPLAY_HEIGHT * BYTE_PER_PIXEL)
static uint8_t buf_1[BUFF_SIZE];
static uint8_t buf_2[BUFF_SIZE];

my_flush_cb

void my_flush_cb(lv_display_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
		comm_out(0x2C);
		for (int i = 0; i < 38400; i++) {
			uint16_t color_full = (color_p->red << 11) | (color_p->green << 5) | (color_p->blue);
		data_out(color_full >> 8);
		data_out(color_full & 0xff);
		data_out(color_full >> 8);
		data_out(color_full & 0xff);
		 color_p++;
		}

  /* IMPORTANT!!!
  * Inform the graphics library that you are ready with the flushing*/
  lv_display_flush_ready(disp);
}

Init Function

void Display_parallel_init(){



	   /* Initialize LVGL */
		 lv_init();

		 data_out(0x00);
		 	   HAL_GPIO_WritePin(RD_GPIO_Port, RD_Pin, HIGH);
		 	   HAL_GPIO_WritePin(WR_GPIO_Port, WR_Pin, LOW);
		 	   HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, LOW);
		 	   HAL_Delay(250);
		 	   HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, HIGH);
		 	   HAL_Delay(250);


		 	   comm_out(0x28);  //display off
		 	   comm_out(0x11);  //exit SLEEP mode
		 	   HAL_Delay(100);

		 	   comm_out(0x36);  //MADCTL: memory data access control
		 	   data_out(0x88);

		 	   comm_out(0x3A);  //COLMOD: Interface Pixel format  *** 65K-colors in 16bit/pixel (5-6-5) format when using 16-bit interface to allow 1-byte per pixel
		 	   data_out(0x55);

		 	   comm_out(0xB2);  //PORCTRK: Porch setting
		 	   data_out(0x0C);
		 	   data_out(0x0C);
		 	   data_out(0x00);
		 	   data_out(0x33);
		 	   data_out(0x33);

		 	   comm_out(0xB7);  //GCTRL: Gate Control
		 	   data_out(0x35);

		 	   comm_out(0xBB);  //VCOMS: VCOM setting
		 	   data_out(0x2B);

		 	   comm_out(0xC0);  //LCMCTRL: LCM Control
		 	   data_out(0x2C);

		 	   comm_out(0xC2);  //VDVVRHEN: VDV and VRH Command Enable
		 	   data_out(0x01);
		 	   data_out(0xFF);

		 	   comm_out(0xC3);  //VRHS: VRH Set
		 	   data_out(0x11);

		 	   comm_out(0xC4);  //VDVS: VDV Set
		 	   data_out(0x20);

		 	   comm_out(0xC6);  //FRCTRL2: Frame Rate control in normal mode
		 	   data_out(0x0F);

		 	   comm_out(0xD0);  //PWCTRL1: Power Control 1
		 	   data_out(0xA4);
		 	   data_out(0xA1);

		 	   comm_out(0xE0);  //PVGAMCTRL: Positive Voltage Gamma control
		 	   data_out(0xD0);
		 	   data_out(0x00);
		 	   data_out(0x05);
		 	   data_out(0x0E);
		 	   data_out(0x15);
		 	   data_out(0x0D);
		 	   data_out(0x37);
		 	   data_out(0x43);
		 	   data_out(0x47);
		 	   data_out(0x09);
		 	   data_out(0x15);
		 	   data_out(0x12);
		 	   data_out(0x16);
		 	   data_out(0x19);

		 	   comm_out(0xE1);  //NVGAMCTRL: Negative Voltage Gamma control
		 	   data_out(0xD0);
		 	   data_out(0x00);
		 	   data_out(0x05);
		 	   data_out(0x0D);
		 	   data_out(0x0C);
		 	   data_out(0x06);
		 	   data_out(0x2D);
		 	   data_out(0x44);
		 	   data_out(0x40);
		 	   data_out(0x0E);
		 	   data_out(0x1C);
		 	   data_out(0x18);
		 	   data_out(0x16);
		 	   data_out(0x19);

		 	   comm_out(0x2A);  //X address set
		 	   data_out(0x00);
		 	   data_out(0x00);
		 	   data_out(0x00);
		 	   data_out(0xEF);

		 	   comm_out(0x2B);  //Y address set
		 	   data_out(0x00);
		 	   data_out(0x00);
		 	   data_out(0x01);
		 	   data_out(0x3F);
		 	   HAL_Delay(10);

		 	   comm_out(0x21);  //Display Inversion for 2.8" TFT
		 	   comm_out(0x29);  //display ON
		 	   HAL_Delay(10);

		 //	   disp1();
		 //	   HAL_Delay(100);
		 	   disp2();
		 //	   HAL_Delay(100);
		 //	   Border_Fill();
		 //	   HAL_Delay(100);

		 	   disp3();

//		 	  pixel(320/2,240/2, 0xFFFF);

		 	 lv_display_t * disp = lv_display_create(DISPLAY_HEIGHT,DISPLAY_WIDTH); /*Basic initialization with horizontal and vertical resolution in pixels*/
		 	 lv_display_set_flush_cb(disp, my_flush_cb); /*Set a flush callback to draw to the display*/
		 	 lv_display_set_buffers(disp, buf_1, buf_2, sizeof(buf_1), LV_DISPLAY_RENDER_MODE_DIRECT); /*Set an initialized buffer*/
//		 	 lv_display_rotate_area(disp, LV_DISPLAY_ROTATION_90);
		 	lv_obj_set_style_bg_color(lv_screen_active(), lv_color_hex(0x003a57), LV_PART_MAIN);

		 	lv_obj_t * spinner = lv_spinner_create(lv_screen_active());
		 	    lv_obj_set_size(spinner, 100, 100);
		 	    lv_obj_center(spinner);
		 	    lv_spinner_set_anim_params(spinner, 10000, 200);
		 for(;;) {
				 /* The task running lv_timer_handler should have lower priority than that running `lv_tick_inc` */
				 lv_timer_handler();
				 /* raise the task priority of LVGL and/or reduce the handler period can improve the performance */
				 HAL_Delay(5);
		 }

}

Other functions to control the display


void comm_out(unsigned char c)
{
  HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, LOW);
  for(uint8_t i=0; i<8; i++){
	  HAL_GPIO_WritePin(GPIOB, pinz[i], ((c) >> (i)) & 0x01);
  }
  HAL_GPIO_WritePin(WR_GPIO_Port, WR_Pin, LOW);
  HAL_GPIO_WritePin(WR_GPIO_Port, WR_Pin, HIGH);
}

void data_out(unsigned char d)
{
  HAL_GPIO_WritePin(RS_GPIO_Port, RS_Pin, HIGH);
  for(uint8_t i=0; i<8; i++){
	  HAL_GPIO_WritePin(GPIOB, pinz[i], ((d) >> (i)) & 0x01);
  }
  HAL_GPIO_WritePin(WR_GPIO_Port, WR_Pin, LOW);
  HAL_GPIO_WritePin(WR_GPIO_Port, WR_Pin, HIGH);
}

Screenshot and/or video

image

Hmm why your flushcb send one pixel twice ? Too convert color_full is waste mcu.
But primary flush is only area… * render_mode

  • LV_DISPLAY_RENDER_MODE_PARTIAL Use the buffer(s) to render the screen in smaller parts. This way the buffers can be smaller then the display to save RAM. At least 1/10 screen size buffer(s) are recommended. In flush_cb the rendered images needs to be copied to the given area of the display. In this mode if a button is pressed only the button’s area will be redrawn.
  • LV_DISPLAY_RENDER_MODE_DIRECT The buffer(s) has to be screen sized and LVGL will render into the correct location of the buffer. This way the buffer always contain the whole image. If two buffer are used the rendered areas are automatically copied to the other buffer after flushing. Due to this in flush_cb typically only a frame buffer address needs to be changed. If a button is pressed only the button’s area will be redrawn.
  • LV_DISPLAY_RENDER_MODE_FULL The buffer(s) has to be screen sized and LVGL will always redraw the whole screen even if only 1 pixel has been changed. If two screen sized draw buffers are provided, LVGL’s display handling works like “traditional” double buffering. This means the flush_cb callback only has to update the address of the frame buffer to the px_map parameter.

My bad, yes that is one of the problems.

I have been doing some testing, but I am not getting a full clear image still.

My modifications are the following:

#define DISPLAY_WIDTH 320
#define DISPLAY_HEIGHT 240
#define BYTE_PER_PIXEL (LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565A8)) /*will be 2 for RGB565 */
#define BUFF_SIZE (DISPLAY_WIDTH * DISPLAY_HEIGHT * BYTE_PER_PIXEL)
static uint8_t buf_1[BUFF_SIZE];
static uint8_t buf_2[BUFF_SIZE];
void my_flush_cb(lv_display_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
	window_set(area->x1, area->x2, area->y1, area->y2);

	int height = area->y2 - area->y1 + 1;
	  int width = area->x2 - area->x1 + 1;

		comm_out(0x2C);
		for (int i = 0; i < height*width; i++) {
			uint16_t color_full = (color_p->red << 11) | (color_p->green << 5) | (color_p->blue);
		data_out(color_full >> 8);
		data_out(color_full & 0xff);
		 color_p++;
		}

  /* IMPORTANT!!!
  * Inform the graphics library that you are ready with the flushing*/
  lv_display_flush_ready(disp);
}
void Display_parallel_init(){



	   /* Initialize LVGL */
		 lv_init();

		 data_out(0x00);
		 	   HAL_GPIO_WritePin(RD_GPIO_Port, RD_Pin, HIGH);
		 	   HAL_GPIO_WritePin(WR_GPIO_Port, WR_Pin, LOW);
		 	   HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, LOW);
		 	   HAL_Delay(250);
		 	   HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, HIGH);
		 	   HAL_Delay(250);


		 	   comm_out(0x28);  //display off
		 	   comm_out(0x11);  //exit SLEEP mode
		 	   HAL_Delay(100);

		 	   comm_out(0x36);  //MADCTL: memory data access control
		 	   data_out(0x88);

		 	   comm_out(0x3A);  //COLMOD: Interface Pixel format  *** 65K-colors in 16bit/pixel (5-6-5) format when using 16-bit interface to allow 1-byte per pixel
		 	   data_out(0x55);

		 	   comm_out(0xB2);  //PORCTRK: Porch setting
		 	   data_out(0x0C);
		 	   data_out(0x0C);
		 	   data_out(0x00);
		 	   data_out(0x33);
		 	   data_out(0x33);

		 	   comm_out(0xB7);  //GCTRL: Gate Control
		 	   data_out(0x35);

		 	   comm_out(0xBB);  //VCOMS: VCOM setting
		 	   data_out(0x2B);

		 	   comm_out(0xC0);  //LCMCTRL: LCM Control
		 	   data_out(0x2C);

		 	   comm_out(0xC2);  //VDVVRHEN: VDV and VRH Command Enable
		 	   data_out(0x01);
		 	   data_out(0xFF);

		 	   comm_out(0xC3);  //VRHS: VRH Set
		 	   data_out(0x11);

		 	   comm_out(0xC4);  //VDVS: VDV Set
		 	   data_out(0x20);

		 	   comm_out(0xC6);  //FRCTRL2: Frame Rate control in normal mode
		 	   data_out(0x0F);

		 	   comm_out(0xD0);  //PWCTRL1: Power Control 1
		 	   data_out(0xA4);
		 	   data_out(0xA1);

		 	   comm_out(0xE0);  //PVGAMCTRL: Positive Voltage Gamma control
		 	   data_out(0xD0);
		 	   data_out(0x00);
		 	   data_out(0x05);
		 	   data_out(0x0E);
		 	   data_out(0x15);
		 	   data_out(0x0D);
		 	   data_out(0x37);
		 	   data_out(0x43);
		 	   data_out(0x47);
		 	   data_out(0x09);
		 	   data_out(0x15);
		 	   data_out(0x12);
		 	   data_out(0x16);
		 	   data_out(0x19);

		 	   comm_out(0xE1);  //NVGAMCTRL: Negative Voltage Gamma control
		 	   data_out(0xD0);
		 	   data_out(0x00);
		 	   data_out(0x05);
		 	   data_out(0x0D);
		 	   data_out(0x0C);
		 	   data_out(0x06);
		 	   data_out(0x2D);
		 	   data_out(0x44);
		 	   data_out(0x40);
		 	   data_out(0x0E);
		 	   data_out(0x1C);
		 	   data_out(0x18);
		 	   data_out(0x16);
		 	   data_out(0x19);

		 	   comm_out(0x2A);  //X address set
		 	   data_out(0x00);
		 	   data_out(0x00);
		 	   data_out(0x00);
		 	   data_out(0xEF);

		 	   comm_out(0x2B);  //Y address set
		 	   data_out(0x00);
		 	   data_out(0x00);
		 	   data_out(0x01);
		 	   data_out(0x3F);
		 	   HAL_Delay(10);

		 	   comm_out(0x21);  //Display Inversion for 2.8" TFT
		 	   comm_out(0x29);  //display ON
		 	   HAL_Delay(10);

		 //	   disp1();
		 //	   HAL_Delay(100);
//		 	   disp2();
		 //	   HAL_Delay(100);
		 //	   Border_Fill();
		 //	   HAL_Delay(100);

		 	   disp3();

//		 	  pixel(320/2,240/2, 0xFFFF);

		 	 lv_display_t * disp = lv_display_create(DISPLAY_HEIGHT,DISPLAY_WIDTH); /*Basic initialization with horizontal and vertical resolution in pixels*/
		 	 lv_display_set_flush_cb(disp, my_flush_cb); /*Set a flush callback to draw to the display*/
		 	 lv_display_set_buffers(disp, buf_1, buf_2, sizeof(buf_1), LV_DISPLAY_RENDER_MODE_PARTIAL); /*Set an initialized buffer*/
//		 	 lv_display_rotate_area(disp, LV_DISPLAY_ROTATION_90);
		 	lv_obj_set_style_bg_color(lv_screen_active(), lv_color_hex(0x003a57), LV_PART_MAIN);

		 	lv_obj_t * spinner = lv_spinner_create(lv_screen_active());
		 	    lv_obj_set_size(spinner, 100, 100);
		 	    lv_obj_center(spinner);
		 	    lv_spinner_set_anim_params(spinner, 10000, 200);
		 for(;;) {
				 /* The task running lv_timer_handler should have lower priority than that running `lv_tick_inc` */
				 lv_timer_handler();
				 /* raise the task priority of LVGL and/or reduce the handler period can improve the performance */
				 HAL_Delay(5);
		 }

}

What I am seeing on the display is the following: