After about 2-3 weeks of attempts, I finally got my F429 Discovery board to work with my BuyDisplay bare ILI9488 with capacitive touch!
I’m really happy with performance but now in order to upload my own project I need to rotate the display by 90 degrees.
Looking throught the ILI9488 datasheet I didn’t see anything about display or image rotation, so I was wondering if this can be done in the LVGL display driver?
I have seen some examples of swapping x and y but was hoping someone could suggest how to do it with the codebase I have.
What MCU/Processor/Board and compiler are you using?
STM32F429
What LVGL version are you using?
v7.6 +
What do you want to achieve?
Rotatae the display by 90 degrees into landscape mode
You should be able to rotate the screen using the MADCTL register of the driver (if it’s similar to my display writing 0x28 or 0xE8 to MADCTL should do it), and either set the rotated field on the lv_disp_drv_t struct, or swapping the values of LV_HOR_RES_MAX and LV_VER_RES_MAX on your lv_config.h file.
I also tried to flip the column address and page address values to match the landscape mode but that didn’t seem to affect anything.
Any other things I should try?
Is there a way to fill the screen with some colors via LVGL or directly from the LTDC driver? I can use that to test the source of the issue.
Yes, I did change both, but get the same result.
Is it perhaps related to how DMA2D works with the external SDRAM?
I might try compiling and running it with #define LV_USE_GPU_STM32_DMA2D 0 and see if it persists
0x48 and 0x88 work well in portrait mode.
But with 0x28 and 0xe8 it outputs as demonstrated in the 2nd image.
I always get that blank space on the right side of the screen (not physically) plus a triangle in the upper left side of the screen when I have the register set to 0x28 or 0xE8
EDIT: I don’t know if this is related, but my pixel clock is set to 12.5MHz but it should actually be set to 9.5MHz. I will reconfigure the pixel clock when I get home and test it once again
If yes, then No, I am not updating them.
I did try swap the values between to the horizontal and vertical config which did clear up the image a bit, but still left a blank box on the right side of the display and introduces a tearing effect.
I’m not too knowledgeable on how RGB interface works, so I don’t know if it requires updating or not when changing the display orientation.
So after playing around with many of the registers, It all came down to clocking.
If I use the internal clock, it works well in landscape mode but then I get some tearing affect.
If I use the LTDCs dot clock then it kills a 1/3rd of the screen - so the issue is that the dot clock is sending out a command for 320 pixels and not 480 for some reason - which is the next thing I have to find out as to why that is happening (If I understand this right?)
EDIT:
Using the LCDs internal clock but with some tearing
No worries!
The MADCTL register does control orientation, so it was the right suggestions.
But the issue I am facing is now related to the DOTCLK from the CPU.
@kisvegabor have you encountered such an issue before?
So after much searching, searching and searching, I have come to the conclution that the only way to get this to work in portrait mode is to flip the image that LVGL is generating.
I found this thread that has an explanation on how to do it, but since my drivers uses DMA, im not sure how to properly swap over X and Y
@embeddedt@kisvegabor can you perhaps provide an example of how to do this on the F429 driver?
static void tft_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p)
{
/*Return if the area is out the screen*/
if(area->x2 < 0) return;
if(area->y2 < 0) return;
if(area->x1 > TFT_HOR_RES - 1) return;
if(area->y1 > TFT_VER_RES - 1) return;
/*Truncate the area to the screen*/
int32_t act_x1 = area->x1 < 0 ? 0 : area->x1;
int32_t act_y1 = area->y1 < 0 ? 0 : area->y1;
int32_t act_x2 = area->x2 > TFT_HOR_RES - 1 ? TFT_HOR_RES - 1 : area->x2;
int32_t act_y2 = area->y2 > TFT_VER_RES - 1 ? TFT_VER_RES - 1 : area->y2;
x1_flush = act_x1;
y1_flush = act_y1;
x2_flush = act_x2;
y2_fill = act_y2;
y_fill_act = act_y1;
buf_to_flush = color_p;
/*##-7- Start the DMA transfer using the interrupt mode #*/
/* Configure the source, destination and buffer size DMA fields and Start DMA Stream transfer */
/* Enable All the DMA interrupts */
HAL_StatusTypeDef err;
err = HAL_DMA_Start_IT(&DmaHandle,(uint32_t)buf_to_flush, (uint32_t)&my_fb[y_fill_act * TFT_HOR_RES + x1_flush],
(x2_flush - x1_flush + 1));
if(err != HAL_OK)
{
while(1); /*Halt on error*/
}
}
and
static void DMA_TransferComplete(DMA_HandleTypeDef *han)
{
y_fill_act ++;
if(y_fill_act > y2_fill) {
lv_disp_flush_ready(&disp_drv);
} else {
buf_to_flush += x2_flush - x1_flush + 1;
/*##-7- Start the DMA transfer using the interrupt mode ####################*/
/* Configure the source, destination and buffer size DMA fields and Start DMA Stream transfer */
/* Enable All the DMA interrupts */
if(HAL_DMA_Start_IT(han,(uint32_t)buf_to_flush, (uint32_t)&my_fb[y_fill_act * TFT_HOR_RES + x1_flush],
(x2_flush - x1_flush + 1)) != HAL_OK)
{
while(1); /*Halt on error*/
}
}
}
Unfortunately as far as I know, you can’t use the DMA for rotation.
You have to do it within two loops.
A small example of a 90 degrees rotation.
And this function is in no way optimized!
void monitor_flush (lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p)
{
int32_t y;
int32_t x;
int32_t ys;
int32_t xs;
uint32_t w;
uint32_t ulSource;
uint32_t ulDest;
w = lv_area_get_width (area);
for (x = area->x1, xs = 0; x <= area->x2; x++, xs++) { // Copy the area data rotated into frame buffer 1.
for (y = area->y1, ys = 0; y <= area->y2; y++, ys++) {
ulSource = (ULONG) color_p + ((xs + (ys * w)) * sizeof (lv_color_t));
ulDest = (ULONG) 0xd0000000 + ((y + ((disp_drv->hor_res - 1 - x) * disp_drv->ver_res)) * sizeof (lv_color_t));
if (sizeof (lv_color_t) == 4) {
*((uint32_t*) ulDest) = *((uint32_t*) ulSource);
}
else if (sizeof (lv_color_t) == 2) {
*((uint16_t*) ulDest) = *((uint16_t*) ulSource);
}
else {
*((uint8_t*) ulDest) = *((uint8_t*) ulSource);
}
}
}
lv_disp_flush_ready (disp_drv); // IMPORTANT! It must be called to tell the system the flush is ready
}
Would it be possible to rotate the image with the two for loops inside tft_flush before DMA starts to transfer it?
Basically basically resaving area with the new image orientation?
I’m still trying to wrap my head around the whole buffer/dma transfer functions and how they work