cancel
Showing results for 
Search instead for 
Did you mean: 

Help resolve issue for sending data to PC via USB CDC FS Communication

SMour.2
Associate III

Hello All,

I'm really stuck with USB CDC Communication speed using STM32F405 for past 20 days.

I'm trying to send ADC Data to PC . My ADC sampling rate is 32khz. My ADC is 8 channel , but I'm only sending 2 channel data to PC.

The ADC Data ready pin is connected to GPIO pin of the stm32 and it toggles at the rate of 32khz . So it toggles 32000 times per second and inside every toggle I read samples from the ADC. Below is the code I've written to capture data and send data to PC. I'm using FREE RTOS in my code. Total bytes that need to be send is 

32000 samples x 2 channel x 3 bytes per sample = 192000 bytes. I have set the USB data packet size to 64 bytes and after sending one packet I'm sending a zero length packet.

 

void StartDefaultTask(void *argument)
{
  /* init code for USB_DEVICE */
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 5 */
  ADS_Init(); 
j=0;
  /* Infinite loop */
  for(;;)
  {
		  if(dataready == 1) // this is set in DRDY PIN TOGGLE INTERRUPT
		 	 	  {
		 	 		  ADS_RDATA();
		 	 		  dataready = 0;

		 	 	  }

    osDelay(1);
  }
  /* USER CODE END 5 */
}
void sendata(void *argument)
{
  /* USER CODE BEGIN sendata */
  /* Infinite loop */
  for(;;)
  {
	  if(datatype == 't')
	  		  {
	  	  	  if(senddat == 1)
	  	  	  {
	  	  	  CDC_Transmit_FS(buff_tx, sizeof(buff_tx));
	  	  	  CDC_Transmit_FS(ZLP, 0);
	  	  	  senddat=0;
	  	  	  }
	  	  	  }

	  if(datatype == 'z')
	 	  	  {
	 	  		senddat=0;
	 	  	  }
    osDelay(1);
  }

//read data
void ADS_RDATA() {				//  use in Stop Read Continuous mode when DRDY goes low
	uint8_t inByte,inByte1,inByte2,inByte3;
	int i;
	//int nchan = 2;	//assume 8 channel.  If needed, it automatically changes to 16 automatically in a later block.

	stat_1 = 0;							//  clear the status registers
	

	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12,GPIO_PIN_RESET); 					//  open SPI
	transferSPI( _RDATA);

	// READ CHANNEL DATA FROM FIRST ADS IN DAISY LINE
	for(i = 0; i < 3; i++){			//  read 3 byte status register (1100+LOFF_STATP+LOFF_STATN+GPIO[7:4])
		inByte = transferSPI( 0x00);
		stat_1 = (stat_1<<8) | inByte;
	}

	for(i = 0; i < 2; i++) // this is where i read ADC DATA of two channels. 3 Bytes are read for each channel and stored in buff_tx array of size 64 bytes.
		{

		buff_tx[j]=transferSPI( 0x00);
	j++;
		buff_tx[j]=transferSPI( 0x00);
	j++;
		buff_tx[j]=transferSPI( 0x00);
	j++;

	}
	
	if(j==60)
		{
			j=0;
			senddat = 1;
		}
	
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12,GPIO_PIN_SET); 					//  open SPI


}

 

Since my data is not in multiple of 64 , I only store ADC Data upto 60 elements of the array and rest elements are always written 0. In my PC application I only read first 60 bytes of the received packet.

I'm not able to get 192000 samples in one second. It takes around 7-8 seconds to read 192000 samples. Kindly help.

 

 

14 REPLIES 14
Pavel A.
Evangelist III

> I have set the USB data packet size to 64 bytes and after sending one packet I'm sending a zero length packet.

>Since my data is not in multiple of 64 , I only store ADC Data upto 60 elements of the array and rest elements are always written 0. In my PC application I only read first 60 bytes

Isn't this waste? why not to send a short packet (60 bytes) instead of two (64 bytes+ ZLP)?

What if you reduce the data before sending? (like filtering, removing redundant values....)

 

TDK
Guru

>      CDC_Transmit_FS(buff_tx, sizeof(buff_tx));
>      CDC_Transmit_FS(ZLP, 0);

CDC_Transmit_FS does not finish instantly. You'll need to buffer data and send it out after each communication is complete. USB is subject to delays of tens of ms which you will need to smooth out.

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

Hi Pavel,

I thought the maximum speed for USBCDC would be achieved when the data packet is size of 64 bytes.

the data is 3 bytes / sample. all the data processing in done in PC Application. I have to send raw ADC data to the PC.

SMour.2
Associate III

How much data do I need to buffer before sending? If USB delay is in tens of ms , then what data size do I need to send to smooth out the delay.

Calculate your data rate and create a buffer that stores, say, 100 ms worth of data. You can send more than 64 bytes at once, it will be more efficient. I would suggest sending 4096 or 8192 bytes per transfer.

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

