2021-10-01 01:30 PM
Dear all,
I try to read out ADC-Data, that is streamed from an external adc (AD7768) out to the uC. What I want to do is to more or less directly stream out the data via USB CDC.
Therefore, I planned to use the DMA between the SPI and USB. However, this doesn't really work consistently. It seems that there may be a timing problem and after some seconds to minutes the transmission via USB stops.
At the moment I only start the HAL_SPI_Receive_DMA before while(1) and again in the receive complete callback before the USB_CDC_TX is called. Another strange thing is, that as soon as I do something inside the USB_Transmit_complete function, for example a HAL_Delay, the code stops here again.
Is there anything I missed? Is it even possible to use USB with DMA and SPI this way?
I also tried it with Interrupts, in this case the Code doesn't stop. However, only as long as I don't put anything in the CDC_Transmit_Complete function.
I am looking forward to your responses.
Thank you a lot in advance
Best regards
Ben
#define usb_packet_size 100
#define nbr_of_bytes 16
uint8_t adc_buffer[4][nbr_of_bytes*usb_packet_size+2];
uint8_t current_buffer_pos = 0;
uint16_t current_data_pos = 0;
int main(void){
//inits etc...
adc_buffer[0][nbr_of_bytes*usb_packet_size+0] = 0x0d;
adc_buffer[0][nbr_of_bytes*usb_packet_size+1] = 0x0a;
adc_buffer[1][nbr_of_bytes*usb_packet_size+0] = 0x0d;
adc_buffer[1][nbr_of_bytes*usb_packet_size+1] = 0x0a;
adc_buffer[2][nbr_of_bytes*usb_packet_size+0] = 0x0d;
adc_buffer[2][nbr_of_bytes*usb_packet_size+1] = 0x0a;
adc_buffer[3][nbr_of_bytes*usb_packet_size+0] = 0x0d;
adc_buffer[3][nbr_of_bytes*usb_packet_size+1] = 0x0a;
HAL_SPI_Receive_DMA(&hspi1, &adc_buffer[current_buffer_pos][current_data_pos], nbr_of_bytes);
while (1)
{
}
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
if (current_data_pos < 16 * (usb_packet_size - 1)) {
current_data_pos += 16;
HAL_SPI_Receive_DMA(&hspi1,
&adc_buffer[current_buffer_pos][current_data_pos], 16);
}
else {
current_data_pos = 0;
uint8_t current_buffer_cpy = current_buffer_pos;
if (current_buffer_pos == 3) {
current_buffer_pos = 0;
} else {
current_buffer_pos++;
}
HAL_SPI_Receive_DMA(&hspi1,
&adc_buffer[current_buffer_pos][current_data_pos], 16);
CDC_Transmit_HS(&adc_buffer[current_buffer_cpy][0],
16 * usb_packet_size + 2);
}
}
Solved! Go to Solution.
2021-10-02 08:38 AM
In my opinion there is too much stuff inside HAL_SPI_RxCpltCallback. The best approach is to call HAL_SPI_Receive_DMA and make it run endlessly in circular mode, then perform data pumping using DMA MEM2MEM to safe memory location, which is sent out to USB in main while loop. If you use two callbacks:
HAL_SPI_RxHalfCpltCallback
HAL_SPI_RxCpltCallback
and MEM2MEM transfer inside each of them, something like that:
HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream5,
(uint32_t)&someBuffer[0],
(uint32_t)&someBufferSafe[0],
sizeof(someBuffer)/2/4);
HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream5,
(uint32_t)&someBuffer[SOME_BUFFER_SIZE/2],
(uint32_t)&someBufferSafe[SOME_BUFFER_SIZE/2],
sizeof(someBuffer)/2/4);
Note that someBufferSafe may by much larger than someBuffer, and you can implement additional indexes (for storing, and sending). Advantage of using mem2mem dma transfer is that it minimizes time spent in interrupt to minimum, and allows you to perform more efficient buffering to send data further over USB. Circular mode eliminates waste of time by calling HAL_SPI_Receive_DMA. Safe buffer stored data index may be updated using mem2mem callback function, for example
void myDMA_Callback(DMA_HandleTypeDef *_hdma)
{
... you know when mem2mem is done, update indexes, flags, etc. for use in main loop
}
HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream5,HAL_DMA_XFER_CPLT_CB_ID,myDMA_Callback);
Then you can analyze those indexes in main loop and send out using CDC_Transmit_HS, without creating congestion inside interrupt callback functions.
2021-10-02 08:38 AM
In my opinion there is too much stuff inside HAL_SPI_RxCpltCallback. The best approach is to call HAL_SPI_Receive_DMA and make it run endlessly in circular mode, then perform data pumping using DMA MEM2MEM to safe memory location, which is sent out to USB in main while loop. If you use two callbacks:
HAL_SPI_RxHalfCpltCallback
HAL_SPI_RxCpltCallback
and MEM2MEM transfer inside each of them, something like that:
HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream5,
(uint32_t)&someBuffer[0],
(uint32_t)&someBufferSafe[0],
sizeof(someBuffer)/2/4);
HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream5,
(uint32_t)&someBuffer[SOME_BUFFER_SIZE/2],
(uint32_t)&someBufferSafe[SOME_BUFFER_SIZE/2],
sizeof(someBuffer)/2/4);
Note that someBufferSafe may by much larger than someBuffer, and you can implement additional indexes (for storing, and sending). Advantage of using mem2mem dma transfer is that it minimizes time spent in interrupt to minimum, and allows you to perform more efficient buffering to send data further over USB. Circular mode eliminates waste of time by calling HAL_SPI_Receive_DMA. Safe buffer stored data index may be updated using mem2mem callback function, for example
void myDMA_Callback(DMA_HandleTypeDef *_hdma)
{
... you know when mem2mem is done, update indexes, flags, etc. for use in main loop
}
HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream5,HAL_DMA_XFER_CPLT_CB_ID,myDMA_Callback);
Then you can analyze those indexes in main loop and send out using CDC_Transmit_HS, without creating congestion inside interrupt callback functions.
2021-10-06 07:48 PM
Hi!
Thank you so much for your detailed answer! It workes like a charm :).
Incredible easy!
Best regards
Ben
2023-11-03 09:36 PM
Hi Ben,
I am having a similar project. Would you kindly share your source code?
Thanks a lot!