4

I was wondering why there is a 15 ms delay between each iteration of a the main loop.

Code: The following code shows the execution time expressed in microsec between each loop

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

void loop()
{
  Serial.println();
  Serial.print("Start: ");
  Serial.print(micros());
  Serial.print("/////////////////////");
  Serial.print("End:");
  Serial.print(micros()); 
}

Output:

Start: 789452/////////////////////End:821692
Start: 837292/////////////////////End:869532
Start: 885132/////////////////////End:917372
Start: 932972/////////////////////End:965212

Arduino needs 32,240 msec to print the messages to serial and to call the micros() function twice. And this it's fine. Now, considering the difference between the values of the End and the following Start, one question arises:

What does Arduino do between each loop?

5

Just check the sourcecode

for (;;) {
    loop();
    if (serialEventRun) serialEventRun();
}

So it does a check if serialEventRun is True, and then a jump back to the beginning of the for-loop. And then a call to loop.

Also note that Serial.print is blocking if the TX-buffer is full. So you might want to add a delay somewhere to ensure the buffer is never full.

5

Your serial connection is set to 9600 baud, which is 9600 actual data bits per second. The communication consists (*) of 8 bit characters and every bit is useful data (no parity), so you would be able to achieve at most 9600 / 8 = 1200 characters per second.

You have a message of 46 characters including the line break, which brings you to 1200 / 46 = 26 iterations per second.

1000 milliseconds / 26 iterations = about 39 milliseconds per iterations.

This is the theoretic maximum you would get if you would send the data over serial without any other overhead. Pretty close to the 32 + 15 = 47 milliseconds you measured.

Since you print a part of the message (Enter + 'Start:') between the end of the previous iteration and the start of the next (if you take the call to micros() as the start and end), it makes sense that a part of the time is in between there as well.

So the actual overhead you are looking at is about 8ms per iteration. This delay is spread out over the loop and the call overhead of the various functions you use. After all, when you call Serial.print, a lot of work needs to be done before the data can actually be sent to the serial port. You would probably gain more speed/less overhead if you would construct one big string, and send it with a single print command.

Arduino itself is slow, but still much faster than this. It should be able to do a big(ger) number of iterations per second, but you basically limited it by initialising the serial connection to such a slow speed. You can partially eliminate this by picking a higher number. I think both your Arduino and any modern serial port should be able to handle 115200 baud. Nevertheless, you will always have a relatively big overhead when you are sending data over serial. Use your bandwidth wisely, and reduce the number of calls to Serial.print as much as you reasonably can.

* ) 8 bit, no parity is the de facto default. See Serial.begin() for more information about initializing the serial connection.

  • 2
    Note that the 9600 figure is "bits per second", not "baud". You are right that "baud" means "actual data bits" (as opposed to overhead bits, such as start, parity and stop bits), but that distinction is often lost - and in this case incorrect. Each character is 10 bits - 1 start, 8 data, and 1 stop - so the correct number is 960 characters per scond. – John Burger Jul 23 '16 at 10:10
  • @JohnBurger Thanks for that addition. The default setting is indeed 8 data bits and 1 stop bit, no parity, so with a start bit that would be 10 bits per character indeed. You can by the way configure this in the optional second parameter of Serial.begin(). Anyway, the documentation says 'bits per second (baud)', so if you are right, then the docs are wrong (or I misunderstood something). – GolezTrol Jul 23 '16 at 12:08
  • Like I said: your definition of the original term "baud" is factually correct - but that usage has been confused with "bits per second" for so long now that the distinction is lost to the general public. They'll say "9,600 baud" when they mean "9,600 bps", so documentation follows this (incorrect) convention - although you'll note they used parentheses! In fact, if you were to say to someone "Set your baud rate to 7,680", you'd be correct but unsatisfied, as you won't be able to establish communicatons... – John Burger Jul 23 '16 at 12:47
  • @JohnBurger, The distinction between "baud rate" and "bit rate" is not about which bits are data, and which bits are framing. The "baud rate" on a wire tells how many times per second the line state changes. When there are only two line states (usually called "0" and "1", or "mark" and "space"), then the baud rate equals the bit rate. But when you're using modems for long-distance comms, there can be four or eight line states, and the baud rate then is 1/2 or 1/3 of the effective bit rate. All most of us ever care about is effective bit rate. Baud rate is for modem designers. – Solomon Slow Feb 22 '18 at 1:58
  • 1
    Also note that "how many times per second the line state changes" isn't completely correct either: it should be "...the line state could change". A series of 0 or 1 bits (for NRZ encoding) would not involve line state changes, but the number of bits sent would still go up. – John Burger Feb 23 '18 at 2:56
