cancel
Showing results for 
Search instead for 
Did you mean: 

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

nedelcu
Associate II

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?

 

 

 

7 REPLIES 7
SMSAD.1
ST Employee

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.

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

 

SMSAD.1
ST Employee

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

 

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.

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
}

Can anyone confirm
if DCMI does not store data during sync signals ?

I am worried about this issue.

Can a full port, like GPIOB bits, be copied to SDRAM using MDMA linked list ?

Or some other proven-to-work method to read 14 bit parallel data from a port, and storing to SDRAM ?

I am struggling to make DCMI->MDMA Linked List->SDRAM, but by much I test, I am beginning to believe that it is impossible. In the las HAL, MDMA_DCMI_REQUEST it does not exist, and I have used 75 which is DMA_DCMI_REQUEST, but even if I see DCMI FIFO having data, even if I trigger MDMA node list by software trigger, the DCMI does not want to trigger MDMA to start data transfer. 

Thank you!

Hello!

I need to ask again, does the DCMI works to move 14 bit data without HSYNC/VSYNC?

Does the MDMA works to move DCMI data to FMC?

In a post here on the forum Clive1 stated that maybe it is not working at all.