cancel
Showing results for 
Search instead for 
Did you mean: 

How to configura touchgfx for stm32f407zgt6 with cubemx?

Lle.11
Associate II

Hello everyone!

I want to display GUI on stm32f407zgt6 with touchgfx. I find the most similar example is using touchgfx on stm32f429, but there is no ltdc or dma2d on stm32f407, so the project generated by using cubemx has not displayed the correct GUI after downloading to f407. How to configure touchgfx for stm32f407zgt6 with cubemx?

BR.

8 REPLIES 8
Martin KJELDSEN
Chief III

For TFT-controller-less MCUs you must go with "Custom Display interface" in the TouchGFX Generator.

This will allow you to write code in the generated user-editable-classes to transfer the framebuffer to your display when touchgfx requests you to. Please read https://support.touchgfx.com/docs/development/touchgfx-hal-development/scenarios/scenarios-fmc which i just wrote a week ago.

Get back to me if it's not enough.

/Martin

Thanks.

Where can I find the example project of f407?

There's no example project for F407. We don't make examples for every MCU available. Did you read the article? Give it a shot and i'll help you.

My MCU is stm32f407zgt6, lcd controller is NT35310.

LCD Driver

typedef struct
{
	u16 LCD_REG;
	u16 LCD_RAM;
} LCD_TypeDef;
			    
#define LCD_BASE        ((u32)(0x6C000000 | 0x0000007E))
#define TFTLCD             ((LCD_TypeDef *) LCD_BASE)
 
...
...
...
 
void LCD_Set_Window(u16 sx,u16 sy,u16 width,u16 height)
{    
	u8 hsareg,heareg,vsareg,veareg;
	u16 hsaval,heaval,vsaval,veaval; 
	u16 twidth,theight;
	twidth=sx+width-1;
	theight=sy+height-1;
	if(lcddev.id==0X5310)
	{
		LCD_WR_REG(lcddev.setxcmd); 
		LCD_WR_DATA(sx>>8); 
		LCD_WR_DATA(sx&0XFF);	 
		LCD_WR_DATA(twidth>>8); 
		LCD_WR_DATA(twidth&0XFF);  
		LCD_WR_REG(lcddev.setycmd); 
		LCD_WR_DATA(sy>>8); 
		LCD_WR_DATA(sy&0XFF); 
		LCD_WR_DATA(theight>>8); 
		LCD_WR_DATA(theight&0XFF); 
	}
}
 
void LCD_WriteRAM_Prepare(void)
{
 	TFTLCD->LCD_REG=lcddev.wramcmd;	  
}	 
 
void LCD_WriteRAM(u16 RGB_Code)
{							    
	TFTLCD->LCD_RAM = RGB_Code;
}
 
...
...
...

Transferring the framebuffer

void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
    // Calling parent implementation of flushFrameBuffer(const touchgfx::Rect& rect).
    //
    // To overwrite the generated implementation, omit call to parent function
    // and implemented needed functionality here.
    // Please note, HAL::flushFrameBuffer(const touchgfx::Rect& rect) must
    // be called to notify the touchgfx framework that flush has been performed.
 
	__IO uint16_t* ptr;
	HAL::lockFrameBuffer();
 
    //TouchGFXGeneratedHAL::flushFrameBuffer(rect);
	LCD_Set_Window(rect.x, rect.y, rect.width, rect.height);
	LCD_WriteRAM_Prepare();
 
	xSemaphoreTake(screen_frame_buffer_sem, portMAX_DELAY);
	for(int rowidx = 0;rowidx<rect.height;rowidx++)
	{
		ptr = getClientFrameBuffer() + rect.x + (rowidx + rect.y) * lcddev.width;
		for(int colidx=0;colidx<rect.width;colidx++)
		{
			LCD_WriteRAM(*(ptr + colidx));
		}
	}
 
	HAL::unlockFrameBuffer();
	xSemaphoreGive(screen_frame_buffer_sem);
}

The TFTLCD does not provide a teaming effect signal, so I don't know how to unblock the TouchGFX Engine Main loop to render the next frame.

When I download to MCU, System is halt on MX_TouchGFX_Process(), flushFrameBuffer() is never called.

