cancel
Showing results for 
Search instead for 
Did you mean: 

How to stream out data via USB HS with CDC Interface

alexfall
Associate

Hi,

I want to achieve following:

I want to read out an ADC (16-bit at 1MSPS) via SPI and stream those data to a Host PC with the Nucleo-F722ze and the USB_CDC_Interface.

The amount of data per second is to high (16Mbit/s) for USB_FS so I am using USB-HS with an external ULPI (USB3300 from Waveshare) .

I realized the SPI-Transfer, so I am getting one new 16-bit values each 1us. The next step was to transfer these values to a testBuffer via DMA...

Buffer-Declaration:

#define rxBufferLen 2048

volatile uint16_t testBuffer1[rxBufferLen];

My SPI_DMA Setup looks like this:

void DMA_Init_SPI_AE()
{
   /* enable DMA 2 clock */
   RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
 
   // Disable DMA to program the Registers
 
   DMA2_Stream0->CR &=~ DMA_SxCR_EN;
 
   DMA2_Stream0->CR |= (0x03 << DMA_SxCR_CHSEL_Pos) //Channel 3;
                  | (1 << DMA_SxCR_PL_Pos) // Priority middle
                  | DMA_SxCR_CIRC //circular mode used
                  | DMA_SxCR_MINC //memory: increment
                  | (1 << DMA_SxCR_MSIZE_Pos) // memory data size = half-word(16-bit)
                  | (1 << DMA_SxCR_PSIZE_Pos) // peripheral data size = half-word(16-bit)
                  | (0 << DMA_SxCR_DIR_Pos); // peripheral to memory mode
   DMA2_Stream0->PAR = (uint32_t) &SPI1->DR; //peripheral adress is the SPI Data Register
   DMA2_Stream0->M0AR = (uint32_t) testBuffer1; //memory adress for data to be stored
   DMA2_Stream0->NDTR = rxBufferLen;
   DMA2_Stream0->FCR &= ~ DMA_SxFCR_DMDIS; // enable direct mode
 
 
   // enable dma Interrupts
 
   HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 1, 5); //DMA IRQ lower prioriy then USB
   HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
 
 
   //use transfer complete, half-transfer complete and transfer error interrupt
 
   DMA2_Stream0->CR |= DMA_SxCR_HTIE
               | DMA_SxCR_TCIE
               | DMA_SxCR_TEIE;
 
 
}

I am using Half Transmit and Transmit Complete Interrupts.

When the half Transmit Complete Interrupt occurs I want to send the data in the first half of the testBuffer via USB to the Host Laptop, while DMA fills second half of the testBuffer

When the Transmit Complete Interrupt occurs I want to send the data in the second half of the testBuffer via USB to the Host Laptop, while DMA filss first half of the testBuffer

The Interrupts Routines look like this:

void DMA2_Stream0_IRQHandler(void)
{
   //DMA half transfer complete
 
   if(DMA2->LISR & DMA_LISR_HTIF0){
      DMA2->LIFCR |= DMA_LIFCR_CHTIF0;
 
      CDC_Transmit_HS((uint8_t*) &testBuffer1[0], rxBufferLen/2);
   }
 
 
   //DMA transfer compete
 
   if(DMA2->LISR & DMA_LISR_TCIF0){ //check if interrupt occured
      DMA2->LIFCR |= DMA_LIFCR_CTCIF0; // clear interrupt transfer completed flag
 
      CDC_Transmit_HS((uint8_t*) &testBuffer1[rxBufferLen/2], rxBufferLen/2);
   }
 
   //DMA transfer error
 
   if(DMA2->LISR & DMA_LISR_TEIF0){ // check if transfer error interrupt occured
      DMA2->LIFCR = DMA_LIFCR_CTEIF0;
 
      dma_error = 1;
   }
}

in the while(1) I am doing nothing because everything is handled by interrupts.

Whenever run this programm my Laptop says: "Unknown USB Device (Device Descriptor Request Failed)

I already tried to change Heap size, and I even tried what happens if I enable I-Chache, but nothing works.

My assumption is, that USB cannot handle the speed of the DMA. Is this Assumption correct?

If so what would I need to do to make this Programm work?

I thought of transmitting the received SPI-Data via DMA to the USB-Tx-Fifo... not sure if that would help, but that code did not work ether.

I also set up a test USB-Transfer in the while(1) that works:

int main (void)
{
   for(uint16_t i=0; i<2048; i++){
       txBuffer1[i] = i;
}
 
while(1)
{
   CDC_Transmit_HS((uint8_t*)testBuffer1, rxBufferLen);
}

If I run this code everything works.

I left out all the Initialzisation stuff, but if it is needed I can share the whole code in here.

Any help is appreciated.

Thanks,

Alex

1 REPLY 1
TDK
Guru

> My assumption is, that USB cannot handle the speed of the DMA. Is this Assumption correct?

The PC says the device descriptor failed, so that's the problem. Adding a 500ms delay after initialization but before using the USB can help. The ST USB middleware library is not the best, might be an uphill battle.

Signal integrity between the MCU and the external PHY might be an issue. I'm fairly sure there are some ST boards with a USB3300.

Program in steps. Worry about getting it working at all before you try to stream at 16 Mbps. Note that once you do get the STM32 side working, the PC side needs to hold up its end of the bargain as the hardware buffers are relatively small. I would imagine you will need a buffer on the STM32 side to handle hiccups/delays on the PC side unless you are okay with losing data mid stream.

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