Go Down

Topic: speed of digitalWrite and digitalRead on Arduino Due. (Read 9838 times) previous topic - next topic

timKnapen

hi all,

I'm think about using the Arduino Due as the brains of a Reprap like machine. It will read commands from an SD card (or from it's memory, maybe Due has enough?), parse them and control stepper motors.
So I ordered one and started doing some tests. I've found that digitalWrite and digitalRead are remarkably slower on the Due than the UNO.

I used this sketch as a test:
Code: [Select]

#include <digitalWriteFast.h>

/*
I'm trying to see how fast some instructions are on Arduino UNO and Arduino DUE
*/

int sampleSize = 1000000; // do a million times!
long lastTime;

//-----------------------------------------------------------------------------
void setup(){
  delay(50); // wait a bit. (Arduino Due prints nonsense to the serial port...??)
  Serial.begin(115200); // go really fast serial!
 
  // I will store my value in this:
  int n;
 
  // and use these pins for writing or reading
  pinMode(2, OUTPUT);
  pinMode(3, INPUT);

  Serial.println("digitalWrite:");
  lastTime = micros();
  for(int i =0; i < sampleSize; i++){
    digitalWrite(2, HIGH);
    digitalWrite(2, LOW);
  }
  printTime( micros() - lastTime);

#ifdef digitalWriteFast // not working with Arduino Due
  Serial.println("digitalWriteFast:");
   lastTime = micros();
   for(int i =0; i < sampleSize; i++){
   digitalWriteFast(2, HIGH);
   digitalWriteFast(2, LOW);
   }
   printTime( micros() - lastTime);
#endif

  Serial.println("digitalRead:");
  lastTime = micros();
  for(int i =0; i < sampleSize; i++){
    n = digitalRead(3);
  }
  printTime( micros() - lastTime);
 
#ifdef digitalReadFast
  Serial.println("digitalReadFast:");
  lastTime = micros();
  for(int i =0; i < sampleSize; i++){
    n = digitalReadFast(3);
  }
  printTime( micros() - lastTime);
#endif

}

//-----------------------------------------------------------------------------
void loop(){
  // do nothing
}


//-----------------------------------------------------------------------------
void printTime( long microsecs){
  int mins, secs, msecs, usecs;

  msecs = floor(microsecs / 1000);
  usecs = microsecs % 1000;

  secs = floor(msecs / 1000);
  msecs = msecs % 1000;

  mins = floor(secs / 60);
  secs = secs % 60;
  Serial.print("time: ");
  Serial.println(microsecs);

  Serial.print(mins);
  Serial.print(" min ");
  Serial.print(secs);
  Serial.print(" sec ");
  Serial.print(msecs);
  Serial.print(" msec ");
  Serial.print(usecs);
  Serial.println(" usecs");
  Serial.println("");
  delay(20);
}


I commented out "#include <digitalWriteFast.h>" when compiling for the Due.

These are the results:

Arduino Due:
digitalWrite:
time: 5196306
0 min 5 sec 196 msec 306 usecs

digitalRead:
time: 1156070
0 min 1 sec 156 msec 70 usecs

Arduino UNO:
digitalWrite:
time: 142964
0 min 0 sec 142 msec 964 usecs

digitalWriteFast:
time: 8624
0 min 0 sec 8 msec 624 usecs

digitalRead:
time: 87508
0 min 0 sec 87 msec 508 usecs

digitalReadFast:
time: 7544
0 min 0 sec 7 msec 544 usecs

Sanguino ATMEGA664A  (Reprap motherboard v1.2)
digitalWrite:
time: 142964
0 min 0 sec 142 msec 964 usecs

digitalWriteFast:
time: 8628
0 min 0 sec 8 msec 628 usecs

digitalRead:
time: 86440
0 min 0 sec 86 msec 440 usecs

digitalReadFast:
time: 7548
0 min 0 sec 7 msec 548 usecs

Does anyone know why I find this difference? Is there already a digitalWriteFast library for Due available?

best,
tim.

stimmer

This can't be right,1 million digitalWriteFast pairs in 8ms on a Uno is an output frequency of 125MHz which is clearly way beyond the specification of the chip!
Due VGA library - http://arduino.cc/forum/index.php/topic,150517.0.html

stimmer

