Question about the arduino adc

Hi guys,

I know arduino ADC can be achieved by programming as:

analogRead();

My question is how can I control the sampling rate? I have a 2-channel sensor, and I need 3000Hz sampling frequency for each channel. But I don't know how to precisely set the sampling frequency to 3000Hz. Can I use delay(0.33)?

Another question is if I want to transfer the data through serial port, I should use Serial.print/println or Serial.write? I use Serial.println before, and set the delay time to delay(1) so that the sampling rate is 1000 samples/s, but when I use labview to collect the data, I found that labview program collect the data at a very slow speed(<<<1000 samples/s), the data seem to be stack somewhere.

Hope you can understand me :roll_eyes:

Can I use delay(0.33)?

No.
You could try delayMicroseconds.

use Serial.println before,

At what speed?

If you want anything to happen at exact times, and especially 2 or more things, don't use delay(). Look at how millis() is used to manage time in several things at a time.

If you are changing ADC channels you need to take two reads after the change and ignore the first one - to give the ADC time to settle at the new channel.

The interval between reads at 3khz would be 333 microsecs. You can use the technique in several things at a time with micros() just as well as with millis().

Use Serial.println() ( or print() ) unless it proves to be unsatisfactory in some way. I suspect you may be expecting too much to read 6000 values per second and send the results over a Serial connection. You will need to experiment to see what is possible, and use a very high baud rate.

What are you sampling?

...R

delay(0.33) will not work, use delayMicroseconds(333). I believe the ADC can sample at ~9600hz (16Mhz/(13 clock cycles * 128 prescaler)). You could always set the prescaler to a smaller value to make it faster, however, you lose accuracy of the ADC.

As far as using Serial.print vs Serial.write, you should use Serial.write because if you use Serial.print(26), it will send 50, 54 (ASCII values for the characters '2' and '6'). Serial.write(26) will send 26.

So maybe something like this

void setup()
{
  Serial.begin(115200); //send serial out very fast, 9600 is default and slow
}

void loop()
{
  int val = analogRead(A0);
  Serial.write(val >> 8); //Serial.write sends a byte or 8 bits, ADC is 10 bit, so this send bits 8 and 9
  Serial.write(val & 0xFF); //This sends bits 0,1,2,3,4,5,6,7
  //WARNING: As AWOL says, a typical analogRead takes about 100uS, so maybe use delayMicroseconds(233);
  delayMicroseconds(333); //refresh rate at 3Khz
}

The interval between reads at 3khz would be 333 microsecs.

. . . and don't forget the reads themselves will take around 100us

Ps991:
As far as using Serial.print vs Serial.write, you should use Serial.write because if you use Serial.print(26), it will send 50, 54 (ASCII values for the characters '2' and '6'). Serial.write(26) will send 26.

I agree with you regarding speed. However sending the data in binary will require different software at the receiving end. Which is why I suggested using Serial.print() unless it proves unsatisfactory.

The Arduino can work much faster than 115,200 baud - even if the Serial Monitor can't.

...R

Robin2:
I agree with you regarding speed. However sending the data in binary will require different software at the receiving end.

Oh, is labView a serial monitor type thing? Because if it is, then you are right to use Serial.print(). If you are using another program or whatever to store the data, then use Serial.write(). It depends if you want to read the data in a monitor or store the data through another program.

Robin2:
If you want anything to happen at exact times, and especially 2 or more things, don't use delay(). Look at how millis() is used to manage time in several things at a time.

If you are changing ADC channels you need to take two reads after the change and ignore the first one - to give the ADC time to settle at the new channel.

The interval between reads at 3khz would be 333 microsecs. You can use the technique in several things at a time with micros() just as well as with millis().

Use Serial.println() ( or print() ) unless it proves to be unsatisfactory in some way. I suspect you may be expecting too much to read 6000 values per second and send the results over a Serial connection. You will need to experiment to see what is possible, and use a very high baud rate.

What are you sampling?

...R