I have set heap size as 0x800 and stack size as 0x400.

The Stm32 code will handle array of size 4096? Should I increase the stack size?

Pavel A.
Evangelist III

USB device can send only when the host asks it to (polls) and only by chunks less or equal to max. data size for endpoint (which is limited to 64 bytes for FS devices). The ST USB driver can accept buffer size > endpoint size, in this case it automatically forwards the data pointer and sends out the next full-size chunk when the host polls. The last chunk can be short (less then the endpoint size). ZLPs should be sent explicitly. CDC_Transmit_FS cannot be called again with new data until the previous call completed with status other than BUSY. I don't remember off top of my head how the ST driver signals completion of send (besides periodically calling CDC_Transmit_xxx). But the way to go is to wait for this signal - which comes from the USB interrupt handler. Waiting for any fixed amount of time is not reliable. Of course, consider that the connection to host can break anytime (cable disconnect, reset request from host, etc.)

SMour.2
Associate III

I understand that more amount of data I send at one time the better speed efficiency  I will get. I now collect 19200 bytes of data from the ADC in an array i.e 100ms of data and then set a flag so that my FREE RTOS task sends the data as soon it detects the boolean flag true.

My MCU code has three major functions shown below.

  1. default task is continuously look for ADC Data ready interrupt.
  2. ADS_RDATA is the function inside which the data is read and stored in a buffer of 19200 bytes. once the buffer is full I set a flag of sendat =1 
  3. senddata task keeps checking if the sendat flag is 1. if it is, it transmits the buffer data and resets the flag.Also datatype is the variable which gets its value from the PC side to start or stop sending the data.

 

The issue at PC side is I get the First data packet of 19200 bytes instantly , but if I check for double of data packet i.e 38400, it takes 2-3 three seconds to reach that count. I'm sending the data from MCU Continuously after every 100ms interval.

void ADS_RDATA() {				//  use in Stop Read Continuous mode when DRDY goes low
	uint8_t inByte,inByte1,inByte2,inByte3;
	int i;
	

	stat_1 = 0;							//  clear the status registers
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12,GPIO_PIN_RESET); 					//  open SPI
	transferSPI( _RDATA);

	// READ CHANNEL DATA FROM FIRST ADS IN DAISY LINE
	for(i = 0; i < 3; i++){			//  read 3 byte status register (1100+LOFF_STATP+LOFF_STATN+GPIO[7:4])
		inByte = transferSPI( 0x00);
		stat_1 = (stat_1<<8) | inByte;
	}

	for(i = 0; i < 2; i++)
		{

		buff_tx[j]=transferSPI( 0x00);
	    j++;
	    buff_tx[j]=transferSPI( 0x00);
	    j++;
	    buff_tx[j]=transferSPI( 0x00);
	    j++;

	}
	
	if(j==19200)
		{
			j=0;
			senddat = 1;
		}
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12,GPIO_PIN_SET); 					//  open SPI


}

void StartDefaultTask(void *argument)
{
  /* init code for USB_DEVICE */
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 5 */
  ADS_Init();
  senddat=0;
j=0;
  /* Infinite loop */
  for(;;)
  {


		  if(dataready == 1) // ADC DRDY Interrupt
		 	 	  {
		 	 		  ADS_RDATA();
		 	 		  dataready = 0;

		 	 	  }

    osDelay(1);
  }
  /* USER CODE END 5 */
}

void sendata(void *argument) // this is task in which I'm sending data
{
  /* USER CODE BEGIN sendata */
  /* Infinite loop */
  for(;;)
  {

	   while(datatype == 't') // to tell the MCU to start sending data.
	  	  	  		  {
	  	  	  if(senddat == 1)
	  	  	  {
	  	  		if(((USBD_CDC_HandleTypeDef*)(hUsbDeviceFS.pClassData))->TxState==0)
	  	  		{
	  	  		CDC_Transmit_FS(buff_tx, sizeof(buff_tx)) ;
	  	  	    //CDC_Transmit_FS(ZLP, 0);
	  	  	     }	  	  	  
	  	  	  senddat=0;
	  	  	  }
	  	  	  		  }

	  if(datatype == 'z') // to stop sending data.
	 	  	  {
	 	  		senddat=0;
	 	  		for(uint8_t k=0;k<19200 ; k++)
	 	  		{
	 	  		buff_tx[k]=0;
	 	  		}
	 	  	  }
    osDelay(1);
  }

 

SMour.2
Associate III

Also,

/* USER CODE BEGIN EXPORTED_DEFINES */
/* Define size for the receive and transmit buffer over CDC */
/* It's up to user to redefine and/or remove those define */
#define APP_RX_DATA_SIZE 2048
#define APP_TX_DATA_SIZE 1920

these are changes I have done inside usb_cdc_if.h file