cancel
Showing results for 
Search instead for 
Did you mean: 

streaming of buffer that fills with DMA over USB_FS

esukh
Associate III

I encountered a complex problem and I don't understand where should I start untangle it.

What I want to do:

I have a buffer (allocated in SRAM_1) that filling up with DMA. DMA pass TIM2_CCR1 value into buffer's cells.

Flow:

unsigned int dma_buffer[15360*4];

DMA1_Stream0->NDTR = 15360*4;

TIM2 input capture timer see event (high transition or low transition in my case I'm capture both), update CCR1 and kicks in DMA, that pass this(TIM2->CCR1) into dma_buffer, Increment addres, when next event occurs pas another value and so far and so forth.

when dma_buffer is half filled (thus (DMA1_Stream0 ->NDTR > 15360*2) -> half of the buffer length).

I want to transfer this half of buffer via USB_FS, the fastest way I found is:

for(int i = 0; i < 2; ++i) {
        //SCB_InvalidateDCache();
        CDC_Transmit_FS(buffer+SIZE*i, SIZE*sizeof(unsigned int));
        while(((USBD_CDC_HandleTypeDef*)(hUsbDeviceFS.pClassData))->TxState != 0);
} // transfer via USB first half of buffer
 

and when DMA interrupt occurs I set flipFlag

void DMA1_Stream0_IRQHandler(void) {
  
  // clear interrupt flag
  DMA1->LIFCR |= (0x1 << 3) | (0x1 << 5);
  dmaFlag++;
  flipFlag++; // <-- indicates that second half of dma_buffer is already filled and we can transfer it
  
}

after I transfer second part of dma_buffer:

for(int i = 2; i < 4; ++i) {
        //SCB_InvalidateDCache();
        CDC_Transmit_FS(buffer + SIZE*i, SIZE*sizeof(unsigned int));
        while(((USBD_CDC_HandleTypeDef*)(hUsbDeviceFS.pClassData))->TxState != 0);
} // transfer second part of dma_buffer

the problem:

I have inconsistent data transferred like

first 15360 buffer entries 15360-15360*2 15360*2 - 15360*3 15360*3 - 15360*4

1-2-3-4 |5-6-7-8 |9 - 32 - 33 - 34 |35-36-37-38

the first two data chunks of SIZE(15360*sizeof(unsigned int)) are transferred correctly (first hald of dma_buffer).

but the third data chunk of SIZE starts correctly but soon became incoherent with the rest of data, it's like just some big number was added to.

Real life example:

dma_buffer[0] = 145 dma_buffer[1] = 604 dma_buffer[2] = 1103 dma_buffer[3] = 1604

......

dma_buffer[15360] = 7680113

......

dma_buffer[30720] = 15360115

.......

dma_buffer[31166] = 15583113

dma_buffer[31167] = 25692048 --> approx 10E6 incrementation ERROR

dma_buffer[31168] = 25694103

dma_buffer[31168] = 25694604 --> same ~500 delta from previous and starts going to the end

Any idea why is this happening? Both cache enabled (usb and uart transfer didn't seem to work without) I was trying invalidate cache before any data usages, but problem stays.

I have some ideas in my head:

  1. USB transfer can't share memory area with DMA that writing to that memory
  2. DMA halting until USB finish it transfer and but in that time CCR1 register is still getting new values so and after USB finish DMA starts writing that value to my buffer and that causes data incoherence
  3. Somewhat cause data corruption in dma_buffer but only highest bits not lowest

My experience in this fild is HIGHLY limited so I just don't know where to start, so any ideas and recommendations will be highly appreciated.

2 REPLIES 2

The USB transmission lasts too long, so the buffer gets overwritten by DMA while transmitted.

JW

esukh
Associate III

This happens even with the situation where DMA circular buffer mode enabled, so DMA should only fill buffer once. I have simple code like:

  my_DMA_init();
  my_TIM2_initInputCaptureTimer();
  my_initTIM6();
  
  unsigned int circ_buffer_ptr = 0;
  unsigned int counter = 0;
  unsigned int stopFlag = 0;
  while (1)
  {
    if(!stopFlag) {
      for(int i = 0; i < 2; ++i) {
        writeFile();
      }
      counter = 1;
    }
      
    if(counter == 1) {
      counter = 0;
      stopFlag = 1;
      // end of transmission
      
      //writeFile();
      //printDebugBuffer();
      
      buffer[0] = 0x00646E65;
      CDC_Transmit_FS(buffer, SIZE*sizeof(unsigned int));
    }
  }// while
}// main

this code doesn't wait for DMA filling first half of buffer, It's just fire up immediately. And what I have:

values in buffer was preinitialized for debug purposes from 0 to 61439,

DMA circular buffer mode OFF - when dma fill up buffer it stops

what do i have in my first file -> CDC_Transmit_FS just transfer full buffer without waiting

dma_buffer[0] = 145

dma_buffer[1] = 604

dma_buffer[2] = 2

dma_buffer[3] = 3

.....

dma_buffer[61439] = 61439

but then it transfer this buffer again (filled in DMA) and I have this

dma_buffer[0] = 145

dma_buffer[1] = 604

dma_buffer[2] = 10354526

dma_buffer[3] = 10354672