Es sieht so aus als muss Serial.flush genutzt werden, damit alle Daten
im Sendepuffer auch immer gesendet werden.
In der Arduino Doku steht aber auch:
Serial.flush wartet bis alle ausgehenden Daten gesendet wurden
Nun habe ich im Arduino Source Code nachgesehen, dort steht tatsächlich:
void HardwareSerial::flush()
{
...
while (transmitting && ! (*_ucsra & _BV(TXC0)));
...
}
Serial.setTimeout hat also hier keinen Effekt
Das bedeutet aber doch, wenn sich z.B. das serielle Kabel lösen würde,
hängt sich hier das Programm auf. Was kann man da machen?
Alles was mir dazu einfällt, ist kompliziert: Timer-Interrupt nutzen, um
das hängen zu erkennen. HardwareSerial abändern und Serial.setTimeout in
flush einfügen ...
> Das bedeutet aber doch, wenn sich z.B. das serielle Kabel lösen würde,> hängt sich hier das Programm auf. Was kann man da machen?
Warum sollte es? Die UART schiebt die Daten raus, egal ob da nen Kabel
drin steckt oder nicht.
> Es sieht so aus als muss Serial.flush genutzt werden, damit alle Daten> im Sendepuffer auch immer gesendet werden.
Hab zwar nie Arduino genutzt, aber laut Doku ist nen flush nicht nötig
- es wird auch so alles gesendet. Mit flush wartet man nur, bis alles
gesendet wurde.
Meiner Erfahrung nach, liegt das Problem, das Anfänger anscheinden mit
nem flush beheben, üblicherweise woanders - nen flush ist nur in
seltenen Ausnahmen nötig.
Gestählt, durch langjährige Arduino Erfahrung, muss ich dem foobar
uneingeschränkt zustimmen.
Lothar schrieb:> Alles was mir dazu einfällt,...
Lothar, vielleicht solltest du mal sagen, welche Probleme dich wirklich
plagen. Denn höchst vermutlich buddelst du auf der falschen Baustelle.
foobar schrieb:> Anfänger
Da es um einen Port von 8051 auf Arduino geht bin ich auf Arduino wohl
tatsächlich Anfänger ...
Die Ursache warum es nur mit flush funktioniert ist vermutlich gefunden.
Diese serielle Schnittstelle ist über RS485 angeschlossen und es müssen
alle Daten aus dem Sendepuffer raus bevor der Transceiver umgeschaltet
wird.
Wird sogar hier erklärt:
https://www.mikrocontroller.net/articles/RS-485
Eine Überwachung mit Timer-Interrupt ist somit zum sicheren Betrieb mit
Timeout erforderlich.
Wird z.B. hier erklärt:
https://oscarliang.com/arduino-timer-and-interrupt-tutorial/
Da die Timer 0-2 von der Arduino Software genutzt werden - für millis
und so - und die Timer 3-5 frei sind auf dem Arduino MEGA kann ich also
den Timer 5 dafür nehmen. Jetzt muss ich als AVR Nicht-Nutzer "nur" noch
rausfinden, wie man auf dem ATMEGA2560 mit Timern arbeitet.
Es gibt zwar auch mehrere Arduino Timer Libraries. Wenn ich das aber
richtig verstanden habe, nutzen die alle millis - also Polling - und
würden sich mit dem Programm aufhängen.
Lothar schrieb:> Da die Timer 0-2 von der Arduino Software genutzt werden
Eigentlich nur Timer0
Lothar schrieb:> und es müssen> alle Daten aus dem Sendepuffer raus bevor der Transceiver umgeschaltet> wird.
Das ist ein guter Grund für einen Flush.
Was hast du denn jetzt für ein Problem mit den Timern?
Eben noch ein Flush Problem und jetzt Timer....
Wie hängt das zusammen?
Was willst du wirklich erreichen?
foobar schrieb:> Hab zwar nie Arduino genutzt,
Eine optimale Voraussetzung, hier Tipps zu geben :-(
> aber laut Doku ist nen flush nicht nötig - es wird auch so alles gesendet.> Mit flush wartet man nur, bis alles gesendet wurde.
Ich habe eine Anwendung, wo ich den Arduino kurz nach seiner Aktion
schlafen lege. Serial brauche ich nur für die Kalibrierung oder
Fehlersuche.
Hier gab es Ärger, dass der schlafen geht, bevor die Daten vollständig
raus sind - da hilft Serial.flush(), bevor dann
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); aus der "#include
"LowPower.h" zuschlägt.
Lothar schrieb:> Das bedeutet aber doch, wenn sich z.B. das serielle Kabel lösen würde, hängt
sich hier das Programm auf.
Das tut es hier nicht, Daten gehen stumpf raus, ob etwas dran hängt oder
nicht, ist egal.
Arduino Fanboy D. schrieb:> Was willst du wirklich erreichen?
Also wenn Du wirklich helfen willst. Ich hatte doch geschrieben:
Lothar schrieb:> Port von 8051 auf Arduino
Beim 8051 braucht es keinen Flush da kein Sendepuffer. Auf Arduino muss
nun aber diese Flush Funktion aufgerufen werden. In der Flush Funktion
hat man ein Loop ohne Timeout hinprogrammiert. Es braucht also eine
Möglichkeit im Programm festzustellen ob diese Flush Funktion zu Ende
ausgeführt wurde oder ob das Programm dort hängt.
Für sowas gibt es z.B. den Watchdog. Der würde aber einen Reset machen.
Ich würde einen Fehler ausgeben wollen. Dafür könnte man einen Timer vor
dieser Flush Funktion starten und danach stoppen. Wenn die Flush
Funktion hängt zeigt sich das in der Timer ISR - Timer läuft schon zu
lange.
Wenn Du eine bessere Lösung hast kannst Du es ja sagen.
Was keine Lösung ist: Natürlich hat die Flush Funktion beim Testen
bisher noch nie gehangen. Dieser unwahrscheinliche Fall muss aber
dennoch abgesichert werden. Meiner Meinung nach.
Warum man in der Arduino Software nicht einfach in Serial.flush auch den
Serial.setTimeout eingebaut hat - woher soll ich das wissen.
Lothar schrieb:> Also wenn Du wirklich helfen willst.
Bei einer solchen Ansage, sagt meine Empfindung:
Du willst keine Irrtümer einsehen.
So können wir beide nicht zusammen kommen.
Lothar schrieb:> Warum man in der Arduino Software nicht einfach in Serial.flush auch den> Serial.setTimeout eingebaut hat - woher soll ich das wissen.
Ich kanns dir sagen: Weil es völlig Sinn befreit wäre das zu tun.
Lothar schrieb:> Es braucht also eine> Möglichkeit im Programm festzustellen ob diese Flush Funktion zu Ende> ausgeführt wurde oder ob das Programm dort hängt.
Wenn Serial.flush() zurückkommt, ist es auch fertig.
Und es kommt auf jeden Fall zurück.
Es sei denn, die Interrupts sind abgeschaltet.
Und dann tuts auch dein Timer Interrupt nicht mehr
Das ist dann aber eine Dummheit des Programmierers.
Lothar schrieb:> Dafür könnte man einen Timer vor> dieser Flush Funktion starten und danach stoppen. Wenn die Flush> Funktion hängt zeigt sich das in der Timer ISR - Timer läuft schon zu> lange.
Habe ich dir schon widerlegt.
Lothar schrieb:> Dieser unwahrscheinliche Fall muss aber> dennoch abgesichert werden. Meiner Meinung nach.
Deine Meinung in Ehren.
Aber hier irrst du.
Klarer:
Du jagst deine eigenen Hirngespinste/Ängste ums Dorf.
(meine Meinung zu diesem Thema)
Nachtrag:
OK, der WDT kann hier seinen Einsatz bekommen.
Lothar schrieb:> Natürlich hat die Flush Funktion beim Testen> bisher noch nie gehangen. Dieser unwahrscheinliche Fall muss aber> dennoch abgesichert werden. Meiner Meinung nach.
Dann erklaere doch mal deine Meinung: Warum sollte flush(), deiner
Meinung nach, haengen? Wie soll das gehen?
Schau doch mal bitte in das Datenblatt des uC, lies was die Flags der
Schnittstelle machen und wie Schnittstelle ueberhaupt funktioniert. Dann
wirst du (hoffentlich) verstehen, das du versuchst ein Problem loesen,
das gar nicht existiert.
Arduino Fanboy D. schrieb:> Lothar schrieb:>> Es braucht also eine>> Möglichkeit im Programm festzustellen ob diese Flush Funktion zu Ende
ausgeführt wurde oder ob das Programm dort hängt.
>> Wenn Serial.flush() zurückkommt, ist es auch fertig.> Und es kommt auf jeden Fall zurück .
Lothar will es nicht begreifen und ist wohl auch nicht in der Lage, das
einfach zu probieren!
Ich schrieb es auch schon: Ich habe ein Gerät mit Serial.flush(), wo
seit über einem Jahr keine Schnittstelle angeschlossen ist, da hängt
nichts, das Ding funktioniert einfach vor sich hin. Die Daten gehen ins
Nirgendwo, der ProMini geht schlafen ... wäre das anders, wäre alle paar
Tage mein Akku leer.
Ich habe durchaus schon einiges probiert um zu sehen was da vor sich
geht. Wenn ich das so mache, dann würde ich erwarten, dass spätestens
nach dem delay das Byte raus ist:
Serial.write(msg[0]);
delay(1000);
Ist aber nicht so, erst mit flush geht das Byte raus:
Serial.write(msg[0]);
Serial.flush();
delay(1000);
Das ist wohl so wegen dem Sendepuffer in der Arduino Software. Aber
selbst das hier ändert daran nichts:
#define SERIAL_TX_BUFFER_SIZE 1
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/HardwareSerial.h
Lothar schrieb:> Wenn ich das so mache, dann würde ich erwarten, dass spätestens> nach dem delay das Byte raus ist:>> Serial.write(msg[0]);> delay(1000);>> Ist aber nicht so, erst mit flush geht das Byte raus:
Das kann ich nicht bestätigen.
Meine Tests zeigen, dass Serial keine Bytes zurückhält.
Alles wird sobald wie möglich Ausgegeben.
Arduino Fanboy D. schrieb:> Das kann ich nicht bestätigen.> Meine Tests zeigen, dass Serial keine Bytes zurückhält.> Alles wird sobald wie möglich Ausgegeben.
richtig
Das flush() bewirkt nur, dass die Ausführung des Code anhält bis das
letzte Zeichen gesendet wird.
Der Controller wartet bis der Sendebuffer wieder frei ist.
Thomas W. schrieb:> dass die Ausführung des Code anhält bis das> letzte Zeichen gesendet wird
Also ich habe mal gelernt dass es - egal ob bei PC Programmierung oder
Mikrocontroller - keine Funktion geben darf die das Programm anhält und
auf etwas wartet. Das ist Busy Waiting und das macht man nicht.
Wenn ich bei der PC Programmierung eine Bibliothek mit so einer Funktion
habe dann muss ich entweder dafür einen neuen Thread aufmachen damit das
Hauptprogramm nicht hängen bleiben kann. Oder einen Timeout Timer.
Bei Mikrocontroller habe ich mit Serial.flush jetzt zum ersten Mal eine
Funktion gesehen die ein Busy Waiting while drin hat.
> Der Controller wartet bis der Sendebuffer wieder frei ist
Also wenn ich das Datenblatt richtig verstanden habe ist das nicht so.
Der Controller hat nur ein Byte Senderegister. Wenn es dort rausgeht
gibt es ein Polling Flag oder einen Interrupt falls aktiv. Das hält das
Programm nicht an.
Serial.flush verwendet aber einen 64 Byte Sendebuffer in Software.
Daneben steht übrigens dieser Kommentar: "We're using a ring buffer (I
think)"
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/HardwareSerial.h
Lothar schrieb:> Serial.flush verwendet aber einen 64 Byte Sendebuffer in Software.
Ich meinte mit "Sendebuffer" schon den 64 Byte Sendebuffer
Lothar schrieb:> Also ich habe mal gelernt dass es - egal ob bei PC Programmierung oder> Mikrocontroller
Da gebe ich dir schon recht.
Aber trotzdem gibt es Fälle wo es durchaus Sinn macht, zu warten ...
Lothar schrieb:> Bei Mikrocontroller habe ich mit Serial.flush jetzt zum ersten Mal eine> Funktion gesehen die ein Busy Waiting while drin hat.
Das gibt's doch ständig. Alle möglichen Bibliotheken für Displays,
SD-Karten und somit auch FAT-Libraries, I²C und darauf basierende
Komponenten machen das so; insbesondere im Arduino-Umfeld. Selbst
analogRead wartet, bis eine ADC-Wandlung abgeschlossen ist. Das ist m.E.
der Hauptgrund, warum Embedded-Bibliotheken oft schlecht skalierbar bzw.
wiederverwendbar sind - wenn mehrere davon blockierende Funktionen
haben, beeinflussen die sich gegenseitig bzw. brauchen einfach zu viel
Zeit. Daher werden ja auch oft RTOS genutzt, um das Problem zu
umgehen...
Lothar schrieb:> Ist aber nicht so, erst mit flush geht das Byte raus:>> Serial.write(msg[0]);> delay(1000);
Ist vielleicht das delay(1000) zu kurz um auf alles zu warten? Setze vor
dem delay mal einen Pin, schließe ein Oszilloskop an diesen sowie den
UART-TX-Pin an und schau ob danach alles raus kommt.
Niklas G. schrieb:> analogRead wartet, bis eine ADC-Wandlung abgeschlossen ist
Habe ich bisher noch nicht genutzt aber - echt jetzt? Ich würde das so
machen dass ein analogRead den ADC startet und spätestens nach Timeout
eine globale Variable zurückgibt und diese globale Variable wird in
einer ISR vom ADC geupdatet wenn der fertig ist. Alles was dann
passieren kann ist dass analogRead noch den alten Wert zurückgibt wenn
der ADC noch nicht fertig war. Aber hängen kann es dann nicht.
Wenn ich analogRead mal verwende würde ich dann auch sicherheitshalber
einen Timeout Timer nutzen. Wie ich das bei Serial.flush auch grade
versuche.
Thomas W. schrieb:> Aber trotzdem gibt es Fälle wo es durchaus Sinn macht, zu warten ...
Schon möglich aber mein Hauptprogramm muss noch andere Dinge
zeitkritisch erledigen. Wenn es auf die serielle Schnittstelle warten
muss dann wenigstens mit Timeout.
Lothar schrieb:> Habe ich bisher noch nicht genutzt aber - echt jetzt?
Ich würde die Referenz so verstehen:
https://www.arduino.cc/reference/en/language/functions/analog-io/analogread/
It takes about 100 microseconds (0.0001 s) to read an analog input, so
the maximum reading rate is about 10,000 times a second.
Lothar schrieb:> Ich würde das so> machen...
Und dann beschwert sich wer, dass das API suggeriert, der ADC sei
schneller als er tatsächlich ist...
Lothar schrieb:> Wenn ich analogRead mal verwende würde ich dann auch sicherheitshalber> einen Timeout Timer nutzen. Wie ich das bei Serial.flush auch grade> versuche.
Das muss auch ohne gehen. Einen Timer nur für den UART abzustellen ist
overkill...
Gerade das Arduino Framework (ich nenne es mal so) zielt auf schnelle
Erfolge für unbedarfte Anwender.
Liefert Komfort Funktionen für ein breites Spektrum an µC.
Will man was spezielles, muss man es selber bauen.
Klarer:
Wenn dir die Serial Klasse nicht gefällt, dann nutze sie nicht!
Bau dir was eigenes.
Serial.print() und seine Brüder sind auch blockierend, wenn der
Ringpuffer voll ist.
Kehren also erst zurück, wenn alle übergebenen Zeichen im Ringpuffer
versenkt werden konnten.
Serial.availableForWrite() sagt dir wieviel Platz noch im Ringpuffer
ist.
Zusätzlich könnten noch ein(zwei?) Byte im UART Sendepuffer stecken.
---
analogRead() ist wirklich recht primitiv.
Nutzt kaum die Fähigkeiten der verschiedenen AVR ADC aus
Hat sogar noch einen Fehler im Bauch, bei der ADC Vorteiler Berechnung.
Bei 1, 8 und 16MHZ ist alles OK, aber bei krummen Frequenzen, z.B. bei
einem Baudrate Quarz kanns klemmen.
Wenn du möchtest, kann ich dir eine "verbesserte" ADC Lib zukommen
lassen.
Lothar schrieb:> Also ich habe mal gelernt dass es - egal ob bei PC Programmierung oder> Mikrocontroller - keine Funktion geben darf die das Programm anhält und> auf etwas wartet. Das ist Busy Waiting und das macht man nicht.>> Wenn ich bei der PC Programmierung eine Bibliothek mit so einer Funktion> habe dann muss ich entweder dafür einen neuen Thread aufmachen damit das> Hauptprogramm nicht hängen bleiben kann. Oder einen Timeout Timer.
Das ist so pauschal ja erstmal Unfug. "Ich hab das mal so gelernt...",
hast du das auch mal hinterfragt? Ganz einfaches Beispiel:
Ein Konsolenprogramm das eine Nutzereingabe erwartet. Natuerlich
blockiert das Programm an der stelle und wartet bis der Nutzer seine
Eingabe gemacht hat. Warum sollte man fuer sowas einen Thread aufmachen
(und was soll der Rest des Programms in der Zeit machen?), oder
irgendwelche Timer nutzen? Wenn das Programm ohne Nutzereingabe nicht
weitermachen kann, dann muss es halt an der Stelle warten.
Hallo,
natürlich kann man auch auf einem Microcontroller einen Timer laufen
lassen, der zyklisch nachschaut, ob der Sendebuffer endlich leer ist.
Dazwischen kann man entweder in der loop() auf die Fertigmeldung des
Timer warten (wie sinnvoll in disem Fall...), man kann natürlich auch in
der Wartezeit auch die Mining-Funktiuon für die Bitcoins laufen lassen..
PS: mir fällt in vielen Arduinoprogrammen auf, daß die serielle stur auf
den default 9600 Baud gelassen wird. 38400 und 76800 gehen mit der
gleichen geringen Fehlerrate, 115200 üblicherweise auch noch, obwohl der
Fehler da mit 2,1% dicht an der Grenze ist.
Schon bei 38400 ist der Sendebuffer in einem viertel der Zeit leer...
Gruß aus Berlin
Michael
Michael U. schrieb:> natürlich kann man auch auf einem Microcontroller einen Timer laufen> lassen, der zyklisch nachschaut, ob der Sendebuffer endlich leer ist.> Dazwischen kann man entweder in der loop() auf die Fertigmeldung des> Timer warten (wie sinnvoll in disem Fall...), man kann natürlich auch in> der Wartezeit auch die Mining-Funktiuon für die Bitcoins laufen lassen..
Ja und nein...
Im Falle Arduino wird der SendePuffer auf jeden Fall irgendwann leer.
Es sei denn die Interrupts sind aus, aber dann hilft auch kein Timer
Interrupt mehr.
Michael U. schrieb:> PS: mir fällt in vielen Arduinoprogrammen auf, daß die serielle stur auf> den default 9600 Baud gelassen wird. 38400 und 76800 gehen mit der> gleichen geringen Fehlerrate, 115200 üblicherweise auch noch, obwohl der> Fehler da mit 2,1% dicht an der Grenze ist.
Einerseits hast du natürlich recht...
Aber ist denn immer eine hohe Geschwindigkeit sinnvoll?
Ok.. die Kabellänge spielt keine Rolle, wenn der USB Adapter mit auf dem
Board steckt.
Andererseits gibt es auch keine Flusskontrolle, weder in Hardware, noch
Software. Da kann es schon passieren, dass die Verarbeitungszeiten auf
dem µC zu lang sind, um hohe Datenraten zu verarbeiten.
Und das letzte, vielleicht (für mich) wichtigste Argument, für dauerhaft
9600Baud, man muss nicht dauernd umstellen.
Aber, wie fast immer:
Die Anforderungen bestimmen die Grenzen und sinnvollen Werte.
Kaj schrieb:> Ein Konsolenprogramm das eine Nutzereingabe erwartet
Auf einem Mikrocontroller? Und da soll nix währenddessen passieren? Wie
Batterie prüfen, irgendwelche I/Os nachsehen. Hatte ich noch nie. Ich
habe z.B. aktuell - nicht auf Arduino - einen PID-Regler dem man seriell
die Parameter ändern kann. Das wäre nicht so gut wenn der während der
seriellen Kommunikation nix macht.
Beim PC ist es sogar zwingend für serielle Kommunikation einen Timeout
oder Thread zu machen. Führt zwar jetzt hier zu weit aber damit das
Programm nicht hängt oder merkwürdige Sachen macht braucht es entweder -
einfach - DoEvents oder - mehr Aufwand aber gerne gesehen - Invoke:
asynchroner Thread.
Die einzige Möglichkeit das zu vermeiden wäre das Konsolenprogramm in
eine Datei schreiben zu lassen und das davon getrennte Hauptprogramm die
Datei zu prüfen und es dort wenn fertig abzuholen. Das ist aber nicht
mehr modern.
Hallo,
Arduino Fanboy D. schrieb:> Michael U. schrieb:>> natürlich kann man auch auf einem Microcontroller einen Timer laufen>> lassen, der zyklisch nachschaut, ob der Sendebuffer endlich leer ist.>> Dazwischen kann man entweder in der loop() auf die Fertigmeldung des>> Timer warten (wie sinnvoll in disem Fall...), man kann natürlich auch in>> der Wartezeit auch die Mining-Funktiuon für die Bitcoins laufen lassen..>> Ja und nein...> Im Falle Arduino wird der SendePuffer auf jeden Fall irgendwann leer.> Es sei denn die Interrupts sind aus, aber dann hilft auch kein Timer> Interrupt mehr.
Das bezig sich einerseits auf den Ausgangspunkt, daß man den AVR
schalfen schicken will, da ist dann auf Dauer direkt Batteriekapazität.
>> Michael U. schrieb:>> PS: mir fällt in vielen Arduinoprogrammen auf, daß die serielle stur auf>> den default 9600 Baud gelassen wird. 38400 und 76800 gehen mit der>> gleichen geringen Fehlerrate, 115200 üblicherweise auch noch, obwohl der>> Fehler da mit 2,1% dicht an der Grenze ist.>> Einerseits hast du natürlich recht...> Aber ist denn immer eine hohe Geschwindigkeit sinnvoll?>> Ok.. die Kabellänge spielt keine Rolle, wenn der USB Adapter mit auf dem> Board steckt.> Andererseits gibt es auch keine Flusskontrolle, weder in Hardware, noch> Software. Da kann es schon passieren, dass die Verarbeitungszeiten auf> dem µC zu lang sind, um hohe Datenraten zu verarbeiten.
Mit 115200 habe ich real auch nie ein Problem gehabt, wenn der
USB-Wandler per Dupontkabeln o.ä. angeschlossen war. Von ewig langen
Leitungen rede ich hier natürlich nicht, eben für Debug-Zwecke.
Ich habe schon mehrfach den Zustand gehabt, daß zu umfangreiche
Debug-Ausgaben letztlich meinen Ablauf ausgebremst haben. Auch
Serial.print() wartet, wenn der Sendebuffer voll ist...
Die Verarbeitungszeit spielt erst beim Empfangen eine Rolle, wenn mehr
Zeichen kommen als der serielle Buffer aufnehmen kann ohne daß sie
abgeholt werden.
Beim Senden muß der Empfänger damit klarkommen. Wenn ich auf dem AVR
eben nur selten Bytes sende, sind die Pausen dazwischen eben länger.
> Und das letzte, vielleicht (für mich) wichtigste Argument, für dauerhaft> 9600Baud, man muss nicht dauernd umstellen.
Ist jeweils meine erste Änderung in fremden Sketchen, die ArduinoIDE
merkt sich das sowieso und steht damit immer auf den 115200...
> Aber, wie fast immer:> Die Anforderungen bestimmen die Grenzen und sinnvollen Werte.
So ist es.
Gruß aus Berlin
Michael
Das gilt sowieso immer