cancel
Showing results for 
Search instead for 
Did you mean: 

Apparently a conflict when clearing TC flags of DMA2 Streams

Digimorf
Associate III

Hello to all, I hope you and your families are safe and sound. 

I am working to improve my project based on the STM32F469 MCU, but now I am experiencing a weird behavior. I can't post the code of course, too many files connected, so I will try to explain what happens hoping that this can light up some idea on how to solve it.

This system has:

- VGA port with a 12 bit resistor DAC connected to GPIOB. The video signal is generated thanks to a PWM signal for the HSync, This trigs the TIM8 that drives the DMA2Stream1 channel7, and transfers uint16_t words from a frame buffer to the GPIOB peripheral. The mode set is Memory to Peripheral. Every scanline is counted with the TIM2 interrupt that handles all operations used to prepare the settings of the DMA transfer for pixels. 

When the DMA2Stream1 starts, trigged by the TIM8 activated by TIM2 PWM, the words of the colors are trensferred from SRAM to GPIOB. When the NDTR reaches 0 the DMA2Stream1 IRQ is fired, and here the DMA2Stream1 is disabled, the addresses are updated etc, then everything goes on for the next scanline.

- 2 LCD ports, connected to the FMC bus, using FMC_NORSRAM_BANK1. Both of them use an 8080 8bit parallel port, CS-RST-WR-RD-C/D signals are connected to the FMC and Address lines so that it's possible to write data on LCDS at different addresses of the FMC. 

Two buffers are set on the SDRAM, on FMC of course, and pixel data are trensferred from SDRAM to FMC_NORSRAM_BANK1 through DMA2Stream4 and DMA2Stream5 line by line from 0 to 239 (240 lines of the LCD) . The mode used is Mem2Mem. Every TC the IRQ is fired and addresses are updated like what happens to the VGA driver.

That said, the VGA signal works, the transfer of pixels data from SDRAM to LCDs works. BUT separately, not at the same time. Example. The VGA video starts, I load two images from the SD card to SDRAM, then I start the transfer with DMA2Streams4/5 and as soon as the TC flag is cleared in the IRQ routines of those streams, some how, the DMA2Stream1 of the VGA stops. The IRQ of the VGA is not fired anymore.

If I don't start the LCDs image refresh the VGA goes well. After several tests I saw that the problem happens when the TC flags of DMA2Stream4/5 are cleared by setting HISR registers. If I don't clear them, of course the DMA transfers of the LCDs stop, but the VGA is not affected and goes on perfectly.

I can't understand why clearing those flags the IRQ of the other stream is not fired anymore.

I hope to have explained it well.

Thanks

8 REPLIES 8
TDK
Guru

I would guess the DMA is overloaded. If a stream is stopped, you should be able to inspect its registers to see why. Possible it has an overrun/underrun error flag indicated.

If you feel a post has answered your question, please click "Accept as Solution".

+1

Show all DMA registers.

Priorities are set how, both DMA and interrupts?

How exactly do you clear the TC flags? Show code.

JW

Thank you for the suggestion, I have set up the interrupts on errors for the three streams, and the DMA2Stream1 only reports a TE when one of the others starts. It's weird because the three streams has to transfer 320 uint16 words a time, so there isn't a big load of data.

Currently, a solution to not stop the VGA DMA2Stream1 is to clear the Error flag, but the image on the screen disappear until the streams of the LCDs finish the transfer.

The transfer error if the other streams transfer a low amount of data, let's say 32 uint16 instead of 320. This unfortunately results in a slow refresh of the LCDs.

Try a timer-triggered transfer into the LCDs instead of mem2mem, to reduce the instantaneous load.

Do you have FIFO enabled for the VGA DMA?

JW

