Wieso schafft der Atmel nur 88khz am Ausgang?

Hab bissle rumgespielt und komme auf folgendes Phänomen

Man kann einen Ausgang mit maximal 88,75 bis 89 KHz takten. Schneller läuft das Programm nicht.

int LEDpin3 = 12;
int LEDpin2 = 11;
int LEDpin1 = 10;

void setup() {
  // put your setup code here, to run once:
pinMode(LEDpin1, OUTPUT);
pinMode(LEDpin2, OUTPUT);
pinMode(LEDpin3, OUTPUT);
}

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

digitalWrite(LEDpin3,HIGH);
digitalWrite(LEDpin3,LOW);

}

Das selbe dann nochmal etwas modifiziert

int LEDpin3 = 12;
int LEDpin2 = 11;
int LEDpin1 = 10;

void setup() {
  // put your setup code here, to run once:
pinMode(LEDpin1, OUTPUT);
pinMode(LEDpin2, OUTPUT);
pinMode(LEDpin3, OUTPUT);
}

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

digitalWrite(LEDpin3,HIGH);
digitalWrite(LEDpin1,HIGH);
digitalWrite(LEDpin3,LOW);
digitalWrite(LEDpin1,LOW);

}

Ergebniss nur noch 38,75KHz

Am Oszi sieht man zwischen den positiven Flanken von 3 und 1 8µsec Zeit vergehen. Bei 16MHz sind das 128 Takte. was macht den der Microcode im Controller da alles um einen Ausgang einzuschalten?

Zwischendrin sieht man noch irgendwelche Interrupts, weil das Signal nicht konstant ist. Es hat ab und zu längere Offphasen, was aber erstmal egal ist. Wäre eh nicht zu ändern.

Jedenfalls sind 128 Takte doch ne ganze Menge. Oder mess ich da was falsch? Vieleicht kann jemand mit Oszi das mal prüfen

Achja, hab einen Arduino Nano Clone mit 328p

Ich weiß nicht ob der zitierte Code hier aktuell ist, aber schau Dir mal an, was digitalWrite so alles macht ...

digitalWrite() ist recht aufwendig und dauert etwa 4µs. Wenn es schneller gehen soll, spreche die I/Os über die Register an.

Oder verwende das:

Serenifly:
Oder verwende das:
Arduino-Libs/digitalWriteFast/digitalWriteFast.h at master · watterott/Arduino-Libs · GitHub

Ah, scheint eine (Rück-)Umsetzung der entsprechenden Funktion vom Teensy zu sein.

Ich habe mich mal irgendwann um schnelle Portzugriffe bemüht:
https://forum.arduino.cc/index.php?topic=371923
Vielleicht kannste dir da ja was abschauen....

Jedenfalls sind 128 Takte doch ne ganze Menge. Oder mess ich da was falsch? Vieleicht kann jemand mit Oszi das mal prüfen

Done!

Ok, das digitalwrite soviel gedöns aussenrum macht hatte ich nicht erwartet, dachte das es deutlicher hardwarenahe arbeitet.

Also direkt das Register anspricht nachdem es sich die zwei Parameter geholt hat. Dann wären das ein paar Takte, vieleicht 10-15.

Aber mit deiner Bibliothek sieht das dann anders aus. War mir auch nicht bewusst, das wenn wir Pin 0-13 aufrufen letztendlich erstmal ermittelt werden muss welches Register und welcher Pin. Was ja auch takte kostet.

Dein lib werde ich mir auch gleich mal ziehen und zu meinen Favoriten hinzufügen. Danke

Hallo,

das ist der Preis des Arduino IDE Komforts. Egal welcher Arduino, Pin 11 ist immer der Pin mit der Board Nummer 11. Abseits davon muß man sich selbst mit Registern rumschlagen und beschäftigen. Bei höheren Frequenzen die zusätzlich stabil sein sollen empfehle ich einen Timer zu verwenden.

