17

I am beginning to get a bit confused about sampling rates and baudrates etc. I have this Arduino code:

#include <eHealth.h>

extern volatile unsigned long timer0_overflow_count;
float fanalog0;
int analog0;
unsigned long time;


byte serialByte;
void setup() {
  Serial.begin(9600);
}

void loop() { 
  while (Serial.available()>0){  
    serialByte=Serial.read();
    if (serialByte=='S'){        
      while(1){
        fanalog0=eHealth.getECG();  
        // Use the timer0 => 1 tick every 4 us
        time=(timer0_overflow_count << 8) + TCNT0;        
        // Microseconds conversion.
        time=(time*4);   
        //Print in a file for simulation
        //Serial.print(time);
        //Serial.print(" ");
        Serial.print(fanalog0,5);
        Serial.print("\n");

        if (Serial.available()>0){
          serialByte=Serial.read();
          if (serialByte=='F')  break;
        }
      }
    }
  }
}

Since there is no delay interrupt, what is the sampling rate/frequency? Is it based on the Arduino ADC speed? When I increase the baudrate am I increasing the sampling frequency or just the rate at which I send data over the serial port?

7 Answers 7

25

The Arduino ADC clock speed is set in ..arduino-1.5.5\hardware\arduino\avr\cores\arduino\wiring.c

Here is the relevant part

#if defined(ADCSRA)
    // Set A/D prescale factor to 128
    // 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
    // XXX: this will not work properly for other clock speeds, and
    // this code should use F_CPU to determine the prescale factor.
    sbi(ADCSRA, ADPS2);
    sbi(ADCSRA, ADPS1);
    sbi(ADCSRA, ADPS0);

    // Enable A/D conversions
    sbi(ADCSRA, ADEN);
#endif

For a 16 MHz Arduino the ADC clock is set to 16 MHz/128 = 125 KHz. Each conversion in AVR takes 13 ADC clocks so 125 KHz /13 = 9615 Hz.

That is the maximum possible sampling rate, but the actual sampling rate in your application depends on the interval between successive conversions calls.
Since you read the result and send it through the serial port, you are getting a delay that increases as the baud rate decreases. The lower the baud rate the longer it will take to send the same length of data and the longer it will take to call the next ADC conversion.

The actual sampling rate in your application can be determined with the use of a debugger or a simulator, but an easier solution is to toggle a digital pin every time you execute a conversion and measure the frequency that the digital pin toggles at.

2
  • Also, the time between my time stamps increases from ~1300 all the way up to ~16400, surely they should stay the same? That is at 9600, at 115200, they increase only to about 1500 after a lot of time.
    – hawkar
    Commented Mar 10, 2014 at 23:02
  • @user3284376 regarding your time stamp code, I think it cannot work at all times (can be biased by some interrupts at the wrong time). I'd suggest you post a specific question about how to get high precision timing on Arduino and put the relevant part of your code there.
    – jfpoilpret
    Commented Mar 11, 2014 at 6:02
9

I also wanted to get a high sampling rate, for a project. turns out that the ADPS2,ADPS1,ADPS0 bits of the ADCSRA register can be configured to get a sampling rate of 76923 s/s or 76.8 ks/s. But, beware that I am running my arduino's ADC in free running mode, the following lines worked for me.

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

void setup() {
// Set the Prescaler to 16 (16000KHz/16 = 1MHz)
// WARNING: Above 200KHz 10-bit results are not reliable.
//ADCSRA |= B00000100;
sbi(ADCSRA, ADPS2);
cbi(ADCSRA, ADPS1);
cbi(ADCSRA, ADPS0);

// Set ADIE in ADCSRA (0x7A) to enable the ADC interrupt.
// Without this, the internal interrupt will not trigger.
//ADCSRA |= B00001000;
sbi(ADCSRA,ADIE)
}

At this frequency the usual 10-bit results are not reliable. It means that increasing the sample rate will decrease the precision of the results. So I only use the upper 8 bits because at this prescalar the upper 8-bits are reliable. You can go into more detail on this page, this dude rocks! he made a high sample rate oscilloscpe using UNO http://www.instructables.com/id/Girino-Fast-Arduino-Oscilloscope/

