2026-01-20 2:09 AM - last edited on 2026-01-23 3:26 AM by mƎALLEm
Hello everyone,
I'm seeking advice on video playback implementation using TouchGFX 4.25 with a Riverdi RVT121HVSFWN00 display (12.1" LCD with STM32H7 MCU).
**Current working setup:**
- TouchGFX version: 4.25
- Display: Riverdi RVT121HVSFWN00
- Current video configuration: 480x272 resolution
- FFmpeg command that works:
ffmpeg -i video2.mp4 -s 480x272 -vcodec mjpeg -q:v 1 -pix_fmt yuv420p -color_range 1 -strict -1 -an output.avi
This works perfectly with the Video widget alongside other UI elements (backgrounds, buttons, etc.)
When I try to increase the video resolution to 800x800 pixels, I only get a black screen. No video is displayed.
**What I've tried:**
1. Modified FFmpeg command for 800x800:
ffmpeg -i input.mp4 -vf "scale=800:800:force_original_aspect_ratio=decrease,pad=800:800:(ow-iw)/2:(oh-ih)/2" \
-c:v libxvid -qscale:v 5 -c:a libmp3lame -b:a 128k output.avi
```
2. Also tried various combinations with `mjpeg` codec and different pixel formats
**My questions:**
1. Are there hardware limitations with the STM32H7 (likely in the Riverdi display) that prevent 800x800 video playback?
2. Do I need additional configuration in TouchGFX for higher resolution videos?
3. Could this be a memory issue (SDRAM configuration maybe)?
4. Are there specific codec/pixel format requirements for larger videos in TouchGFX?
**Additional context:**
- The display has a native resolution of 1280x800
- I'm using the Video widget within a screen containing other graphical elements
- The 480x272 video works flawlessly, suggesting the basic setup is correct
**What I suspect:**
- Possible memory constraints (800x800 RGB565 = ~1.25MB per frame)
- Maybe SDRAM isn't properly configured for larger video buffers
- Could be a decoding performance issue with higher resolution
Has anyone successfully implemented 800x800 or similar high-resolution video playback on STM32H7 with TouchGFX? Any guidance on:
- Optimal FFmpeg parameters for larger videos
- TouchGFX configuration adjustments
- Memory management strategies
- Hardware limitations to consider
Any help or insights would be greatly appreciated!
Thank you in advance.
Solved! Go to Solution.
2026-02-16 1:30 AM
Hello everyone,
After some experimentation, I've successfully managed to play a video in full screen on a Riverdi 12-inch display!
Here's what I did:
However, I encountered an issue when trying the "Double framebuffer" strategy. I set the width to 1280 px and height to 800 px, but the result was the same frame image being repeated and overlaid twice on the screen.
I'm curious and would like to understand how to properly set up the MPU and the linker script (.ld file) to resolve this, especially given the following linker script configuration:
/*********************************************************************************** File : LinkerScript.ld**** Author : STM32CubeIDE**** Abstract : Linker script for STM32H7 series** 1024Kbytes FLASH** 800Kbytes RAM**** Set heap size, stack size and stack location according** to application requirements.**** Set memory bank area and size if external memory is used.**** Target : STMicroelectronics STM32**** Distribution: The file is distributed as is without any warranty** of any kind.********************************************************************************* @attention**** Copyright (c) 2023 STMicroelectronics.** All rights reserved.**** This software is licensed under terms that can be found in the LICENSE file** in the root directory of this software component.** If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************//* Entry Point */ENTRY(Reset_Handler)/* Highest address of the user mode stack */_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1); /* end of "RAM_D1" Ram type memory */_Min_Heap_Size = 0xc000; /* required amount of heap */_Min_Stack_Size = 0xc800; /* required amount of stack *//* Memories definition */MEMORY{ RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* Memory is divided. Actual start is 0x08000000 and actual length is 2048K */ DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K QUADSPI (r) : ORIGIN = 0x90000000, LENGTH = 64M SDRAM (xrw) : ORIGIN = 0xD0000000, LENGTH = 6000K SDRAM2 (xrw) : ORIGIN = 0xD05DC000, LENGTH = 2192K}/* Sections */SECTIONS{ /* The startup code into "FLASH" Rom type memory */ .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } >FLASH /* The program code and other data into "FLASH" Rom type memory */ .text : { . = ALIGN(4); *(.text) /* .text sections (code) */ *(.text*) /* .text* sections (code) */ *(.glue_7) /* glue arm to thumb code */ *(.glue_7t) /* glue thumb to arm code */ *(.eh_frame) KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(4); _etext = .; /* define a global symbols at end of code */ } >FLASH /* Constant data into "FLASH" Rom type memory */ .rodata : { . = ALIGN(4); *(.rodata) /* .rodata sections (constants, strings, etc.) */ *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ . = ALIGN(4); } >FLASH .ARM.extab : { . = ALIGN(4); *(.ARM.extab* .gnu.linkonce.armextab.*) . = ALIGN(4); } >FLASH .ARM : { . = ALIGN(4); __exidx_start = .; *(.ARM.exidx*) __exidx_end = .; . = ALIGN(4); } >FLASH .preinit_array : { . = ALIGN(4); PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); . = ALIGN(4); } >FLASH .init_array : { . = ALIGN(4); PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); . = ALIGN(4); } >FLASH .fini_array : { . = ALIGN(4); PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(SORT(.fini_array.*))) KEEP (*(.fini_array*)) PROVIDE_HIDDEN (__fini_array_end = .); . = ALIGN(4); } >FLASH /* Used by the startup to initialize data */ _sidata = LOADADDR(.data); /* Initialized data sections into "RAM" Ram type memory */ .data : { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ *(.data) /* .data sections */ *(.data*) /* .data* sections */ *(.RamFunc) /* .RamFunc sections */ *(.RamFunc*) /* .RamFunc* sections */ . = ALIGN(4); _edata = .; /* define a global symbol at data end */ } >RAM_D1 AT> FLASH /* Uninitialized data section into "RAM" Ram type memory */ . = ALIGN(4); .bss : { /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; /* define a global symbol at bss end */ __bss_end__ = _ebss; } >RAM_D1 /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */ ._user_heap_stack : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); } >RAM_D1 .lwip_sec (NOLOAD) : { . = ABSOLUTE(0x30000000); *(.RxDecripSection) . = ABSOLUTE(0x30000200); *(.TxDecripSection) . = ABSOLUTE(0x30000400); *(.Rx_PoolSection) } >RAM_D2 AT> FLASH /* Remove information from the compiler libraries */ /DISCARD/ : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } .ARM.attributes 0 : { *(.ARM.attributes) }TouchGFX_Framebuffer (NOLOAD) : { *(TouchGFX_Framebuffer TouchGFX_Framebuffer.*) *(.gnu.linkonce.r.*) . = ALIGN(0x4); } >SDRAMBufferSection (NOLOAD) : { *(Video_RGB_Buffer Video_RGB_Buffer.*) *(.gnu.linkonce.r.*) . = ALIGN(0x4); } >SDRAM2ExtFlashSection : { *(ExtFlashSection ExtFlashSection.*) *(.gnu.linkonce.r.*) . = ALIGN(0x4); } >QUADSPI}Any insights or suggestions regarding the MPU configuration and linker script for the "Double framebuffer" strategy would be greatly appreciated!
Thanks for your collaboration!
2026-01-20 2:54 AM
Hello @Sasa1234.
If you are using the TBS available in TouchGFX Designer, you need to update the video configuration in X-CUBE-TOUCHGFX in STM32CubeMX. The current configuration is set up for single-buffer video, but the buffer size is only 640x480 pixels, so the video does not fit. You need to update the buffer size, and ensure that the linker and MPU configuration are aligned with the new size.
Alternatively, use the Direct to Framebuffer video decoding strategy instead. Then you avoid the video buffer completely.
Best regards,
Johan
2026-01-20 8:19 AM
Hi @JohanAstrup ,
I try to change from X-CUBE-TOUCHGFX some parameters like Buffer Height and Buffer Width and Buffer Height but nothing changes and it does not work!!!However there is a way to change manually ,in files generated by touchgfx designer, files in order to use your suggestions.
Thanks you again,
Sasà
2026-01-22 6:41 AM
Hi @JohanAstrup ,
I have a little progress. By using Direct to Frame strategies, I obtain a screen with green lines at top of the display. I modified TouchGFXGeneratedHAl.cpp and linker file.May I miss something? Thank you for your Help!!!!!
/**
******************************************************************************
* File Name : TouchGFXGeneratedHAL.cpp
******************************************************************************
* This file is generated by TouchGFX Generator 4.25.0. Please, do not edit!
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include <TouchGFXGeneratedHAL.hpp>
#include <touchgfx/hal/OSWrappers.hpp>
#include <gui/common/FrontendHeap.hpp>
#include <touchgfx/hal/GPIO.hpp>
#include <HardwareMJPEGDecoder.hpp>
#include <DedicatedBufferVideoController.hpp>
#include <stm32h7xx_hal.h>
HardwareMJPEGDecoder mjpegdecoder1;
namespace
{
//LOCATION_PRAGMA_NOLOAD("Video_RGB_Buffer")
//uint32_t videoRGBBuffer[153600] LOCATION_ATTRIBUTE_NOLOAD("Video_RGB_Buffer");
//DedicatedBufferController<1, 640, 480, 640 * 2U, Bitmap::RGB565> videoController;
DedicatedBufferController<1, 800, 800, 800 * 2U, Bitmap::RGB565> videoController;
}
//Singleton Factory
VideoController& VideoController::getInstance()
{
return videoController;
}
#include "stm32h7xx.h"
#include "stm32h7xx_hal_ltdc.h"
using namespace touchgfx;
namespace
{
static uint16_t lcd_int_active_line;
static uint16_t lcd_int_porch_line;
}
void TouchGFXGeneratedHAL::initialize()
{
HAL::initialize();
registerEventListener(*(Application::getInstance()));
setFrameBufferStartAddresses((void*)0xD0000000, (void*)0xD02EE000, (void*)0);
/*
* Add DMA2D to hardware decoder
*/
mjpegdecoder1.addDMA(dma);
/*
* Add hardware decoder to video controller
*/
videoController.addDecoder(mjpegdecoder1, 0);
// videoController.setRGBBuffer((uint8_t*)videoRGBBuffer, sizeof(videoRGBBuffer));
videoController.setRGBBuffer((uint8_t*)LTDC_Layer1->CFBAR, 800*800*2);
}
void TouchGFXGeneratedHAL::configureInterrupts()
{
NVIC_SetPriority(DMA2D_IRQn, 9);
NVIC_SetPriority(LTDC_IRQn, 9);
}
void TouchGFXGeneratedHAL::enableInterrupts()
{
NVIC_EnableIRQ(DMA2D_IRQn);
NVIC_EnableIRQ(LTDC_IRQn);
}
void TouchGFXGeneratedHAL::disableInterrupts()
{
NVIC_DisableIRQ(DMA2D_IRQn);
NVIC_DisableIRQ(LTDC_IRQn);
}
void TouchGFXGeneratedHAL::enableLCDControllerInterrupt()
{
lcd_int_active_line = (LTDC->BPCR & LTDC_BPCR_AVBP_Msk) - 1;
lcd_int_porch_line = (LTDC->AWCR & LTDC_AWCR_AAH_Msk) - 1;
/* Sets the Line Interrupt position */
LTDC->LIPCR = lcd_int_active_line;
/* Line Interrupt Enable */
LTDC->IER |= LTDC_IER_LIE;
}
bool TouchGFXGeneratedHAL::beginFrame()
{
return HAL::beginFrame();
}
void TouchGFXGeneratedHAL::endFrame()
{
HAL::endFrame();
videoController.endFrame();
dma.start();
}
uint16_t* TouchGFXGeneratedHAL::getTFTFrameBuffer() const
{
return (uint16_t*)LTDC_Layer1->CFBAR;
}
void TouchGFXGeneratedHAL::setTFTFrameBuffer(uint16_t* adr)
{
LTDC_Layer1->CFBAR = (uint32_t)adr;
/* Reload immediate */
LTDC->SRCR = (uint32_t)LTDC_SRCR_IMR;
}
void TouchGFXGeneratedHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
HAL::flushFrameBuffer(rect);
}
bool TouchGFXGeneratedHAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes)
{
return HAL::blockCopy(dest, src, numBytes);
}
void TouchGFXGeneratedHAL::InvalidateCache()
{
// Because DMA2D access main memory directly, the DCache must be invalidated
// becuase it could hold a wrong image of the framebuffer. That's done
// using the function SCB_CleanInvalidateDCache(). Remember to enable
// "CPU Cache" in the "System Core" settings for "Cortex M7" in CubeMX
// in order for this function call to work.
if (SCB->CCR & SCB_CCR_DC_Msk)
{
SCB_CleanDCache_by_Addr((uint32_t*)LTDC_Layer1->CFBAR, 800*800*2);
//SCB_CleanInvalidateDCache();
}
}
void TouchGFXGeneratedHAL::FlushCache()
{
// If the framebuffer is placed in Write-Back cached memory (e.g. SRAM) then
// the DCache must be flushed prior to DMA2D accessing it. That's done
// using the function SCB_CleanInvalidateDCache(). Remember to enable
// "CPU Cache" in the "System Core" settings for "Cortex M7" in CubeMX in
// order for this function call to work.
if (SCB->CCR & SCB_CCR_DC_Msk)
{
SCB_CleanInvalidateDCache();
}
}
extern "C" void videoTaskFunc(void* argument)
{
videoController.decoderTaskEntry();
}
extern "C"
{
void HAL_LTDC_LineEventCallback(LTDC_HandleTypeDef* hltdc)
{
if (!HAL::getInstance())
{
return;
}
if (LTDC->LIPCR == lcd_int_active_line)
{
//entering active area
HAL_LTDC_ProgramLineEvent(hltdc, lcd_int_porch_line);
HAL::getInstance()->vSync();
OSWrappers::signalVSync();
// Swap frame buffers immediately instead of waiting for the task to be scheduled in.
// Note: task will also swap when it wakes up, but that operation is guarded and will not have
// any effect if already swapped.
HAL::getInstance()->swapFrameBuffers();
GPIO::set(GPIO::VSYNC_FREQ);
}
else
{
//exiting active area
HAL_LTDC_ProgramLineEvent(hltdc, lcd_int_active_line);
// Signal to the framework that display update has finished.
HAL::getInstance()->frontPorchEntered();
GPIO::clear(GPIO::VSYNC_FREQ);
}
}
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/*
******************************************************************************
**
** File : LinkerScript.ld
**
** Author : STM32CubeIDE
**
** Abstract : Linker script for STM32H7 series
** 1024Kbytes FLASH
** 800Kbytes RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
** Distribution: The file is distributed as is without any warranty
** of any kind.
**
*****************************************************************************
** @attention
**
** Copyright (c) 2023 STMicroelectronics.
** All rights reserved.
**
** This software is licensed under terms that can be found in the LICENSE file
** in the root directory of this software component.
** If no LICENSE file comes with this software, it is provided AS-IS.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1); /* end of "RAM_D1" Ram type memory */
_Min_Heap_Size = 0xc000; /* required amount of heap */
_Min_Stack_Size = 0xc800; /* required amount of stack */
/* Memories definition */
MEMORY
{
RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* Memory is divided. Actual start is 0x08000000 and actual length is 2048K */
DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K
RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K
ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
QUADSPI (r) : ORIGIN = 0x90000000, LENGTH = 64M
SDRAM (xrw) : ORIGIN = 0xD0000000, LENGTH = 8000K
/* SDRAM2 (xrw) : ORIGIN = 0xD05DC000, LENGTH = 2192K*/
}
/* Sections */
SECTIONS
{
/* The startup code into "FLASH" Rom type memory */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data into "FLASH" Rom type memory */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data into "FLASH" Rom type memory */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab : {
. = ALIGN(4);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(4);
} >FLASH
.ARM : {
. = ALIGN(4);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(4);
} >FLASH
.preinit_array :
{
. = ALIGN(4);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
} >FLASH
.init_array :
{
. = ALIGN(4);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
} >FLASH
.fini_array :
{
. = ALIGN(4);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(4);
} >FLASH
/* Used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections into "RAM" Ram type memory */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
*(.RamFunc) /* .RamFunc sections */
*(.RamFunc*) /* .RamFunc* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM_D1 AT> FLASH
/* Uninitialized data section into "RAM" Ram type memory */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss section */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM_D1
/* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM_D1
.lwip_sec (NOLOAD) : {
. = ABSOLUTE(0x30000000);
*(.RxDecripSection)
. = ABSOLUTE(0x30000200);
*(.TxDecripSection)
. = ABSOLUTE(0x30000400);
*(.Rx_PoolSection)
} >RAM_D2 AT> FLASH
/* Remove information from the compiler libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
TouchGFX_Framebuffer (NOLOAD) :
{ . = ALIGN(32);
*(TouchGFX_Framebuffer TouchGFX_Framebuffer.*)
*(.gnu.linkonce.r.*)
. = ALIGN(32);
} >SDRAM
/*
BufferSection (NOLOAD) :
{
*(Video_RGB_Buffer Video_RGB_Buffer.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >SDRAM2
*/
ExtFlashSection :
{
*(ExtFlashSection ExtFlashSection.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >QUADSPI
}
2026-01-23 3:07 AM
It does not sound very good, if generation in CubeMX does not trigger any code changes, when you change parameters ..
Can you try to start a new project and verify if it works then?
Does that also mean that you have changed to Direct to Framebuffer manually - not using STM32CubeMX?
Best regards,
Johan
2026-01-23 5:59 AM
Hi @JohanAstrup ,
I am working on a project using TouchGFX for a Riverdi 12.1" display. I’ve encountered an issue when trying to play a video with a resolution of 800x480 px (the default resolution for this framework/display setup). Although the code generates without errors, the result on the target hardware is a black screen.
I noticed a significant discrepancy between the generated HAL and the video assets:
Code Generation Issue: Even after modifying the configuration via the .ioc file (as shown in my screenshots) and enabling automatic code generation, the settings don't seem to sync properly. The system appears to default to a double framebuffer strategy regardless of my changes.
Buffer Mismatch: In TouchGFXGeneratedHAL.cpp, the video buffer is allocated for a 640x480 resolution:
uint32_t videoRGBBuffer[153600]
DedicatedBufferController<1, 640, 480, 640 * 2U, Bitmap::RGB565> videoController;
Asset Mismatch: However, my VideoDatabase.hpp correctly identifies the asset as 800x480:
const uint32_t video_video1_800x480_bin_length = 5519444;
Because the videoRGBBuffer is calculated for 640x480 ($153,600$ pixels) instead of 800x480 ($384,000$ pixels), I suspect a memory overflow or a failure in the decoding controller is causing the black screen.
My questions is:
Is there a specific guide or best practice for manually overriding these framebuffering strategies without having them overwritten by the code generator?
Any help or documentation on handling video buffering strategies for high-resolution displays would be greatly appreciated!"
2026-01-30 12:56 AM - edited 2026-01-30 12:56 AM
Hello @JohanAstrup ,
I want to write an updated situation...Some times I have this picture on display
But it is a random situation.....How may I solve this?
2026-02-02 6:59 AM
Hello @Sasa1234.
As I do not have the board, I cannot verify it on hardware.
However, please first of all confirm whether your code generation works. If code generation in STM32CubeMX does not work, this is the first issue you should resolve. When I start a new project from the TBS available in TouchGFX Designer, I can open the .ioc file, change the video buffer configuration in X-CUBE-TOUCHGFX, and see that the changes are reflected in the generated code. Therefore, if code generation does not work and you are unable to make it work, create a new project.
As the second step, you have two options. You can continue to use a dedicated video buffer, but you need to increase the buffer width and height to at least 800 x 800. If you do this, ensure that the linker script and MPU configuration support the larger video buffer.
Alternatively, you can use Direct to Framebuffer. In this case, you do not need to modify the linker script or MPU configuration.
You can read about the different video decoding strategies here.
Best regards,
Johan
2026-02-05 2:17 AM - edited 2026-02-05 2:18 AM
Hi @JohanAstrup ,
thank you for your reply.
My board is the Riverdi 12-inch RVT121HVSFWN00 and I am using TouchGFX Designer 4.25.
From this software, I go to Create New → By Partners → RVT121HVSFWN00 and generate the entire project, which internally creates everything up to the .ioc file.
If I then modify the .ioc file, it does not result in any code changes related to the video decoding strategies.
It might be a framework bug.
The other option I have is to create a new project by selecting the microcontroller first and then importing TouchGFX—but would I need to test that approach?
I am curious anyway, but at the code level, are there any manual references, and which files would I need to modify?
Thank you again for your help, which is always very welcome.
2026-02-16 1:30 AM
Hello everyone,
After some experimentation, I've successfully managed to play a video in full screen on a Riverdi 12-inch display!
Here's what I did:
However, I encountered an issue when trying the "Double framebuffer" strategy. I set the width to 1280 px and height to 800 px, but the result was the same frame image being repeated and overlaid twice on the screen.
I'm curious and would like to understand how to properly set up the MPU and the linker script (.ld file) to resolve this, especially given the following linker script configuration:
/*********************************************************************************** File : LinkerScript.ld**** Author : STM32CubeIDE**** Abstract : Linker script for STM32H7 series** 1024Kbytes FLASH** 800Kbytes RAM**** Set heap size, stack size and stack location according** to application requirements.**** Set memory bank area and size if external memory is used.**** Target : STMicroelectronics STM32**** Distribution: The file is distributed as is without any warranty** of any kind.********************************************************************************* @attention**** Copyright (c) 2023 STMicroelectronics.** All rights reserved.**** This software is licensed under terms that can be found in the LICENSE file** in the root directory of this software component.** If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************//* Entry Point */ENTRY(Reset_Handler)/* Highest address of the user mode stack */_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1); /* end of "RAM_D1" Ram type memory */_Min_Heap_Size = 0xc000; /* required amount of heap */_Min_Stack_Size = 0xc800; /* required amount of stack *//* Memories definition */MEMORY{ RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* Memory is divided. Actual start is 0x08000000 and actual length is 2048K */ DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K QUADSPI (r) : ORIGIN = 0x90000000, LENGTH = 64M SDRAM (xrw) : ORIGIN = 0xD0000000, LENGTH = 6000K SDRAM2 (xrw) : ORIGIN = 0xD05DC000, LENGTH = 2192K}/* Sections */SECTIONS{ /* The startup code into "FLASH" Rom type memory */ .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } >FLASH /* The program code and other data into "FLASH" Rom type memory */ .text : { . = ALIGN(4); *(.text) /* .text sections (code) */ *(.text*) /* .text* sections (code) */ *(.glue_7) /* glue arm to thumb code */ *(.glue_7t) /* glue thumb to arm code */ *(.eh_frame) KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(4); _etext = .; /* define a global symbols at end of code */ } >FLASH /* Constant data into "FLASH" Rom type memory */ .rodata : { . = ALIGN(4); *(.rodata) /* .rodata sections (constants, strings, etc.) */ *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ . = ALIGN(4); } >FLASH .ARM.extab : { . = ALIGN(4); *(.ARM.extab* .gnu.linkonce.armextab.*) . = ALIGN(4); } >FLASH .ARM : { . = ALIGN(4); __exidx_start = .; *(.ARM.exidx*) __exidx_end = .; . = ALIGN(4); } >FLASH .preinit_array : { . = ALIGN(4); PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); . = ALIGN(4); } >FLASH .init_array : { . = ALIGN(4); PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); . = ALIGN(4); } >FLASH .fini_array : { . = ALIGN(4); PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(SORT(.fini_array.*))) KEEP (*(.fini_array*)) PROVIDE_HIDDEN (__fini_array_end = .); . = ALIGN(4); } >FLASH /* Used by the startup to initialize data */ _sidata = LOADADDR(.data); /* Initialized data sections into "RAM" Ram type memory */ .data : { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ *(.data) /* .data sections */ *(.data*) /* .data* sections */ *(.RamFunc) /* .RamFunc sections */ *(.RamFunc*) /* .RamFunc* sections */ . = ALIGN(4); _edata = .; /* define a global symbol at data end */ } >RAM_D1 AT> FLASH /* Uninitialized data section into "RAM" Ram type memory */ . = ALIGN(4); .bss : { /* This is used by the startup in order to initialize the .bss section */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; /* define a global symbol at bss end */ __bss_end__ = _ebss; } >RAM_D1 /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */ ._user_heap_stack : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); } >RAM_D1 .lwip_sec (NOLOAD) : { . = ABSOLUTE(0x30000000); *(.RxDecripSection) . = ABSOLUTE(0x30000200); *(.TxDecripSection) . = ABSOLUTE(0x30000400); *(.Rx_PoolSection) } >RAM_D2 AT> FLASH /* Remove information from the compiler libraries */ /DISCARD/ : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } .ARM.attributes 0 : { *(.ARM.attributes) }TouchGFX_Framebuffer (NOLOAD) : { *(TouchGFX_Framebuffer TouchGFX_Framebuffer.*) *(.gnu.linkonce.r.*) . = ALIGN(0x4); } >SDRAMBufferSection (NOLOAD) : { *(Video_RGB_Buffer Video_RGB_Buffer.*) *(.gnu.linkonce.r.*) . = ALIGN(0x4); } >SDRAM2ExtFlashSection : { *(ExtFlashSection ExtFlashSection.*) *(.gnu.linkonce.r.*) . = ALIGN(0x4); } >QUADSPI}Any insights or suggestions regarding the MPU configuration and linker script for the "Double framebuffer" strategy would be greatly appreciated!
Thanks for your collaboration!