Natürlich ist digitalWrite langsam und muss es auch sein, aber wenn du

int LEDPin1=13;

schreibst, wird es sogar langsamer sein als richtig mit [b] const [/b]int  LEDPin1=13;. Der erste "Schuldige" bist also du selbst :wink:

Einen Mittelweg zwischen Arduino-hardwareunabhängig und schnell findest du, wenn du nachdigitalWriteFast suchst.

Dass das Problem eher in der Fragestellung "Wieso schafft der Atmel nur 88khz am Ausgang?" liegt, sollte eigentlich klar sein.

als richtig mit const int LEDPin1=13;.

const byte  LEDPin1=13;

:smiling_imp: :smiling_imp: :smiling_imp:

combie:
:smiling_imp: :smiling_imp: :smiling_imp:

Punkt für dich. Allerdings bügelt das der Compiler aus (schon in meiner Version 1.6.7)

#define TYPE byte  // oder int
const TYPE LED=13;
void setup() {
pinMode(LED, OUTPUT);

}

void loop() {
  while (true) {  
    digitalWrite(LED, HIGH);
    digitalWrite(LED, LOW);
  }
}

Der Code ist in beiden Fällen (byte oder int) gleich groß ...

Der Code ist in beiden Fällen (byte oder int) gleich groß ...

Ja, das ist er!
:wink:

michael_x:
Natürlich ist digitalWrite langsam und muss es auch sein, aber wenn du

int LEDPin1=13;

schreibst, wird es sogar langsamer sein als richtig mit [b] const [/b]int  LEDPin1=13;. Der erste "Schuldige" bist also du selbst :wink:

Einen Mittelweg zwischen Arduino-hardwareunabhängig und schnell findest du, wenn du nachdigitalWriteFast suchst.

Dass das Problem eher in der Fragestellung "Wieso schafft der Atmel nur 88khz am Ausgang?" liegt, sollte eigentlich klar sein.

Naja, 128 Takte in meiner Version. Wieviel Takte werden es weniger?

Mich wundert es schon, das man mit 16 MHz und einem einfachen Bit schreiben soviel Takte braucht. Wenn ich einen 8255 beschreibe von einem 6502 aus, der mit 1 MHz getaktet ist komme ich auch schon auf 100KHz. Allerdings in Assembler.

Ich teste heute im Büro mal, wieviel es schneller wird, wenn man const byte nimmt.

nächster Versuch

const int LEDpin = 12;

void setup() {
  // put your setup code here, to run once:
pinMode(LEDpin, OUTPUT);
}

void loop() {

digitalWrite(LEDpin,HIGH);
digitalWrite(LEDpin,LOW);

}

Frequenz 95 khz
854 byte Programm
9 Byte Variable

nächster Versuch

byte LEDpin = 12;

void setup() {
  // put your setup code here, to run once:
pinMode(LEDpin, OUTPUT);
}

void loop() {

digitalWrite(LEDpin,HIGH);
digitalWrite(LEDpin,LOW);

}

Frequenz 94 khz
884 byte Programm
11 Byte Variable

Noch ein versuch

void setup() {
  // put your setup code here, to run once:
pinMode(12, OUTPUT);
}

void loop() {

digitalWrite(12,HIGH);
digitalWrite(12,LOW);

}

Frequenz 95 khz
854 byte Programm
9 Byte Variable

So wirklich viel mehr ist es nicht geworden, aber ein wenig.

Wieso macht Digitalwrite soviel Overhead. Immerhin ist es dir Primärfunktion, die sollte hochoptimiert sein. Egal...es sind einfach Dinge die man sonst nicht liest und wo man sich dann eben völlig falsche Vorstellungen macht was der Arduino schafft und was nicht. 3 Stepper betreiben mit reinen ausgangstreibern ist da ja schon Grenzwertig. Weil bei nur 240U/min schon ein Takt von über 3KHz nötig ist. Und bereits das Abfragen der Zeit kostet dich wieder 20KHz.

