2021-11-29 02:40 AM
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
2021-11-30 03:03 PM
> 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.