cancel
Showing results for 
Search instead for 
Did you mean: 

implement maximum speed adc to usb transfert

julien12657
Associate III

I am trying that :

sprintf((char*)buffer,"%d\r",HAL_ADC_GetValue(&hadc));

CDC_Transmit_FS(buffer,8);

but the speed is only 3 or 4kS/s

Someone has something better ?

53 REPLIES 53
julien12657
Associate III

So the only thing I understand is that HAL_ADC_GetValue() is a 32-bit value with only the 12 LSB which are significant

so for example it can be 0000 0000 0000 0000 0000 1011 0110 1110 which means that the decimal value is 2926.

so if the reference voltage is 3.300 V the voltage I read is 3.300*(2926/4096)=2.357 V

but what should I store in my array (or buffer), should I store the "0" and the "1" and how ?

julien12657
Associate III

Hi guys, tonight is for me a great night, I think I have the solution. Is it that :

uint8_t buffer[32];

 while (1)

 {

      val =HAL_ADC_GetValue(&hadc);

      for (int i=0;i<32;i++)

      {

 buffer[i]=val<<(32-i);

      }

CDC_Transmit_FS((uint8_t*)buffer,32);

Bob S
Principal

Not quite.

How long have you been programming "C", or *any* embedded CPU? I don't intend this as an insult, just an honest question as you seem to be confused by binary values versus text strings.

Go back to

julien12657
Associate III

I am programming embedded CPU for less than two years but not every days because I am more a physicist who's trying to make its own acquisition cards.

Bob S
Principal

ARRRRGGG!!! Hit the wrong button :(

Not quite. All that does is take a 32-bit (i.e. 4 byte) binary value and change it into a 32-byte series of binary 1's and 0's.

How long have you been programming "C", or *any* embedded CPU? I don't intend this as an insult, just an honest question as you seem to be confused by binary values versus text strings.

First question: Do you want to send human-readable text or binary data?

If you want to send human-readable text, you need to use sprintf() to translate the BINARY data from HAL_ADC_GetValue() into an ASCII text string. I'll give a hint based on your original code that sends one value at a time.

// For now we will only use the first 8 bytes of this array.  The entire array
// will (or may) be used when sending multiple samples at once.
char buffer[64];
// NOTE: Using snprintf() instead of sprintf() to ensure no buffer overflow!!
// This snprintf() call stores at most 7 bytes of ASCII data including the "\r"
// PLUS A TRAILING NULL. The value will be paddded with spaces on the
// left as needed, ex:
// "  2343\r"
snprintf( buffer, sizeof(buffer), "%6d\r", HAL_ADC_GetValue(&hadc) );
// Do not send the trailing NULL, only send 7 bytes (data plus "\r")
CDC_Transmit_FS( buffer, 7 );

If you want to send BINARY data, go back to @Community member​ original post. In a simple example that, as your initial code, only sends one data value at a time:

// buffer is declared as an array for future expansion.
// For now we only use the first element.
uint16_t buffer[32];
buffer[0] = HAL_ADC_GetValue(&hadc);
// This sends only the first element of buffer[], which is 2 bytes long)
// Hint: when using the entire buffer array, the 2nd param will change
// to "sizeof(buffer)".
CDC_Transmit_FS( (uint8_t *)buffer, sizeof(uint16_t) );

Once you have either of these working, then see if you can expand them to store multiple values in buffer[] and send the buffer when it is full.

julien12657
Associate III

So if I want the maximum speed in human-readable text, it is :

char buffer[64];
  while (1)
  {
 
		snprintf( buffer, sizeof(buffer), "%63d\r", HAL_ADC_GetValue(&hadc) );
    CDC_Transmit_FS((uint8_t*)buffer,64);
 
 
  }

is that right ?

Bob S
Principal

Nope on 2 counts. First, all that does it send one sample that is 63 printable characters wide (lots of spaces before the digits). Second, this will truncate the "\r" character because your buffer size is 64 bytes, you tell snprintf() to write no more than 64 bytes INCLUDING THE TRAILING NULL (so it only writes 63 bytes of text then adds a NULL char).

I have a soft spot for physicists as I married one :) So here's another hint: you need to create a loop to fill up the buffer. There are two ways you can do this.

First, requires more "moving data around" but may be clearer in its intention:

// WARNING: THIS CODE DOES NOT SHOW CODE NEEDED TO
// MAKE SURE THAT THE USB INTERFACE IS READY TO SEND
// MORE DATA.  Either check the return value from 
// CDC_Transmit_FS() and retry while is says "busy", or
// poll the USB status before trying to send and check for
// "not busy".
int i;
char buffer[64];
 
while(1) {  // Do this forever
   // Each data is 7 ASCII characters including the "\r" char
   // and NOT including the trailing NULL.  So we can fit
   // 9 data values in 64 bytes.
   buffer[0] = '\0';  // Initialize buffer to an empty "C" string
   for ( i = 0; i < 9; i++ ) {
      char temp[8];
      snprintf( temp, sizeof(temp), "%6d\r", HAL_ADC_GetValue(&hadc) );
      strcat( buffer, temp );
   } // end for()
   CDC_Transmit_FS( buffer, (9*7) );  // 9 each 7 char strings
} // end while()(

The second skips the intermediate temp[] variable and the associated strcpy() call and has snprintf() write directly into the buffer[] starting every 7 bytes. Left as an exercise for the student :)