long int zeit;
void setup() {
  // put your setup code here, to run once:
pinMode(12, OUTPUT);
}

void loop() {

digitalWrite(12,HIGH);
zeit = micros();
digitalWrite(12,LOW);

}

Frequenz 72 khz
944 byte Programm
13 Byte Variable

Nun muss man aber nicht nur Zeit abfragen, sondern bearbeiten.

Gehen wird es, wenn man timer dazu benutzt. Schnell darf man aber nicht werden.

Hallo,

chefin:
Wieso macht Digitalwrite soviel Overhead. Immerhin ist es dir Primärfunktion, die sollte hochoptimiert sein.

Das gehört zu den Mysterien für die ich auch noch keine Erklärung gefunden habe.

Merkwürdigerweise hat die Konkurrenz von ARM mbed die gleiche Krankheit. Auch da gibt es alternative Libs für schnelleres IO.

Aber auch auf Controller Ebene finden man da Unterschiede. Bei den AVRs ist es ja wohl so, dass man ein IO-Bit ändert, in dem man das Portregister liest, das Bit ändert und wieder zurückschreibt. Die Kinetis Controller bei den Teensy Boards haben zusätzlich auch "Portsetz" und "Portrücksetz" Register. Man kann z.B. ein Bit löschen, in dem man ins entsprechende Rücksetzregister an der Stelle eine 1 schreibt. Den Rest erledigt die Hardware. Das spart wieder ein paar Takte.

jep, genau so dachte ich mir das auch irgendwie. Ich konnte am C64 in 256Byte RAM einen Kopierschutzcrack schreiben, der on the fly den Kopiershutz aushebelt.

Ghostbuster auf dem C64 hat Sprachausgabe geschafft, indem es in einen Soundchip 3 Werte geschrieben hat pro Sample und immerhin 5000/s Samples damit erreicht, WÄHREND die Grafik noch bewegt wurde. Und das mit einem 1MHz Prozessor.

Es verwundert halt, wenn das 30 Jahre später (scheisse ist das lange her) nicht wirklich signifikant besser ist trotz 16MHz. Will damit nicht sagen, es ist der Weltuntergang und es dürfte 99,9% der Projekte nicht mal berühren, weil zwischen 2 Useraktivitäten immer noch hundertausende von Takten liegen. Es wundert halt nur, weil man im Programm eher Zeitverlust beim Rechnen und Verzweigen erwartet.

long int zeit;
void setup() {
  // put your setup code here, to run once:
pinMode(12, OUTPUT);
}

void loop() {

digitalWrite(12,HIGH);
zeit = micros();
zeit = zeit / 2;
digitalWrite(12,LOW);
zeit = micros();
zeit = zeit / 2;

}

Und schon ist man bei 10KHz unten.

Ich denke da ist noch sehr viel Luft für Optimierung.

Wer würde mal auf alternativen IDEs testen. Könnte ja sein, das andere Programmieroberflächen es besser optimieren beim Compilieren. Ich glaube es gibt da einige, ich habe aber noch nichts getestet davon.

Ich habe mir als Projekt für die dunkle Jahreszeit mal einige Vergleichstests vorgenommen.

Beim Ausführen des gleichen Sketches auf Uno, Micro oder Teensy waren mir einige deutliche Zeitunterschiede aufgefallen, die müssen noch genauer erforscht werden. (Bei manchen Dingen ist der Teensy 40x schneller.)

Einen alternativen Compiler für AVR habe ich aber nicht. Neben der Arduino IDE habe ich auch Platformio im Einsatz, aber da müsste der gleiche Compiler drin sein.

Bei den 32 Bittern habe ich mehr Vergleichsmöglichkeiten, speziell bei mbed. Die Online IDE verwendet den Keil Compiler, Platformio den gleichen g++ wie die Arduino IDE (4.8.?) und mbed cli den g++ 5.4.