I just realised where you went wrong:

int sampleSize = 1000000;

On a Uno int is 16 bits, so this will actually set sampleSize to 16960  :0
Due VGA library - http://arduino.cc/forum/index.php/topic,150517.0.html


timKnapen

so I've changed my test like this:

Code: [Select]

//#include <digitalWriteFast.h> // uncomment for Arduino UNO

/*
I'm trying to see how fast some instructions are on Arduino UNO and Arduino DUE
*/

long sampleSize = 1000000; // do a million times!
long lastTime;

//-----------------------------------------------------------------------------
void setup(){
  delay(50); // wait a bit. (Arduino Due prints nonsense to the serial port...??)
  Serial.begin(115200); // go really fast serial!
  Serial.print("Sample size: ");
  Serial.println(sampleSize);
  // I will store my value in this:
  int n;
 
  // and use these pins for writing or reading
  pinMode(2, OUTPUT);
  pinMode(3, INPUT);

  Serial.println("digitalWrite:");
  lastTime = micros();
  for(long i =0; i < sampleSize; i++){
    digitalWrite(2, HIGH);
    digitalWrite(2, LOW);
  }
  printTime( micros() - lastTime);

#ifdef digitalWriteFast // not working with Arduino Due
  Serial.println("digitalWriteFast:");
   lastTime = micros();
   for(long i =0; i < sampleSize; i++){
   digitalWriteFast(2, HIGH);
   digitalWriteFast(2, LOW);
   }
   printTime( micros() - lastTime);
#endif

  Serial.println("digitalRead:");
  lastTime = micros();
  for(long i =0; i < sampleSize; i++){
    n = digitalRead(3);
  }
  printTime( micros() - lastTime);
 
#ifdef digitalReadFast
  Serial.println("digitalReadFast:");
  lastTime = micros();
  for(long i =0; i < sampleSize; i++){
    n = digitalReadFast(3);
  }
  printTime( micros() - lastTime);
#endif

}

//-----------------------------------------------------------------------------
void loop(){
  // do nothing
}


//-----------------------------------------------------------------------------
void printTime( long microsecs){
  int mins, secs, msecs, usecs;

  msecs = floor(microsecs / 1000);
  usecs = microsecs % 1000;

  secs = floor(msecs / 1000);
  msecs = msecs % 1000;

  mins = floor(secs / 60);
  secs = secs % 60;
  Serial.print("time: ");
  Serial.println(microsecs);

  Serial.print(mins);
  Serial.print(" min ");
  Serial.print(secs);
  Serial.print(" sec ");
  Serial.print(msecs);
  Serial.print(" msec ");
  Serial.print(usecs);
  Serial.println(" usecs");
  Serial.println("");
  delay(20);
}


and the new results are:

Arduino UNO
Sample size: 1000000
digitalWrite:
time: 8991484
0 min 8 sec 991 msec 484 usecs

digitalWriteFast:
time: 754608
0 min 0 sec 754 msec 608 usecs

digitalRead:
time: 5721828
0 min 5 sec 721 msec 828 usecs

digitalReadFast:
time: 691724
0 min 0 sec 691 msec 724 usecs


Arduino Due
Sample size: 1000000
digitalWrite:
time: 5148742
0 min 5 sec 148 msec 742 usecs

digitalRead:
time: 1275278
0 min 1 sec 275 msec 278 usecs

Much better! :-)
So I guess I won't be needing to worry about the speed of digitalWrite or read.

sam0737

Just to let you know I intend to do the same - Due for Reprap. But I would take the rewrite from scratch, with an RTOS (ChibiOS/RT) instead of porting existings firmware.

I would create a macro to manipulate the pin directly using PIOA->SODR/CODR instead of using digitalWrite. digitalWrite does too much magic that makes it 10 times slower than direct pin manipulation.

timKnapen

Hi Sam,
Very cool! I'm not really using it for Reprap, but something very similar: a drawing machine (this one: http://www.flickr.com/photos/worldreceiver/8215231164/in/set-72157622815511236 ) but it's easier to say "reprap-like"
I think digitalWriteFast is implemented only with macros if I'm not mistaken. If you get direct pin manipulation to work on the Due, would you mind releasing it? I for one would find it very interesting.

jiblets

Reading through these other threads will give you a pretty good idea of how to use port manipulation on the due.