VGA DMA: SRAM to GPIOB AHB1, TIM8 driven:

  if (LL_AHB1_GRP1_IsEnabledClock (LL_AHB1_GRP1_PERIPH_DMA2) == DISABLE)
    LL_AHB1_GRP1_EnableClock (LL_AHB1_GRP1_PERIPH_DMA2);
 
  LL_DMA_StructInit (&DMA_InitStruct);
  DMA_InitStruct.Channel =  LL_DMA_CHANNEL_7;
  DMA_InitStruct.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
  DMA_InitStruct.Priority = LL_DMA_PRIORITY_VERYHIGH;
  DMA_InitStruct.Mode = LL_DMA_MODE_NORMAL;
  DMA_InitStruct.NbData = pResolution.h_clock_definition.active;
  DMA_InitStruct.PeriphOrM2MSrcAddress = (uint32_t) SYS_RGB_Port;
  DMA_InitStruct.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
  DMA_InitStruct.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
  DMA_InitStruct.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
  DMA_InitStruct.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
  DMA_InitStruct.PeriphBurst = LL_DMA_PBURST_SINGLE;
  DMA_InitStruct.MemBurst = LL_DMA_MBURST_SINGLE;
  DMA_InitStruct.FIFOMode = LL_DMA_FIFOMODE_DISABLE;
  DMA_InitStruct.FIFOThreshold = LL_DMA_FIFOTHRESHOLD_3_4;
  LL_DMA_Init (DMA2, LL_DMA_STREAM_1, &DMA_InitStruct);
  LL_DMA_EnableIT_TC (DMA2, LL_DMA_STREAM_1);
 
  // Error handlers
  LL_DMA_EnableIT_TE (DMA2, LL_DMA_STREAM_1);
  LL_DMA_EnableIT_FE (DMA2, LL_DMA_STREAM_1);
  LL_DMA_EnableIT_DME (DMA2, LL_DMA_STREAM_1);

LCD DMA: FMC SDRAM AHB3 -> FMC bank 1 AHB3, direct mem2mem

  LL_DMA_StructInit (&DMA_InitStruct);
  DMA_InitStruct.Direction = LL_DMA_DIRECTION_MEMORY_TO_MEMORY;
  DMA_InitStruct.Priority = LL_DMA_PRIORITY_LOW;
  DMA_InitStruct.Mode = LL_DMA_MODE_NORMAL;
  DMA_InitStruct.NbData = 0;
  DMA_InitStruct.PeriphOrM2MSrcAddress  = (uint32_t)(gArcadeITFirmware.lcd_driver[pLCDId]->buffer_show);
  DMA_InitStruct.PeriphOrM2MSrcIncMode  = LL_DMA_PERIPH_INCREMENT;
  DMA_InitStruct.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
  DMA_InitStruct.MemoryOrM2MDstAddress  = (uint32_t)gArcadeITFirmware.lcd_driver[pLCDId]->ptr_data;
  DMA_InitStruct.MemoryOrM2MDstIncMode  = LL_DMA_MEMORY_NOINCREMENT;
  DMA_InitStruct.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
  DMA_InitStruct.PeriphBurst = LL_DMA_PBURST_SINGLE;
  DMA_InitStruct.MemBurst = LL_DMA_MBURST_SINGLE;
  DMA_InitStruct.FIFOMode = LL_DMA_FIFOMODE_ENABLE;
  DMA_InitStruct.FIFOThreshold = LL_DMA_FIFOTHRESHOLD_FULL;
 
  switch (pLCDId)
  {
    case LCD_1:
      DMA_InitStruct.Channel = LL_DMA_CHANNEL_6;
      LL_DMA_Init (DMA2, LL_DMA_STREAM_4, &DMA_InitStruct);
      LL_DMA_EnableIT_TC (DMA2, LL_DMA_STREAM_4);
      NVIC_SetPriority (DMA2_Stream4_IRQn, NVIC_EncodePriority (NVIC_GetPriorityGrouping (), 3, pLCDId));
      NVIC_EnableIRQ (DMA2_Stream4_IRQn);
      break;
 
    case LCD_2:
      DMA_InitStruct.Channel = LL_DMA_CHANNEL_6;
      LL_DMA_Init (DMA2, LL_DMA_STREAM_5, &DMA_InitStruct);
      LL_DMA_EnableIT_TC (DMA2, LL_DMA_STREAM_5);
      NVIC_SetPriority (DMA2_Stream5_IRQn, NVIC_EncodePriority (NVIC_GetPriorityGrouping (), 3, pLCDId));
      NVIC_EnableIRQ (DMA2_Stream5_IRQn);
      break;
 
  } // End switch.

TC flags are cleared as soon as the IRQ ISR are called.

#if (VECTOR_TABLE == DYNAMIC)
  void ArcadeIT_Video_VGA_Stopline_Handler (void)