chefin:
Es wundert halt nur, weil man im Programm eher Zeitverlust beim Rechnen und Verzweigen erwartet.
....

Ich denke da ist noch sehr viel Luft für Optimierung.

Darf ich dir eine These liefern?
Du bist zu klug dafür! (und dann auch noch bockig)
Darum stellst du auch die falschen Fragen.

Und, weil du es offensichtlich noch nicht gemacht hast:
Schau doch mal in den Quelltext.
Der Code von digitalWrite() gehört zu Lieferumfang, ist also bei jeder Arduino IDE dabei.
Er findet sich bei mir in: E:\Programme\Arduino\hardware\arduino\avr\cores\arduino\wiring_digital.c

Ich habe das mal für dich getan:
(ist gelogen, denn ich habs vor langer Zeit aus egoistischen Interesse gemacht)

Bei jedem digitalWrite() werden folgende Dinge erledigt:

  1. holen der PortAdresse
  2. holen der Pin Maske
  3. betreffenden Timer herausfinden
  4. Prüfen, ob das ein PWM Pin ist
  5. stoppen des PWM Generators/Timers
  6. setzen des Ausgangspins

Schaut man sich den Code an, sieht man, dass da nix mehr zu optimieren ist.
Die Aufgaben 1 bis 6 werden zügig gelöst.
Der Code funktioniert unverändert, für alle AVR basierten Arduinos.
Er ist also recht portabel.

Wer würde mal auf alternativen IDEs testen. Könnte ja sein, das andere Programmieroberflächen es besser optimieren beim Compilieren. Ich glaube es gibt da einige, ich habe aber noch nichts getestet davon.

Wie auch immer....
Es ist kein Problem des Kompilers!
Auch nicht der Optimierung.
Und auch nicht der IDE.

Der Programmierer hat das getan, was er tun sollte.

:o :o :o :o :o :o :o :o

combie:
Bei jedem digitalWrite() werden folgende Dinge erledigt:

  1. holen der PortAdresse
  2. holen der Pin Maske
    ...
    Schaut man sich den Code an, sieht man, dass da nix mehr zu optimieren ist.

Hm, das könnte man in C++ (wo wir ja sind) schon optimieren, in dem man die Funktion für konstante und nicht konstante Parameter überlädt. Wenn digitalWrite mit einem konstanten Pinwert aufgerufen wird, könnte man die PortAdresse und PinMaske auch zur Übersetzungszeit berechnen.

Wenn digitalWrite mit einem konstanten Pinwert aufgerufen wird, könnte man die PortAdresse und PinMaske auch zur Übersetzungszeit berechnen.

Das zeige mir doch mal..... bitte...
Für meine [Projekt] Schnelle Digital Eingabe/Ausgabe - Deutsch - Arduino Forum könnte ich das gut gebrauchen

Ich denke es gibt drei Wege. Ein fertiges digitalWrite habe ich aber nicht auf die Schnelle.

Variante 1: Templates

In mbed kann man ja schreiben

FastIn<D5, PullUp> input;

Pins sind da Objekte wie Serial bei Arduino, aber das macht keinen Unterschied, man kann auch Funktionen mit Templateparametern schreiben. Die werden dann schon zur Übersetzungszeit ausgewertet.

Eventuell gibt es da schon fertigen Code für Arduino irgendwo.
[Edit]
z.B.
http://forum.arduino.cc/index.php?topic=86931.0
]

Buchempfehlung: "Real-Time C++" von Christopher Kormanyos

Umsetzung für mbed

https://developer.mbed.org/users/igorsk/notebook/fast-gpio-with-c-templates/

Variante 2: constexpr Funktion für die Portmakse usw., wenn der Compiler der Arduino IDE das schon mitmacht.

Einstieg
http://en.cppreference.com/w/cpp/language/constexpr

Variante 3: Einfaches Überladen des Parameters, wie oben gesagt. War erstmal nur so ein Gedanke, bin mir nicht sicher, ob das an die anderen Varianten heranreicht.