Thank you very much Robin! I'm sampling the data from a radar sensor. So the problem is maybe the serial port cannot read 6000 values per second? I can decrease the adc sampling rate to 1000Hz, but I'm not sure whether it will work.

The reason why I want to use Serial.write is that since Serial.println will convert the data to ASCII character, I'm afraid it will decrease serial port reading speed. As I mentioned, I use serial.print before and transferred the data to labview program through bluetooth, and my labview program collect the data at a very low speed, it seems that the data were stuck at the serial port.

AWOL:
No.
You could try delayMicroseconds.
At what speed?

Hi AWOL, thank you for your reply. I don't know how to describe the speed, did you have experience on Labview? Labview program can display the real time data on the graph, however, the data displaying speed is much lower than the producing speed, it makes me feel that the data were stuck somewhere, or arduino adc didn't sample enough data as I expect.

Ps991:
delay(0.33) will not work, use delayMicroseconds(333). I believe the ADC can sample at ~9600hz (16Mhz/(13 clock cycles * 128 prescaler)). You could always set the prescaler to a smaller value to make it faster, however, you lose accuracy of the ADC.

As far as using Serial.print vs Serial.write, you should use Serial.write because if you use Serial.print(26), it will send 50, 54 (ASCII values for the characters '2' and '6'). Serial.write(26) will send 26.

So maybe something like this

void setup()

{
  Serial.begin(115200); //send serial out very fast, 9600 is default and slow
}

void loop()
{
  int val = analogRead(A0);
  Serial.write(val >> 8); //Serial.write sends a byte or 8 bits, ADC is 10 bit, so this send bits 8 and 9
  Serial.write(val & 0xFF); //This sends bits 0,1,2,3,4,5,6,7
  //WARNING: As AWOL says, a typical analogRead takes about 100uS, so maybe use delayMicroseconds(233);
  delayMicroseconds(333); //refresh rate at 3Khz
}

Hi Ps991, thank you very much for your reply!

So Serial.write(val >> 8); Serial.write(val & 0xFF); can send the 10-bit digital data correctly? I will have a try!

Ps991:
delay(0.33) will not work, use delayMicroseconds(333). I believe the ADC can sample at ~9600hz (16Mhz/(13 clock cycles * 128 prescaler)). You could always set the prescaler to a smaller value to make it faster, however, you lose accuracy of the ADC.

As far as using Serial.print vs Serial.write, you should use Serial.write because if you use Serial.print(26), it will send 50, 54 (ASCII values for the characters '2' and '6'). Serial.write(26) will send 26.

So maybe something like this

void setup()

{
  Serial.begin(115200); //send serial out very fast, 9600 is default and slow
}

void loop()
{
  int val = analogRead(A0);
  Serial.write(val >> 8); //Serial.write sends a byte or 8 bits, ADC is 10 bit, so this send bits 8 and 9
  Serial.write(val & 0xFF); //This sends bits 0,1,2,3,4,5,6,7
  //WARNING: As AWOL says, a typical analogRead takes about 100uS, so maybe use delayMicroseconds(233);
  delayMicroseconds(333); //refresh rate at 3Khz
}

And one more question, Serial.write(val >> 8); and Serial.write(val & 0xFF) will divide the 10-bits data in two parts and send them separately? Is there any method to combine them and transmit them together? Because it will make the receiving end much easier.

assassin:
The reason why I want to use Serial.write is that since Serial.println will convert the data to ASCII character,

Serial.write() is agood idea if you know what you are doing.

It seems to me it is time to write a short test program to check out the throughput. There is no point designing a complex solution if at the end you find it won't work fast enough. Just write a program that reads the adc (even though it is not connected to anything), does whatever needs to be done with the adc values and sends them to your PC - to a program that can tell you if it is receiving the stuff correctly. The fact that the data means nothing is irrelevant - the quantity and accuracy of throughput is all that matters.

Serial.write() only sends bytes. But if they are in the correct order the receiving program can treat a pair of them as a 16 bit value.

