Frame sync with wait_cb()

This post is related to wait_cb and frame sync issue at Performance ideas

I am trying to use wait_cb to synchronize data update with a FR pin that is similar to the TE pin on most small-middle size TFTs. On every frame there is a high pulse of FR signal available from my OLED (64*128 monochrome). Users may poll the pin for a 0->1 edge to update the LCD to avoid tearing effect. Traces captured by a DSO below show the FR pulse coupling with the SPI clock. Without any synchronization measure, screen update is performed in a random manner that leads to tearing effect.

To make sure data write sync with FR 0->1 edge, a wait_cb() function is developed with snippet below:

//local variable for FR event
static bool FR_Event_Flag=false;

//IRQ function
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if (GPIO_Pin==OLED_FR_Pin)
	{
		FR_Event_Flag=true;
	}
}

void my_wait_cb(lv_disp_drv_t * disp_drv)
{
	//wait until a valid FR low->high transition
	 HAL_NVIC_EnableIRQ(OLED_FR_EXTI_IRQn); //enable EXTI interrupt
	 while(FR_Event_Flag==false) //infinite loop to wait for FR event flag==true
	  ;

	 HAL_NVIC_DisableIRQ(OLED_FR_EXTI_IRQn); //disable EXTI for next frame update
	 FR_Event_Flag=false; //reset the FR event flag
}

Registration of wait_cb() is :

void my_hal_init(void)
{
	my_init();

	static lv_disp_buf_t lv_disp_buf;
	lv_disp_drv_t disp_drv;

	/* Flushing freq for the full frame is a variable of the buffer size of lv_buf_array[]
	 * */
	lv_disp_buf_init(&lv_disp_buf, lv_buf_array, NULL, LV_HOR_RES_MAX*LV_VER_RES_MAX);

	disp_drv.rotated = (uint32_t)lv_disp_get_orientation();
	lv_disp_drv_init(&disp_drv);
	disp_drv.buffer = &lv_disp_buf;
	disp_drv.flush_cb = my_flush_cb;
	disp_drv.wait_cb = my_wait_cb;
	disp_drv.set_px_cb = my_set_px_cb;

	lv_disp_drv_register(&disp_drv);
//...
}

Issue: wait_cb() is never called. I set breakpoints at any of the lines in wait_cb() but the program is not halted. At the end there is still tearing effect. SPI data write is not synchronized with FR edge.

Ideally, on every FR pulse SPI data write should be tightly synchronized like what is shown in waveform below. I got the trace with an non-LVGL demo, synchronizing data update with each FR rising edge.

Any idea why wait_cb() is never called?

Hi,

wait_cb is not called because LVGL has nothing to wait for. If you don’t use GPU, and flush the display buffer without DMA (blocking send in my_flush_cb) there are not wait cycles from LVGL’s point of view.

My idea is to not flush to the display directly in my_flush_cb but to a temporary buffer and send this temporarily buffer on FR signal.

You can call lv_disp_flush_ready when the temp. buffer is sent be sure no new data will be rendered meanwhile.

1 Like

@kisvegabor

My idea is to not flush to the display directly in my_flush_cb but to a temporary buffer and send this temporarily buffer on FR signal.

It works, thank you. But I am using the same buffer lv_buf_array[] that LVGL uses to draw instead of a temporary buffer. Pseudocode with comments now looks like:

lv_color_t lv_buf_array[OLED_VER_RES*(OLED_HOR_RES>>3)]; //divide by 8 because it is a monochrome display
static lv_disp_drv_t disp_drv;
static bool flush_pending_flag=false; //use it in EXTI callback for flush pending call

void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    //fill lv_buf_array[]
    FrameBuffer_FillArea(area->x1,area->y1,area->x2,area->y2,color); 
    Flush_Wait2_Finish(); //wait until there is no pending SPI DMA transfer
    flush_pending_flag=true;
}

//IRQ callback, it is STM32 specific
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
   if(GPIO_Pin==OLED_FR_Pin)
   {
     if(flush_pending_flag==true)
     { 
       //Copying the full frame from lv_buf_array[] to OLED via SPI DMA 
       //Full frame because the time taken is shorter than a FR pulse interval anyway
       FrameBuffer_Copy2OLED();
     }
   }
}

//There is a callback from STM32 library to signify SPI transfer complete
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
   flush_pending_flag = false;
   lv_disp_flush_ready(&disp_drv);
}

Verification using a DSO shows on every touch event, SPI data transfer takes place at the rising edge of FR depicted below:

1 Like