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
S.Ma
Principal

What is the ADC sampling rate? Is it a single channel?

Bob S
Principal

OK, you are now using DMA, good. But this code is essentially the same as your very first post. You aren't checking to see if there is new data in ADC_buffer, you just blindly read and re-read from the buffer. So you are still probably sending the same data multiple times over the USB interface. And you are still only sending one sample per call to CDC_Transmit_FS().

First - make your ADC_buffer LARGE. I'm too lazy to look up possible clock frequencies for your CPU, so I don't know what your sample rate is. But something like 500 (or a nice power of 2 for 512, or even 1024) should handle things. And please please use a #define for your buffer size, like this:

#define ADC_BUFFER_SIZE     1024
 
int ADC_buffer[ADC_BUFFER_SIZE];

This makes is easier to configure the DMA (HAL_ADC_Start_DMA), and when you need to use the buffer size to calculate where the DMA is writing data, should you ever change the size of your buffer. With the #define you only need to change the value in one place.

Second - Before you do your snprintf() with data from ADC_buffer, you need to check to see if new data is in the ADC_buffer. You do this by declaring your own index into the array that points to the next data to extract (initially == 0), and using the DMA channel's CNDTR to calculate where the DMA last wrote data into the array. Since CNDTR is the number of data remaining to transfer (starts at sizeof your buffer then decrements), (BUFFERSIZE - CNDTR) is the index at which DMA will store the NEXT data. So if your "extract" index != (BUFFERSIZE - CNDTR), there is data available. Read the data from your "extract index", increment your extract index and wrap it to zero if it is >= your buffer size.

If what I wrote above is confusing, search for the UART DMA examples on this forum for code that checks if data is available (probably/hopefully search terms UART DMA circular).

Get this working so that you no longer send the same data sample multiple times. Then...

Third - once you get code that determines when a new sample is available, your should change your code so that it sends more than one data sample per call to CDC_Transmit_FS(). I posted code for this before in this thread (see post that uses strcat() for a functional but not optimized version).

S.Ma
Principal

uint8_t Buffer[64];

uint16_t ADC_Value_16bit = HAL_ADC_GetValue(&hadc) >> 0; // too bad all the data are not stored in a FIFO... ADC can convert at 1 MHz, USB sends a single message every 1 msec (1kHz) if Full Speed.

Buffer[0] = ADC_Value_16bit >> 8; // MSB

Buffer[1] = ADC_Value_16bit & 0xFF; // LSB

Buffer[2] = 0; // we don't have other ADC values to send... too bad for the thoughtput (1msec per USB message by host)

Buffer[3] = 0; //

CDC_Transmit_FS(Buffer,8); // why 8?

This is sending raw bytes by USB CDC. They will show up as garbage data if shown as ASCII text on a teraterm console. This would be normal.

julien12657
Associate III

The frequency clock of the ADC is 14 MHz and I am using a sampling time of 239.5 cycles (the maximum).

So according to the datasheet, the total sampling time is 239.5+12.5=252 cycles. So it gives a frequency sampling of 55 kHz (of course this frequency sampling can be increased to 1 MHz by choosing a sampling time of 1.5 cycles)

@KIC8462852 EPIC204278916

You are using the line of code

uint16_t ADC_Value_16bit = HAL_ADC_GetValue(&hadc) >> 0

which means that the value of HAL_ADC_GetValue(&hadc) is right-shiffted by 0, so in practice it does nothing. why are you using this line of code ?

I understand this two lines :

Buffer[0] = ADC_Value_16bit >> 8; // MSB

Buffer[1] = ADC_Value_16bit & 0xFF; // LSB

Which means that you put the MSB of the first sample in Buffer[0] and the LSB of the first

sample in Buffer[1].

But then how to put the MSB of the second sample in Buffer[3] and the LSB of the second sample in Buffer[4], until Buffer[1024].

Do we have to check for a flag ?

Then let's supposed the Buffer is filled with 1024 bytes, all I have to do is :

CDC_Transmit_FS(Buffer,1024);

Right ? provided I have declared as mentionned by Bob :

#define ADC_BUFFER_SIZE    1024