http://arduino.cc/forum/index.php/topic,155431.0.html
http://arduino.cc/forum/index.php?topic=129868.0

timKnapen

#8
Jun 06, 2013, 06:45 pm Last Edit: Jun 06, 2013, 06:53 pm by timKnapen Reason: 1
I've finally cleaned up the source of my drawing machine and did an initial commit to github.
It's Arduino Due + micro sD reader + 3 A4988 stepper drivers
You can check it here http://github.com/timknapen/Longhand-Firmware

jappiemike

#9
May 19, 2015, 09:42 am Last Edit: May 19, 2015, 09:44 am by jappiemike
I've used code from this posting http://forum.arduino.cc/index.php?topic=315049.0 to enable fast reads and writes on the Due but I'm getting really strange results. Maybe someone can tell me what I'm doing wrong.
Firstly I made a library
Code: [Select]
#ifndef DigitalRW_Direct
#define DigitalRW_Direct

#include <Arduino.h>

inline void digitalWriteDirect(int pin, boolean val){
 if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
 else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

inline void digitalWriteDirectHigh(int pin){
 g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
}

inline void digitalWriteDirectLow(int pin){
g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

inline int digitalReadDirect(int pin){
 return !!(g_APinDescription[pin].pPort -> PIO_PDSR & g_APinDescription[pin].ulPin);
}

#endif

You'll see I used separate HIGH and LOW functions just to see if the if (val) comparison was a bottleneck.
I then used this test  code
Code: [Select]
#include <Arduino.h>
#include <DigitalRW_Direct.h>




long sampleSize = 100000; // do a million times!
long lastTime;

void setup() {
  // put your setup code here, to run once:
  delay(50); // wait a bit. (Arduino Due prints nonsense to the serial port...??)
  Serial.begin(115200); // go really fast serial!
  Serial.print("Sample size: ");
  Serial.println(sampleSize);
  // I will store my value in this:
  int n;

  // and use these pins for writing or reading
  pinMode(7, OUTPUT);
  pinMode(8, INPUT);

  Serial.println("digitalWrite:");
  lastTime = micros();
  for (long i = 0; i < sampleSize; i++) {
    digitalWrite(7, HIGH);
    digitalWrite(7, LOW);
    digitalWrite(7, HIGH);
    digitalWrite(7, LOW);
    digitalWrite(7, HIGH);
    digitalWrite(7, LOW);
    digitalWrite(7, HIGH);
    digitalWrite(7, LOW);
    digitalWrite(7, HIGH);
    digitalWrite(7, LOW);
    digitalWrite(7, HIGH);
    digitalWrite(7, LOW);
    digitalWrite(7, HIGH);
    digitalWrite(7, LOW);
    digitalWrite(7, HIGH);
    digitalWrite(7, LOW);
    digitalWrite(7, HIGH);
    digitalWrite(7, LOW);
    digitalWrite(7, HIGH);
    digitalWrite(7, LOW);
  }
  printTime( micros() - lastTime);

  Serial.println("digitalWriteFast High/Low:");
  lastTime = micros();
  for (long i = 0; i < sampleSize; i++) {
    digitalWriteDirectHigh(7);
    digitalWriteDirectLow(7);
    digitalWriteDirectHigh(7);
    digitalWriteDirectLow(7);
    digitalWriteDirectHigh(7);
    digitalWriteDirectLow(7);
    digitalWriteDirectHigh(7);
    digitalWriteDirectLow(7);
    digitalWriteDirectHigh(7);
    digitalWriteDirectLow(7);
    digitalWriteDirectHigh(7);
    digitalWriteDirectLow(7);
    digitalWriteDirectHigh(7);
    digitalWriteDirectLow(7);
    digitalWriteDirectHigh(7);
    digitalWriteDirectLow(7);
    digitalWriteDirectHigh(7);
    digitalWriteDirectLow(7);
    digitalWriteDirectHigh(7);
    digitalWriteDirectLow(7);
  }
  printTime( micros() - lastTime);

  Serial.println("digitalWriteFast:");
  lastTime = micros();
  for (long i = 0; i < sampleSize; i++) {
    digitalWriteDirect(7, HIGH);
    digitalWriteDirect(7, LOW);
    digitalWriteDirect(7, HIGH);
    digitalWriteDirect(7, LOW);
    digitalWriteDirect(7, HIGH);
    digitalWriteDirect(7, LOW);
    digitalWriteDirect(7, HIGH);
    digitalWriteDirect(7, LOW);
    digitalWriteDirect(7, HIGH);
    digitalWriteDirect(7, LOW);
    digitalWriteDirect(7, HIGH);
    digitalWriteDirect(7, LOW);
    digitalWriteDirect(7, HIGH);
    digitalWriteDirect(7, LOW);
    digitalWriteDirect(7, HIGH);
    digitalWriteDirect(7, LOW);
    digitalWriteDirect(7, HIGH);
    digitalWriteDirect(7, LOW);
    digitalWriteDirect(7, HIGH);
    digitalWriteDirect(7, LOW);
  }
  printTime( micros() - lastTime);
  Serial.println("digitalRead:");
  lastTime = micros();

  for (long i = 0; i < sampleSize; i++) {
    n = digitalRead(8);
  }
  printTime( micros() - lastTime);

  Serial.println("digitalReadFast:");
  lastTime = micros();
  for (long i = 0; i < sampleSize; i++) {
    n = digitalReadDirect(8);
  }
  printTime( micros() - lastTime);

}


void loop() {
  // put your main code here, to run repeatedly:

}

//-----------------------------------------------------------------------------
void printTime( long microsecs) {
  int mins, secs, msecs, usecs;

  msecs = floor(microsecs / 1000);
  usecs = microsecs % 1000;

  secs = floor(msecs / 1000);
  msecs = msecs % 1000;

  mins = floor(secs / 60);
  secs = secs % 60;
  Serial.print("time: ");
  Serial.println(microsecs);

  Serial.print(mins);
  Serial.print(" min ");
  Serial.print(secs);
  Serial.print(" sec ");
  Serial.print(msecs);
  Serial.print(" msec ");
  Serial.print(usecs);
  Serial.println(" usecs");
  Serial.println("");
  delay(20);
}

and unrolled the loops to take out the looping times as much as possible and to give my meter some time to get the frequency readings. I get these timings :
Sample size: 100000
digitalWrite:
time: 4810732
0 min 4 sec 810 msec 732 usecs

digitalWriteFast High/Low:
time: 648482
0 min 0 sec 648 msec 482 usecs

digitalWriteFast:
time: 77501
0 min 0 sec 77 msec 501 usecs

digitalRead:
time: 107290
0 min 0 sec 107 msec 290 usecs

digitalReadFast:
time: 26518
0 min 0 sec 26 msec 518 usecs
I find it hard to believe there is such a difference between digitalWriteFast High/Low and digitalWriteFast. What Am I missing.
Cheers
Mike

MorganS

"Such a difference"? It's not even twice as fast.

What you are missing is your code is now non-portable and non-futureproof and much less maintainable than when you started. A year from now this code which might have been useful will be thrown out and you will start again from scratch.

I'm not familiar with those arrays you used. I suspect that the array access is using at least 4 pointers per digitalWriteDirectHigh(), probably more because they're stored in program memory. There are faster and even less-maintainable methods. Depending on what you are really doing, they might be necessary, but this is unlikely. Optimise your algorithm first.
GoForSmoke: "What GShield? You never mentioned a shield."

Wurstnase

He mean the difference between digitalwritefast and digitamwritefasthigh/low. And there you have the function call overhead.

lesto

And there you have the function call overhead.
depends, probably the compiler has inlined it
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

jappiemike

MorganS, if you're not going to answer the question I asked, why bother posting? The functions I wrote are designed to take away the complexity of doing direct pin manipulation and are only used on the Due. What I'm trying to clear up is the 8.5 times time difference in what are 2 very similar functions.
Wurstnase, the overhead of the function calls should be the same for all the functions so that is not a big issue, certainly not enough to explain an 8.5 times difference in the time for the 2 kinds of functions.
lesto, the functions clearly state the functions are to be inlined.


Mike

MorganS

Hey! Who merged the threads? I was trying to (unsucessfully) answer Jappie's question and now it looks like he's posted something weird on an old thread that died two years ago.

Is there an un-merge?
GoForSmoke: "What GShield? You never mentioned a shield."

Go Up