julien12657
Associate III

Bob,

Thanks for your kind response.

I've tried your last code (with the for loop) but it seems to be quiet slow. I am cheeking roughly the speed of the acquisition by retrieve a sine wave from a waveform generator and plot it with LabVIEW (I can increase the frequency of the sine wave and cheek the moment I can't recover it anymore, this gives me roughly the frequency sample. I find that your last code, even if it works well, is slower than this one :

    // For now we will only use the first 8 bytes of this array.  The entire array
    // will (or may) be used when sending multiple samples at once.
    char buffer[64];
    // NOTE: Using snprintf() instead of sprintf() to ensure no buffer overflow!!
    // This snprintf() call stores at most 7 bytes of ASCII data including the "\r"
    // PLUS A TRAILING NULL. The value will be paddded with spaces on the
    // left as needed, ex:
    // "  2343\r"
    snprintf( buffer, sizeof(buffer), "%6d\r", HAL_ADC_GetValue(&hadc) );
    // Do not send the trailing NULL, only send 7 bytes (data plus "\r")
    CDC_Transmit_FS( buffer, 7 );

Is there something I missed ?

Bob S
Principal

Whoops - the code I posted never checks for new data available from the ADC. It simply reads the ADC data register and writes the value to the buffer as fast as it can. Depending on the sample rate you have the ADC programmed for, running my sample code you should be seeing lots of repeated values from the ADC. This maybe could end up flooding the USB interface and delay getting new data.

I don't know how you have the ADC configured, or what causes the ADC to initiate taking a sample. You should add some code before the HAL_ADC_GetValue() call that checks for the EOC bit set. Presuming that you are not using DMA or interrupt mode (since you are using HAL_ADC_GetValue()), then add this code before the snprintf() and HAL_ADC_GetValue() call:

// This waits a max of 1 second for a new ADC sample.
// Should be WAAAAAY more than enough time.
while ( HAL_ADC_PollForConversion(&hadc,1000) != HAL_OK ) {
   // do nothing???? For real production code if you ever get
   // not OK (i.e. HAL_ERROR or HAL_TIMEOUT) you should flag
   // this as an error and report it somehow, or reconfigure things,
   // or something.
}
// Now do the snprintf() with new ADC data returned from HAL_ADC_GetValue()

Bob S
Principal

One more thing - make sure you have some code before the CDC_Transmit_FS() call to make sure the USB interface is ready to accept another buffer full of data. I alluded to this earlier but didn't include anything in my code. The USB library for the chip I'm using doesn't have a CDC_Transmit_FS() function. Presuming that it returns HAL_OK, HAL_BUSY or some other error, you could loop calling this function the same way I looped on the HAL_ADC_PollForConversion() call - i.e. loop while CDC_Transmit_FS() != HAL_OK. Though a better way would be to find a polling function that tells you that it is OK to send more data.