2025-09-16 1:44 AM - last edited on 2025-09-16 1:50 AM by Andrew Neil
Hi,
I am working with an STM32F769 MCU driving a 480×272 RGB565 TFT display through LTDC.
Architecture:
Framebuffer in external SDRAM (IS42S16800F-7TL, 16 MB) connected via FMC.
LTDC + DMA2D used for graphics update (TouchGFX).
I²C + DMA used to read/write parameters on an external FRAM.
System managed with FreeRTOS.
Problem:
When a long I²C DMA transfer is running (FRAM read/write for parameter storage), I see horizontal glitches on the display: objects temporarily shift to the right (as if some pixels were missing).
The curious part is that the phenomenon only happens if I change the initialization sequence in main() from this:
MX_LTDC_Init();
HAL_Delay(1000); // avoid flickering on display
MX_TouchGFX_Init();
HAL_Delay(200); // avoid flickering on display
osKernelInitialize();
…to this:
MX_LTDC_Init();
HAL_Delay(1000); // avoid flickering on display
MX_TouchGFX_Init();
HAL_Delay(200); // avoid flickering on display
__NOP(); // added
__NOP(); // added
osKernelInitialize();
adding only 2 _NOP();
really can’t explain why just adding, in this init sequence, two NOP instructions makes the glitch appear.
Solved! Go to Solution.
2025-09-18 8:18 AM - edited 2025-09-18 8:20 AM
Ok as I said, it could not solve the issue but that's something recommended.
Could you please share your full MPU configuration? are you sure you called MPU_Config() in main() just before the cache enable?
2025-09-18 8:19 AM
I share my actual MPU_config
void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};
/* Disables the MPU */
HAL_MPU_Disable();
/* Configure the MPU as Strongly ordered for not defined regions */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x00;
MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x87;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0x90000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_256MB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.BaseAddress = 0xC0000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_16MB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.BaseAddress = 0x90000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_64MB;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Number = MPU_REGION_NUMBER3;
MPU_InitStruct.BaseAddress = 0xC00C0000;
MPU_InitStruct.Size = MPU_REGION_SIZE_1MB;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0xC0000000; // Base SDRAM framebuffer
MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; // MPU_ACCESS_CACHEABLE; // Non-cacheable
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; // opzionale, dipende se altri core/DMA accedono
MPU_InitStruct.Number = MPU_REGION_NUMBER4;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00; // tutte abilitate
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
// HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Number = MPU_REGION_NUMBER5;
MPU_InitStruct.BaseAddress = 0XC01C8000;
MPU_InitStruct.Size = MPU_REGION_SIZE_32KB;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enables the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
2025-09-18 8:23 AM
You kept the region number = 0 for the second region which overwrites the first one:
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
You need to increment all the region numbers starting from the QSPI region:
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
2025-09-18 8:23 AM
I share also Linker script file
/* Memories definition */
MEMORY
{
RAM_DMA (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
RAM (xrw) : ORIGIN = 0x20020000, LENGTH = (512K - 128K)
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = (1024K - 8) /* flash size has been halved to allow for dual-bank */
FLASH_METADATA (rx) : ORIGIN = 0x080FFFF8, LENGTH = 8
QUADSPI (rx) : ORIGIN = 0x90000000, LENGTH = 16M /* external flash size has been halved to allow for dual-bank */
QUADSPI_COMMON_DATA (rx) : ORIGIN = 0x92000000, LENGTH = 32M
/* the fist 256K are declared for the HMI frame buffer and cannot be used by others */
EXTERNAL_RAM_DS (xrw) : ORIGIN = 0xC0040000, LENGTH = 512K
/*EXTERNAL_RAM_FREERTOS_HEAP(xrw) : ORIGIN = 0xC00C0000, LENGTH = 512K */
/*EXTERNAL_RAM_LWIP (xrw) : ORIGIN = 0xC0140000, LENGTH = 16K */
/*EXTERNAL_RAM_DATA (xrw) : ORIGIN = 0xC0144000, LENGTH = (16M - 256K -512K - 512K - 16K) */
/* aumentato da 512K a 768K */
EXTERNAL_RAM_FREERTOS_HEAP (xrw) : ORIGIN = 0xC00C0000, LENGTH = 1M
/* LWIP rimane invariato */
EXTERNAL_RAM_LWIP (xrw) : ORIGIN = 0xC01C0000, LENGTH = 32K
EXTERNAL_RAM_LWIP_HEAP (xrw) : ORIGIN = 0XC01C8000, LENGTH = 32K
EXTERNAL_RAM_DATA (xrw) : ORIGIN = 0xC01D0000, LENGTH = (16M - 256K - 1M - 512K - 32K - 32K)
}
2025-09-18 8:24 AM
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* Enable the CPU Cache */
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
2025-09-18 8:36 AM - edited 2025-09-18 8:37 AM
You're right, I forgot to update the order when I added the foreground init but now that I've done so, when It call MX_FMC_init(), it fall into MemManage_Handler().
Did I forget to add some sections to MPU_config?
2025-09-18 8:48 AM
Try to reorganize your MPU config many scattered regions, maybe there is something not correct when you are using the same config for the next region.
For example, this MPU region config is not useful:
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0x90000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_256MB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
That was already disabled by the background region.
Reorganize the MPU config of the SDRAM by incremental address, you have many scattered regions there, and don't use an already initialized member in the MPU structure. Set it again in the new region etc ..
2025-09-18 9:02 AM
Couldn’t this be the cause of our problem? Reference from AN4861.
2025-09-18 9:24 AM - edited 2025-09-18 9:24 AM
@CiuppaPT wrote:
Couldn’t this be the cause of our problem? Reference from AN4861.
The first MPU config, I provided to you (the background config) prevents that issue to occur. Moreover, the workaround of that limitation was already implemented in the system_stm32f7xx.c file / line 632:
/*
* Disable the FMC bank1 (enabled after reset).
* This, prevents CPU speculation access on this bank which blocks the use of FMC during
* 24us. During this time the others FMC master (such as LTDC) cannot use it!
*/
FMC_Bank1->BTCR[0] = 0x000030d2;
(void)(tmp);
}
Now, as I said, try to reorganize your MPU config.
2025-09-18 9:55 AM
My system_stm32f7xx.c ends at line 268 ( CMSIS Device version number V1.2.8 ) and not include line line 632 with the...
FMC_Bank1->BTCR[0] = 0x000030d2;