4

Your code is slightly off if you want to accurately time this

Serial.print("End:");
Serial.print(micros()); 

Will get micros() before printing to serial, and then take the time to print it. You also print some text before taking the time at the top of the loop. Your 15 ms is the time to print these to serial between reading micros. For example, change the loop code to something like this:

uint32_t time, endTime;
...
void loop(){
    time = micros();

    Serial.print(endTime);
    Serial.print('\t');
    Serial.print(time);
    Serial.print('\n');

    endTime = micros();
}

and you should only see a small difference. It showed a 4 microsecond difference when I ran it. The only other thing that runs outside the loop is the serial event handlers, but if you don't use one it obviously doesn't take long.

2

TL;DR: The delays you measure (32.24 ms between Start and End, and 15.6 ms between End and Start) are exactly what is expected from the amount of data you send through the serial port. Yes, exactly: there is zero overhead from the CPU doing other things.

Let me show how the expected timings can be computed. First, a note about the exact baud rate. A rate of 9600 bps implies that each bit takes about 104.17 µs to send. However, the Arduino being clocked at 16 MHz, it cannot achieve this exact baud rate. Instead, when you request 9600 bps, you get the closest value it can achieve, which is about 9615 bps. Then, each bit takes exactly 104 µs. Or at least “exactly” as per the Arduino clock: it really takes 1664 CPU cycles.

Then, as already noted in previous answers, one character is worth 10 raw bits. It thus needs 1040 µs to be transmitted.

Now, let's look at your code. It should be noted that

Serial.print(micros());

is compiled into something equivalent to

unsigned long timestamp = micros();
Serial.print(timestamp);

i.e. the timestamp is taken and then it is transmitted. Your loop() is then equivalent to:

void loop()
{
    Serial.println();                       //  2 chars: CR and LF
    Serial.print("Start: ");                //  7 chars
    unsigned long time_start = micros();
    Serial.print(time_start);               //  6 chars
    Serial.print("/////////////////////");  // 21 chars
    Serial.print("End:");                   //  4 chars
    unsigned long time_end = micros();
    Serial.print(time_end);                 //  6 chars
}

By folding the last Serial.print() of one iteration into the beginning of the next one, one gets the pseudo-code:

forever {
    print 15 chars;  // 15*1040 = 15600 µs
    read time_start;
    print 31 chars;  // 31*1040 = 32240 µs
    read time_end;
}

The computed durations match exactly your printout. This shows that, once the Serial output buffer is full, the timing of your code execution is completely governed by how fast the UART sends the bits out the line. The time taken by the Arduino to do actual CPU work (calling micros(), formatting numbers...) is irrelevant because the CPU works in parallel with the UART and, being faster, it ends up waiting for the UART anyway.

0

At 9600now each data byte needs 10 bits to be transmit. Or 1ms per byte.

20 bytes will take 20me to transmit.

However, the existence of a buffer and the way the transmission is carried out can greatly impact the duration of execution of your code - actual time for transmission is invariant however.

If your transmission is done via the interrupt, the execution can be almost instantaneous.

  • the existence of a buffer and the way the transmission is carried out can greatly impact the duration of execution of your code”. In the OP's example, this is true only at the very beginning. Once the output buffer is full, the timing of the code execution is entirely dictated by the UART. – Edgar Bonet Jun 20 '17 at 17:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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