1
  • I'm on an ATMEGA 2560, but I figured I'd copypaste in case this worked. Once I replaced the calls to sbi and cbi with their definitions (my compiler didn't like them), AND commented out the last line, the quality of my stupid audio circuit improved massively. Thanks!
    – Erhannis
    Commented Feb 10, 2021 at 6:28
4

Sending 11 bits over serial at a baud of 9600, but for the sampling, I store it in an array with as little delay as possible, then once it's done, I send it through the serial port to be read by a python script. I'm doing this for an FFT using matplotlib. I listen to a 0-5V signal, then without using the delay() function, I store the analogRead() values into that array. In a split second the reading is done, then the serial data dump starts. When I calibrated the input frequency using tone() from another connected Arduino, I realized that I had to divide the index by 8915 in order to get accuracy to within .1 Hz. Because one would have to divide by the frequency of the sampling to get the proper index intervals, my guess is the Arduino sampling frequency (at least mine with my code) is 8915Hz.

3

Each loop you are printing 8 characters over a 9600bps serial link. Each character takes 10 bits (1 start, 8 bits for the character, 1 stop). That means you can only go through this loop ~120 times/sec.

The analogRead() function can sample at about 9600 times/sec in theory, realistically it is about 8600 times/sec.

You are being bounded by the serial communication.

5
  • So increasing to 115200, gives 1440 times/sec, is that the sample rate?
    – hawkar
    Commented Mar 10, 2014 at 23:01
  • Give or take, yes. You need to bear in mind that Serial requires that the other end responds, so you are dependent on the PC responding. This is not deterministic so you will get jitter. Commented Mar 11, 2014 at 11:44
  • You are right on the Arduino end of things, it all appears fine, but on Python things are much slower, what kind of things would I need to do to increase the performance on the computer end of things?
    – hawkar
    Commented Mar 11, 2014 at 12:03
  • You need to not look at this as a problem with the serial performance on the PC, but how do you get the sampling to be decoupled from the sending of data. Commented Mar 14, 2014 at 9:40
  • 1
    @Cybergibbons - no, as this is running on an Uno where the USB and serial are decoupled, there is no dependence on the PC beyond issuing the 'S' character and not issuing the 'F' one. The sketch posted here and platform on which it runs will happily throw serial data at the USB-serial companion micro, blindly oblivious if that or anything on the other end of the USB is keeping up. Commented Apr 12, 2014 at 18:26
1

Referring to the part about the difference between sample rate and baud rate, they are different measurements.

Sample Rate is the frequency at which the device (arduino) can recreate a digital representation of incoming analog values.

Baud Rate is the rate at which information is transferred in a communication channel. It describes the rate of communication between the microcontroller and the outside world (the computer).

I would recommend this electronics_stack_exchange link. https://electronics.stackexchange.com/questions/135056/sampling-rate-data-rate-and-bandwidth

0

8915Hz - it is very close to 125000/14 ~= 8928.6 My initial guess that exactly one gap is required between adjacent conversions One ADC clock for sampling and 13 ADC clocks for conversion itself. Small error could be effect of not perfect clock source of Arduino. I am not sure yet. This topic is actual for me now as sampled data must feed digital filter.

2
  • 1
    I'm not sure what you mean when you say "This topic is actual for me now as sampled data must feed digital filter.". Are you having a similar problem?
    – VE7JRO
    Commented Nov 13, 2019 at 22:59
  • Each conversion starts on a rising edge of the ADC clock, and at least one ADC clock cycle is lost executing code. So yes, 8928.6 Hz is the fastest you can get by calling analogRead() in a tight loop, v.s. a very consistent 9615.4 Hz in free-running mode. Commented Nov 14, 2019 at 14:46
0

Have you considered getting A Saleae or similar logic analyzer. The simple inexpensive ones work great and allow you to make these measurements quite easily. This works better then a scope in most of my applications. If there is signal overlap it will show. I simply devote one pin for testing and call it testpin. Just set it high when starting the routine and then low when finished. It has 8 inputs so you can see the relationship between signals. You could monitor the A/D, starting the trigger and place the serial on another pin. You still have 6 left for other things. Spend a few hours with it and it will become one of your most important tools. I use Linux Mint so I just keep it on another screen so a simple click brings it in view and it will run whether the window is in view or not. Be careful if you have unused inputs they will act as antenna and pick up stray signals especially from switching power supplies. Just select the channels you want or ignore the ones that are not relevant. The built in analyzers make short work of serial signals.

Not the answer you're looking for? Browse other questions tagged or ask your own question.