#elif (VECTOR_TABLE == STATIC)
void
DMA2_Stream1_IRQHandler (void)
#endif
{
  /*
   DESCRIPTION:  Interrupt service routine for the scanline renderer.
   PARMS:        See above.
   RETURNS:      Nothing.
   */
  uint8_t lError = 0;
 
  if (LL_DMA_IsActiveFlag_TE1(DMA2))
  {
    LL_DMA_ClearFlag_TE1(DMA2);
    lError |= 1;
 
  } // End if.
 
  if (LL_DMA_IsActiveFlag_FE1(DMA2))
  {
    LL_DMA_ClearFlag_FE1(DMA2);
    lError |= 2;
 
  } // End if.
 
  if (LL_DMA_IsActiveFlag_DME1(DMA2))
  {
    LL_DMA_ClearFlag_DME1(DMA2);
    lError |= 4;
 
  } // End if.
 
  if (LL_DMA_IsActiveFlag_TC1(DMA2))
  {
    LL_DMA_ClearFlag_TC1(DMA2);
 
    DMA2_Stream1->CR &= ~DMA_SxCR_EN; // Disable pixel DMA.
    TIM8->CR1 &= ~TIM_CR1_CEN; // Stop pixel clock.
    TIM8->DIER = 0;            // Update DMA request has to be disabled while zeroing the counter.
    TIM8->EGR = TIM_EGR_UG;    // Force an update event to reset counter. Setting CNT is not reliable.
    TIM8->DIER = TIM_DIER_UDE; // Re-enable update DMA request.
 
    // Render the pointer
    if ((gArcadeITFirmware.rgb_driver->scanline_sw >= gArcadeITFirmware.pointer->y) && (gArcadeITFirmware.rgb_driver->scanline_sw < (gArcadeITFirmware.pointer->y + gArcadeITFirmware.pointer->height)))
    {
      uint32_t lColor; uint8_t lColorIndex;
      uint16_t *lBufferData = (uint16_t*)gArcadeITFirmware.rgb_driver->line_buffer_render + (uint16_t)gArcadeITFirmware.pointer->x;
      uint8_t lWidth = gArcadeITFirmware.rgb_driver->width - gArcadeITFirmware.pointer->x >= gArcadeITFirmware.pointer->width ? gArcadeITFirmware.pointer->width : gArcadeITFirmware.rgb_driver->width - gArcadeITFirmware.pointer->x ;
      for (uint8_t lX = 0; lX < lWidth; lX++)
      {
        lColorIndex = gArcadeITFirmware.pointer->data_offset[lX];
        lColor = gArcadeITFirmware.rgb_driver->palette_show[lColorIndex];
        if (lColorIndex) lBufferData[lX] = RGB_TO_12BIT(((uint8_t*)&lColor)[1], ((uint8_t*)&lColor)[2], ((uint8_t*)&lColor)[3]);
 
      } // End for.
 
    } // End if.
 
  } // End if.
 
  if (lError)
  {
    __ARCADEIT_SERIAL_LOG("VGA DMA: Error %d!\r\n", lError);
    if (lError & 1) __ARCADEIT_SERIAL_LOG("  Transfer error\r\n");
    if (lError & 2) __ARCADEIT_SERIAL_LOG("  FIFO error\r\n");
    if (lError & 4) __ARCADEIT_SERIAL_LOG("  Direct mode error\r\n");
 
  } // End if.
 
} // End DMA2_Stream1_IRQHandler.
#if (VECTOR_TABLE == DYNAMIC)
  void ArcadeIT_LCD_Stopline_Handler (void)
