Skip to main content
Visitor II
October 5, 2025
Question

14bit DCMI to 32MB SDRAM at 48MHz, on STM32H743IIT6

  • October 5, 2025
  • 2 replies
  • 446 views

Hello dear ST forum.

I need again you precious help.

I am working on a data acquisition device, and need to get 14bit data from a parallel ADC connected on DCMI pins,

at 48MHz, and move it to a 32MB SDRAM (W9825G6KH-6I) till almost full. The MCU is STM32H743IIT6.

After reading, and reading, I did understand that the only available way, is to use MDMA linked list. 

Create 255 nodes and transfer the 16 bit data to the SDRAM.

 

Can someone point me the right direction?

 

 

 

    This topic has been closed for replies.

    2 replies

    ST Employee
    October 5, 2025

    Hello nedelcu

    The approach using MDMA linked list nodes is indeed a very efficient way to handle such high-throughput data transfers without CPU overhead.

    Here are some tips to help:

    • Use DCMI to capture 14-bit ADC data at 48 MHz.
    • Use MDMA linked list mode with 255 nodes to transfer data chunks continuously.
    • Configure each MDMA node to transfer a block of 16-bit data from DCMI FIFO to SDRAM.
    • Chain nodes circularly to fill SDRAM buffer until full.
    • Monitor and manage SDRAM buffer usage carefully.
    nedelcuAuthor
    Visitor II
    October 5, 2025

    Thank you for your concise answer!

     

    To be sure that I ask the right questions and not wasting your time, I will point exactly what is my plan

    for the ongoing project.

    My current approach:

    • DCMI configured in continuous mode, no HSYNC/VSYNC (ADC-like data source)

    • 14-bit parallel data at 48 MHz on D[13:0], with DCMI_PCLK = 48 MHz

    • MDMA linked-list mode with 255 nodes

      • Each node transfers 65,535 half-words (131,070 bytes)

      • Each node destination: incremental SDRAM address

      • LAR of last node optionally points to the first for circular capture (maybe implement trigger in future)

    • SDRAM configured and verified working via FMC at +100 MHz x16

    • Cache cleaned/invalidation handled for MDMA buffers

    Please help me with the following questions:

    1. Can DCMI continuous mode capture correctly without HSYNC/VSYNC at 48 MHz if the input clock and data are stable?
      Any special configuration needed for ADC-style capture (no frame sync)?

    2. Is DCMI → DMAMUX → MDMA request rate sufficient to sustain 48 MHz continuous half-word transfers, or should I enable internal burst mode or FIFO threshold settings? Can I go up to 60 MHz?

    3. Can the MDMA linked list operate in circular mode if the last node’s LAR points back to the first node?
      Or does the MDMA stop automatically after the last node?

    4. For SDRAM writes via FMC, are there any recommended AXI or MPU memory attributes (e.g., non-cacheable, write-through) to avoid cache incoherency or MDMA stalls?

    5. Is 48 MHz DCMI pixel clock within the practical limit for STM32H743IIT6 when capturing continuously through MDMA to SDRAM? Can I go up to 60 MHz?

    6. Any ST examples or application notes demonstrating DCMI + MDMA → SDRAM (e.g., from internal test projects)?

    I already reviewed:

    • RM0433 (DCMI, DMAMUX, and MDMA sections)

    • AN5020 (Using DCMI, 6.4.9 DMA configuration for higher resolutions)

    But I’d appreciate confirmation or advice from ST experts before diving deeper finalizing the hardware.

    Thank you for your support,

    Nedelcu Bogdan Sebastian

     

    ST Employee
    October 6, 2025

    Hello @nedelcu 

     

    1 Can DCMI capture correctly without HSYNC/VSYNC if input clock and data are stable?

    • Yes, the STM32H7 DCMI peripheral supports continuous capture mode without HSYNC and VSYNC signals, which is suitable for ADC-like data streams.

    • Special configuration needed?

      • Set DCMI_CROP feature disabled (if cropping not needed).
      • Configure DCMI_CAPTURE_MODE = Continuous.
      • Disable HSYNC and VSYNC inputs in the DCMI control register.
      • Use embedded synchronization mode or hardware synchronization with only PCLK.
      • Ensure DCMI FIFO threshold is configured properly (see next section).
      • The DCMI will capture data on the rising edge of PCLK and push it into its internal FIFO.
    • Important: The input data and clock must be stable and synchronized to the MCU clock domain or have proper FIFO buffering to avoid metastability.

     

    2 Is the request rate sufficient for 48 MHz continuous half-word transfers?

    • At 48 MHz and 16-bit data width, the raw data rate is: 48×106×2 bytes=96 MB/s
    • The STM32H7 MDMA and FMC interface can sustain high throughput, but real achievable throughput depends on bus arbitration, FMC timing, and MDMA burst settings.
    • Can you go up to 60 MHz?

      • 60 MHz is at the upper limit of the DCMI peripheral pixel clock according to STM32H7 datasheet.
      • It is possible but challenging due to increased bus and memory speed requirements.
      • You must carefully optimize FMC timings, MDMA burst.

     

    3 Can MDMA linked list operate circularly by pointing last node’s LAR back to first node?

    • The MDMA linked list does not natively support circular mode.
    • When the last linked list node finishes, the MDMA transfer stops automatically.
    • To implement continuous circular buffer behavior, you must:
      • In MDMA transfer complete interrupt, reconfigure or restart MDMA with the linked list again.
      • Or dynamically update the linked list pointer in software to restart the chain.
      • Some users implement software-managed circular buffering by restarting the MDMA linked list in the ISR.

     

    4 Cache and MPU settings to avoid cache incoherency or MDMA stalls:

    • Configure the SDRAM region as Write-Through or Non-Cacheable in MPU to avoid cache coherency issues.
    • For large continuous DMA transfers, Non-Cacheable is safer but may reduce CPU access speed.
    • Ensure AXI bus priority is set to favor FMC/MDMA transfers to reduce stalls.

     

    5 Yes, 48 MHz is within the practical limit for continuous DCMI capture on STM32H743IIT6.

    • Going beyond 48 MHz (e.g., 60 MHz) is possible but requires careful tuning and may reduce system stability.

     

    ST provides some example projects and application notes that can help:

    • STM32CubeH7 Firmware Package includes DCMI examples (mostly camera capture).
    • AN5116: "Using the STM32H7 FMC SDRAM interface" — for SDRAM optimization.
    • AN4861: "Using the STM32H7 MDMA controller" — explains MDMA linked list and burst modes.
    • STM32CubeH7 DCMI Capture Example: shows DCMI + DMA to internal SRAM or SDRAM.
    • ST Community Forums and GitHub: users share projects with DCMI + MDMA + SDRAM.
    •  

     

    BR

     

    nedelcuAuthor
    Visitor II
    October 10, 2025

    Hello!

    Well..., until now, unfortunately no success. I did try a lot of modifications of the DMA linked list configuration, DCMI configuration, and nothing.

    I use TIM2_CH1 to generate 24MHz on PA5, and route the signal with jumper wire to PCXLK PA6 pin.

    On the data pins I don't have anything connected! 

    I use PA0 as acquisition start trigger.

    In the last HAL, the MDMA_REQUEST_DCMI is not defined, and I did used the 75 value found in DMA file.

    On running it never get's to transfer the data.

    I don't use any optimizations!

    Hope someone will have some spare time and throw an eye, maybe you can see any mistake that can 
    be corrected to make the code work.

    Maybe I don't use peripherals the right way, or maybe I don't initialize them as I should???!!!

    I am now struggling to make a 14 bit sine generator on an Artix7 and a 24MHz clock, maybe the DCMI

    does not trigger the MDMA first node because it doesn't see any data on D0..13 pins??? Maybe?

    I did add the:

     
    /* -----------------------------------------------------------------
     Section for MDMA linked-list nodes or other AXI/D1 SRAM objects.
     Anything declared as:
     __attribute__((section(".RAM_D1")))
     will be placed here (AXI-accessible memory for DMA/MDMA).
     ----------------------------------------------------------------- */
     .RAM_D1 (NOLOAD) :
     {
     . = ALIGN(32);
     *(.RAM_D1)
     *(.RAM_D1*)
     . = ALIGN(32);
     } >RAM_D1
     

    to the STM32H743IITX_FLASH.ld.

    The MDMA Linked Node List are declared as:

    ALIGN_32BYTES ( static MDMA_LinkNodeTypeDef mdma_nodes[MDMA_NODE_COUNT] __attribute__((section(".RAM_D1"))) );

    Thank you!
    If needed I can post the full project, made under STM32CubeIDE Version: 1.19.0 and with:
    stm32cubeh7-v1-12-0, and stm32cubeh7-v1-12-1.

    nedelcuAuthor
    Visitor II
    October 11, 2025

    I did start another approach, because in the documentation states that by not using HSYNC/VSYNC you can not work with 14bit data. At least that is what I understand by now. Maybe someone will teach me something different, please.

    I generate DCMI PXCLK using TIM2 at 24MHz ,and chain TIM3 HSYNC 30 kHz, and TIM4 VSYNC 60 Hz.

    I will test using this configuration to see if DCMI will loose samples.

    On the run I will need your valuable help.

    nedelcu_0-1760302075994.png

    If someone consider useful, here is the timers TIM2 -> TIM3 -> TIM4 chaining function, tested with oscilloscope and working:

    // ====================== STM32H743IIT6 ========================
    // TIM2 -> TIM3 -> TIM4 synchronized chain
    // TIM2: PXCLK 24 MHz (PA5)
    // TIM3: HSYNC 30 kHz (PB5)
    // TIM4: VSYNC 60 Hz (PB6)
    // =============================================================
    static void TIMERS_Init(void) {
     TIM_ClockConfigTypeDef sClockSourceConfig = {0};
     TIM_SlaveConfigTypeDef sSlaveConfig = {0};
     TIM_MasterConfigTypeDef sMasterConfig = {0};
     TIM_OC_InitTypeDef sConfigOC = {0};
     GPIO_InitTypeDef GPIO_InitStruct = {0};
    
     /* Enable GPIO & TIM clocks */
     __HAL_RCC_GPIOA_CLK_ENABLE();
     __HAL_RCC_GPIOB_CLK_ENABLE();
     __HAL_RCC_TIM2_CLK_ENABLE();
     __HAL_RCC_TIM3_CLK_ENABLE();
     __HAL_RCC_TIM4_CLK_ENABLE();
    
     // ===================== TIM2 : Pixel Clock =====================
     htim2.Instance = TIM2;
     htim2.Init.Prescaler = 0; // 240 MHz timer clock
     htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
     htim2.Init.Period = 9; // 240 MHz / (9+1) = 24 MHz
     htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
     htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
     HAL_TIM_Base_Init(&htim2);
     HAL_TIM_PWM_Init(&htim2);
    
     sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; // Send update events to TIM3
     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
     HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
    
     sConfigOC.OCMode = TIM_OCMODE_PWM1;
     sConfigOC.Pulse = 5; // 50% duty (5/10)
     sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
     HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
    
     // PA5 - TIM2_CH1 - PCLK out
     GPIO_InitStruct.Pin = GPIO_PIN_5;
     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
     GPIO_InitStruct.Pull = GPIO_NOPULL;
     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
     GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
     // ===================== TIM3 : HSYNC =====================
     htim3.Instance = TIM3;
     htim3.Init.Prescaler = 0;
     htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
     htim3.Init.Period = 799; // 800 pixels per line
     htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
     htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
     HAL_TIM_Base_Init(&htim3);
     HAL_TIM_PWM_Init(&htim3);
    
     // TIM3 counts TIM2 update events (pixel clocks)
     sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
     sSlaveConfig.InputTrigger = TIM_TS_ITR1; // TIM2 -> TIM3
     HAL_TIM_SlaveConfigSynchro(&htim3, &sSlaveConfig);
    
     sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; // HSYNC trigger for TIM4
     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
     HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);
    
     sConfigOC.OCMode = TIM_OCMODE_PWM1;
     sConfigOC.Pulse = 4; // HSYNC 4 pixels (166.7 ns)
     sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
     HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2);
    
     // PB5 - TIM3_CH2 - HSYNC out
     GPIO_InitStruct.Pin = GPIO_PIN_5;
     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
     GPIO_InitStruct.Pull = GPIO_NOPULL;
     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
     GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
     // ===================== TIM4 : VSYNC =====================
     htim4.Instance = TIM4;
     htim4.Init.Prescaler = 0;
     htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
     htim4.Init.Period = 499; // 500 lines per frame
     htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
     htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
     HAL_TIM_Base_Init(&htim4);
     HAL_TIM_PWM_Init(&htim4);
    
     // TIM4 counts TIM3 update events (lines)
     sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
     sSlaveConfig.InputTrigger = TIM_TS_ITR2; // TIM3 -> TIM4 (ITR2 on H743)
     HAL_TIM_SlaveConfigSynchro(&htim4, &sSlaveConfig);
    
     sConfigOC.OCMode = TIM_OCMODE_PWM1;
     sConfigOC.Pulse = 1; // VSYNC 1 line (33.3 µs)
     sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
     HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1);
    
     // PB6 - TIM4_CH1 - VSYNC out
     GPIO_InitStruct.Pin = GPIO_PIN_6;
     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
     GPIO_InitStruct.Pull = GPIO_NOPULL;
     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
     GPIO_InitStruct.Alternate = GPIO_AF2_TIM4;
     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
     // ===================== Start the chain =====================
     HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); // Deepest slave first
     HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
     HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // Master last
    }