...R

Robin2:
I agree with you regarding speed. However sending the data in binary will require different software at the receiving end. Which is why I suggested using Serial.print() unless it proves unsatisfactory.

The Arduino can work much faster than 115,200 baud - even if the Serial Monitor can't.

...R

Hi Robin, I'm afraid that serial.print will decrease the serial port reading or transmitting speed since it will convert the data to ASCII character

Robin2:
Serial.write() is agood idea if you know what you are doing.

It seems to me it is time to write a short test program to check out the throughput. There is no point designing a complex solution if at the end you find it won't work fast enough. Just write a program that reads the adc (even though it is not connected to anything), does whatever needs to be done with the adc values and sends them to your PC - to a program that can tell you if it is receiving the stuff correctly. The fact that the data means nothing is irrelevant - the quantity and accuracy of throughput is all that matters.

Serial.write() only sends bytes. But if they are in the correct order the receiving program can treat a pair of them as a 16 bit value.

...R

:smiley: It's a good idea to write a short test program. I know Serial.write only sends bytes, so it will divide the 10-bits data in two bytes and I should combine them together in the Labview? Is there any method to combine them in the front-end?

assassin:
Hi Robin, I'm afraid that serial.print will decrease the serial port reading or transmitting speed since it will convert the data to ASCII character

I dealt with that in Reply #11

assassin:
I know Serial.write only sends bytes, so it will divide the 10-bits data in two bytes and I should combine them together in the Labview? Is there any method to combine them in the front-end?

No.
That's what I meant when I said (in Reply #11) ) "Serial.write() only sends bytes". The entire Serial USART transmission system works in bytes.If you want to send an int it has to go as 2 bytes and a long goes as 4 bytes.

The question to ask is whether Labview can automatically read two bytes as an int - and I don't know Labview. I know how to make an Arduino treat 2 bytes as an int, but that is not much good to you.

And it is because of all these questions that I suggested using Serial.print() even though it does use more bytes for the same amount of information.

You can do your speed test without resolving this issue. Just send the correct number of characters and have Labview interpret them as Ascii. So long as the characters received are the same as those transmitted it does not matter if the mean nothing.

...R

Robin2:
I dealt with that in Reply #11No.
That's what I meant when I said (in Reply #11) ) "Serial.write() only sends bytes". The entire Serial USART transmission system works in bytes.If you want to send an int it has to go as 2 bytes and a long goes as 4 bytes.

The question to ask is whether Labview can automatically read two bytes as an int - and I don't know Labview. I know how to make an Arduino treat 2 bytes as an int, but that is not much good to you.

And it is because of all these questions that I suggested using Serial.print() even though it does use more bytes for the same amount of information.

You can do your speed test without resolving this issue. Just send the correct number of characters and have Labview interpret them as Ascii. So long as the characters received are the same as those transmitted it does not matter if the mean nothing.

...R

Hi Robin, labview can read 2 bytes as an int, I will try to use serial.write. As you said, serial.print uses more bytes for the same amount of informatiom, from your experience, could 115200 baud rate transmit 2000 values per second if I use serial.print()?

Work it out yourself. At 115200 baud it will take 1/11520 seconds per byte (86.8 µS).

If you are transmitting 2000 x 2 bytes at that rate I calculate it will take 0.347 seconds to do that, assuming no extra characters like packet delimiters.

Bear in mind that unless you read the ADC asynchronously, each analogRead takes 104 µS as well. That would add another 0.208 seconds to take 2000 readings.

Sorry I took so long to reply, but combining them is as easy as reading the bytes in order (the first byte containing the high 8 bits, the second byte containing the low 8 bits)

So combining is like this

// I will be using serial.read() in this example because I do not know the function in labview
int receivedValue = (serial.read() << 8) + serial.read();

if the ADC read times are too slow for you, you may just want to change the prescaler to 64 instead of 128, although it does slightly reduce the ADC accuracy