Nice article. I'm really interested in that DMA transfer thing, because I'm currently using mcu from the f412/f413 family that does not have ChromART. What I'm not sure is the fact that if a library wants to updade a small rectangle in the middle of a screen. This means that you move cursor to that point and transfer all screen lines that correspond to rectangle height?

How to create a DMA to FMC? Currently I am using an 8-bit LCD with 8080 interface, which is driven by FMC.

Is there a code snipplet for the following already implemented?

Thanks for your help!

Matej

That's correct. flushFrameBuffer() will tell you the dimensions of the exact Rect that was updated, and you move your "cursor" to that starting point on the LCD before transferring. You should be able to use the guidelines from the article - It also applies to F412 + SPI for instance.

Could you check the Cube firmware packs for examples? I'm pretty sure there's a DMA to FMC in there somewhere.

/Martin

If you don't have a signal available from the LCD, then you need something like a Hardware timer.

I'm discussing this with the CubeMX Team so that we'll be able to expose timer configurations to TouchGFX Generator so that we may generate, automatically, a hw timer interrupt to drive TouchGFX applications

/Martin

HI Martin,

I was able to implement the DMA transfer and it works. What I found out is that, when you tell the LCD controller that a smaller rectangle is to be drawn it expects the bytes to come in the rectangle's order and not the buffer-wise manner. Thus I have prepared three possible implementations, one is normal CPU copy, the other uses only DMA, but in case of animations that are using smaller rectangles I'm updating the whole screen lines (as I don't have a 2D DMA on this MCU), and the third one uses a combination (if there is a whole line use DMA if not then use the CPU copy). What I would like to know if there is a way to banchmark the following three scenarios just to be sure what is better. I mean you can visually see the difference if the whole screen is updated using DMA or CPU copy, but where is a trade-off between DMA copy rectange screen lines : CPU copy only a smaller rectangle with a certain size? I am enclosing my implemented snippet of code if you can have a look at it if there is still room for improvements.

Meanwhile I will start on the TE Pin implementation, which is another thing that I have never implemented with TouchGFX lib. Btw impressive work on the library!!!

void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
  // Calling parent implementation of flushFrameBuffer(const touchgfx::Rect& rect).
  HAL::flushFrameBuffer();
 
  // Lock frame buffer
  uint16_t* fb = HAL::lockFrameBuffer();
 
  // Try to take semaphore - Always free here
  xSemaphoreTake(screenFrameBufferSem, portMAX_DELAY);
 
  /* Set Cursor */
#if (HAL_DMA_TO_LCD_TYPE == DMA_TO_LCD_ONLY)
  __ST7789H2_SetDisplayWindow(0, rect.y, ST7789H2_LCD_PIXEL_WIDTH, rect.height);
#else
  __ST7789H2_SetDisplayWindow(rect.x, rect.y, rect.width, rect.height);
#endif
 
  // Prepare the LCD RAM controller
  LCD_RAM_PREPARE();
 
#if (HAL_DMA_TO_LCD_TYPE == DMA_TO_LCD_ONLY)
  // Make a DMA transfer
  if (transferFrameWithDma(rect, fb))
  {
    LOG_ERROR(log_gfx, "DMA transfer error!");
  }
#elif (HAL_DMA_TO_LCD_TYPE == DMA_TO_LCD_MIXED)
  // If we are transferring screen wide pictures then use the DMA
  if ((0 == rect.x) && (ST7789H2_LCD_PIXEL_WIDTH == rect.width))
  {
    // In this case a DMA transfer can be used
    if (transferFrameWithDma(rect, fb))
    {
      LOG_ERROR(log_gfx, "DMA transfer error!");
    }
  }
  else
  {
    // Transfer with for loop
    transferFrameWithCpu(rect, fb);
  }
#else
  // Transfer with for loop
  transferFrameWithCpu(rect, fb);
#endif
  // Unlock TouchGFX framebuffer and give semaphore back - Flushframebuffer() is now a part of RENDER_TIME.
  HAL::unlockFrameBuffer();
  xSemaphoreGive(screenFrameBufferSem);
}

Matej