#elif (VECTOR_TABLE == STATIC)
void
DMA2_Stream4_IRQHandler (void)
#endif
{
  /*
   DESCRIPTION:  Interrupt service routine for the LCD scanline renderer.
   PARMS:        See above.
   RETURNS:      Nothing.
   */
#if 0
    uint8_t lError = 0;
 
    if (LL_DMA_IsActiveFlag_TE4(DMA2))
    {
      LL_DMA_ClearFlag_TE4(DMA2);
      lError |= 1;
 
    } // End if.
 
    if (LL_DMA_IsActiveFlag_FE4(DMA2))
    {
      LL_DMA_ClearFlag_FE4(DMA2);
      lError |= 2;
 
    } // End if.
 
    if (LL_DMA_IsActiveFlag_DME4(DMA2))
    {
      LL_DMA_ClearFlag_DME4(DMA2);
      lError |= 4;
 
    } // End if.
#endif
 
    if (LL_DMA_IsActiveFlag_TC4(DMA2))
    {
      LL_DMA_ClearFlag_TC4(DMA2);
 
      DMA2_Stream4->CR &= ~DMA_SxCR_EN; 
      DMA2_Stream4->PAR = (uint32_t)(gArcadeITFirmware.lcd_driver[LCD_1]->buffer_show)
          + (gArcadeITFirmware.lcd_driver[LCD_1]->chunk * gArcadeITFirmware.lcd_driver[LCD_1]->chunk_size);
      DMA2_Stream4->NDTR = gArcadeITFirmware.lcd_driver[LCD_1]->chunk_size;
      DMA2_Stream4->CR |= DMA_SxCR_EN;
      
      gArcadeITFirmware.lcd_driver[LCD_1]->chunk++;
      if (gArcadeITFirmware.lcd_driver[LCD_1]->chunk == gArcadeITFirmware.lcd_driver[LCD_1]->chunks)
      {
        gArcadeITFirmware.lcd_driver[LCD_1]->chunk = 0;
        gArcadeITFirmware.lcd_driver[LCD_1]->render = FALSE;
 
      } // End if.
 
    } // End if.
 
#if 0
    if (lError)
    {
      __ARCADEIT_SERIAL_LOG("LCD 1 DMA: Error %d!\r\n", lError);
      if (lError & 1) __ARCADEIT_SERIAL_LOG("  Transfer error\r\n");
      if (lError & 2) __ARCADEIT_SERIAL_LOG("  FIFO error\r\n");
      if (lError & 4) __ARCADEIT_SERIAL_LOG("  Direct mode error\r\n");
 
    } // End if.
#endif
 
} // End DMA2_Stream4_IRQHandler.

I will try to drive the LCD DMA transfer with a Timer instead, like the VGA as if it has a pixelclock. Thanks for the suggestion.

Digimorf
Associate III
/*
VGA DRIVER DMA2D and DMA2 transfer
----------------------------------------------------------
SRAM1 160K, 0x20000000 - 0x20027FFF            GPIOB AHB1           
line_buffer_show    @0x20001900      --------> @0x40020414
line_buffer_render  @0x20001b88      -----'
 
----------------------------------------------------------
        App renders to
              |
              V
pRGBDriver->video_buffer_render                @0x20014e28
        (Swap buffers)
pRGBDriver->video_buffer_show                  @0x20002220
              |
            DMA2D (Paletted color to RGBA4444)
              |
              V
pRGBDriver->line_buffer_render                 @0x20001b88
        (Swap lines)
pRGBDriver->line_buffer_show                   @0x20001900
              |
  Tim8 -> DMA2 Stream1 
              V
          GPIOB AHB1                           @0x40020414
          
          
              
LCD DRIVER
-----------------------------------------------------------           
Block 3 - FMC Bank 1
gArcadeITFirmware.lcd_driver[0]->ptr_data      @0x61010000
 
Block 5 - FMC SDRAM
gArcadeITFirmware.lcd_driver[0]->buffer_show   @0xc0000000
-----------------------------------------------------------
 
LCD DMA2 transfer (LCD1/2)
AHB3        ------> AHB3
@0xc0000000         @0x61010000
SDRAM               LCD 8080 I
 
        App renders to
              |
              V
    buffer_show   @0xc0000000
              |
         DMA2 Stream5
              |      
              V
        LCD1 0x61010000
        
        
BUS ERROR TE1 as soon as Stream4/5 enabled
*/

No way, I get a BUS error on DMA2Stream1 as soon as the DMA2Stream4 or 5 start. The priority of DMA2Stream1 is VeryHigh, while DMA2Stream4/5 is Low.

It seems that the data transfer from AHB3 to AHB3 has a kind of exclusive and priority access to the DMA Bus.

Read out and post content of TIM and DMA registers - maybe at the moment when the error occurs, without clearing the error flags.

JW

Digimorf
Associate III

After a long research on Internet I bumped into this Errata for STM32F407.

See 2.1.10 page 15 of this PDF:

https://www.st.com/resource/en/errata_sheet/dm00037591-stm32f405-407xx-and-stm32f415-417xx-device-limitations-stmicroelectronics.pdf

Maybe my case is different but there is the DMA2 involved here so it's probably a similar issue. Maybe I'd better to find out a workaround that may fit the bill.