Then how to be sure that no sample will be missed while the USB is transmiting the data ? I mean : it takes time for the USB to transmit the 1024 byte and during this time, some samples are not taken right ? so there is a "hole" in the signal transmitted ?

S.Ma
Principal

What is the adc sample per second rating?

julien12657
Associate III

55 000 samples per second

julien12657
Associate III

Hi guys,

I've configured the ADC with a frequency sampling of 1MHz

then I came back to classical continuous conversion (without DMA -for the moment)

then I used this code :

uint8_t ADC_buffer[2];
uint16_t ADC_Value_16bit;
while(1) {  
   
     while ( HAL_ADC_PollForConversion(&hadc,1000) != HAL_OK ){}
			 
		 ADC_Value_16bit = HAL_ADC_GetValue(&hadc) >> 0; 
		
     ADC_buffer[0] = ADC_Value_16bit >> 8; // MSB
 
     ADC_buffer[1] = ADC_Value_16bit & 0xFF; // LSB
	
			 
		CDC_Transmit_FS(ADC_buffer,2);
 
}

And the good thing is that I can get the data with LabVIEW and plot my signal from my waveform generator.

it is still slow (I would say 10kbyte/s) but I have understood the principle of sending binary data

Now I am gonna fill a buffer with 1024 bytes with a for loop.

julien12657
Associate III

I am now able to send a buffer of 1024 binary data.

For simplicity (especially with LabVIEW) I shift to 8-bit of resolution for the ADC, still keeping 1µs of sampling period.

the code is here :

#define ADC_BUFFER_SIZE     1024
HAL_ADC_Start(&hadc);
 
uint8_t ADC_buffer[ADC_BUFFER_SIZE ];
 
uint8_t ADC_Value_8bit;
while(1) {  
   for (int i=0;i<ADC_BUFFER_SIZE;i++){
		 
     while ( HAL_ADC_PollForConversion(&hadc,1000) != HAL_OK ){}
		 ADC_buffer[i]=HAL_ADC_GetValue(&hadc);
			 
		 }
	 
			 
		CDC_Transmit_FS(ADC_buffer,ADC_BUFFER_SIZE);
 
}} 

I am sampling a sinewave at 1 kHz, I join a screen shoot of the labview plot, as you can see, the begining is not good but after 370 bytes it is OK

and also we can see that the 1kHz sinewave is very well resolved.Do you know why the first 370 points are that strange ?

0690X000006Cvv2QAC.png

Bob S
Principal

Congrats on getting data through to the PC.

One more thing to add is to check the return value from CDC_Transmit_FS(). Keep retrying that call until it returns HAL_OK. Without that check, if the USB interface is still busy (sending previous data???) you will lose the entire buffer's worth of data.

That strangeness at the beginning looks like your polling code is missing data from the ADC. Most likely this is due to one or more interrupt handlers taking longer than one ADC sample time. My best guess is that this is the USB interrupt handler. Your code snippets don't show when the USB interface is initialized and how much time passes from the USB init code until you try to send your data. It could be that the USB enumeration process is happening during those first data samples.

Two things to try:

(1) The quick and dirty test is the add a HAL_Delay(5000) call after your USBD_Start() call to give the enumeration process time to complete. This is a brute force hack but will confirm that this is the issue.

(2) In the long term, the correct solution is to use DMA for the ADC data. Then in your while() loop you extract data from the DMA buffer instead of calling HAL_ADC_PollForConversion() and HAL_ADC_GetValue().

One more improvement to consider - don't start the ADC conversion until the PC has actually opened the virtual COM port and is ready to receive data. There is some way to determine when the PC is connected (i.e. the enumeration has completed and the PC has opened the virtual COM port). I don't recall how to do that off the top of my head, but there have been posts on this forum that talk about that.

julien12657
Associate III

thanks Bob

is that the way to test if the usb is not bussy ? :

	while(CDC_Transmit_FS(ADC_buffer,buffer_size) !=HAL_OK){}

when I put a longer sampling time 252 cycles instead of 14, the strangeness at the beginning is less strong and restreint to the very begining. I don't understand the first trick (where should I put HAL_Delay(5000) ?) so I will try with DMA