Hallo Allerseits,
ich habe folgendes Problem. Ich habe ein System mit 6 DC12V Motoren,
jeweils mit einer HAL Sensor. Da Stepper Motoren von der Leistung her
nicht gereicht haben, baue ich ein Portal (wie bei einer 3D drucker)
statt mit stepper auf DC Motorenbasis.
Da ich 5 Motoren habe und eigentlich 10 Interrupts benötige aber Arduino
nur 2 und Mega 6, benutze ich eine Mega. Eine ESP32 kommt aufgrund von
wenige GPIO Anzahl nicht in Frage: Ich benutze derzeit alle PWM Pins, 5
Interrupt Pins, alle digitalen PINS bis auf 6 Stück und weil auf der PCB
es günstig gelegen ist, verwende ich 5 der AnalogInputs als Digital
Input für 4 HX711.
Da ich nur 6 External Interrupts habe und eigentlich 12 bräuchte,
verwende ich die günstige Lösung, wo ich die steigende Flanke Interrupts
und den Zustand der anderen HAL Ausgang des Motors abfrage. Siehe Code:
Pin Definition:
und die altbekannte auf die Steigende Flanke hören Lösung:
1
template <int MotorIndex>
2
void handleEncoderMotor()
3
{
4
if (digitalRead(hallPins[MotorIndex]) == LOW)
5
{
6
hallCounters[MotorIndex]++;
7
}
8
else
9
{
10
hallCounters[MotorIndex]--;
11
}
12
}
Das ist, wer hätte gedacht, deutlich unzuverlässiger als ich antizipiert
habe. Es funktioniert gerade mal 20% der Zeit. Die Unzuverlässigkeit
zeigt sich, dass die hallCounter immer ++ gemacht wird, da hallPins[0/1]
immer LOW sind (nicht bestätigt weil Osci nicht vorhanden).
Hier (https://www.mikrocontroller.net/articles/Drehgeber) steht, dass
man es nicht so macht, geht aber keine Lösung für mein Problem hervor.
Hier (Beitrag "Re: Drehknopf mit Arduino auslesen") hat jemand
vollständig auf Interrupts verzichtet. Bei einem Umfeld, wo während der
Motor läuft, auch andere Sachen passieren werden (Lichtschranke und
Endschalter auslesen, mit HX711 wiegen etc.) ist es so sicher wie Amen
in der Kirche, dass ich Steps verlassen werde...
Mir ist gerade die Kreativität ausgegangen, muss ich gestehen. Mehrere
Arduinos für die 12 Interrupt pins? Jedes Motor hat ein eigener Arduino
Nano? Würde theoretisch funktionieren, wenn da nicht die Synchronisation
von zweier Paare wären (Vor Zurück so wie Hoch runter Achsen werden von
zwei Motoren betrieben). Da müsste ich ja die Sychronisationinformation
über i2c oder serial senden und das wird vermutlich relativ schnell
unsychnron (oder mache ich mir vielleicht zu viele Gedanken?)
Vielleicht ist auch Arduino die falsche Lösung? Gibt es eine external
Interrupt extender chip, wie es für die GPIO gibt? Ich habe nicht nur
nichts gefunden, viel mehr kann ich mir technisch nicht vorstellen, wie
so was gehen sollte: Ein Interrupt Pin wird belegt und der liest den
Zustand über die SPA / I2C, was schnarch langsam ist? Kann ich mir nicht
vorstellen. Wie geht ihr in euren Projekten vor, die mehr als 2 / 6
Interrupts benötigen und viele GPIO Pins notwendig haben.
Vielleicht wichtige Nebeninfo: Das sind Motoren mit einer Getriebe davor
und haben 8000rpm = 133 triggers pro Sekunde. Sollte jetzt nicht
unbedingt die Arduino in die Knie zwingen...
Viele Grüße
Stefan
Stefan schrieb:> Hier (Beitrag "Re: Drehknopf mit Arduino auslesen") hat jemand> vollständig auf Interrupts verzichtet.
Hast Du Dir mal Gedanken über die Frequenz gemacht, mit der Deine
Interrupts da einlaufen?
Und, nebenbei, hast Du verifiziert, daß Deine Template-ISR genauso
schnell ist wie zwei separate ISRs?
Harald K. schrieb:> Und, nebenbei, hast Du verifiziert, daß Deine Template-ISR genauso> schnell ist wie zwei separate ISRs?
Lohnt sich auf jeden Fall mal zu probieren. Danke für die Gedankenstoß.
Harald K. schrieb:> Hast Du Dir mal Gedanken über die Frequenz gemacht, mit der Deine> Interrupts da einlaufen?
Klar: Ich würde jetzt mal behaupten, dass 133 Hz * 2 (weil zwei Motoren)
bei einer 20MHz Prozessor erstmal irrelevant sei. Denn aber lasse ich
mich gerne von Gegenteil überzeugen.
Stefan schrieb:> Da ich 5 Motoren habe und eigentlich 10 Interrupts benötige aber Arduino> nur 2 und Mega 6,
Unfug. Beide haben DEUTLICH mehr interruptfähige Pins, praktisch ALLE
Pins können den Pin Change Intzerrupt. Nur daß das eben von der
Aduino-IDE nicht untestützt wird. Muss man selber "per Hand" machen.
> Da ich nur 6 External Interrupts habe und eigentlich 12 bräuchte,> verwende ich die günstige Lösung, wo ich die steigende Flanke Interrupts> und den Zustand der anderen HAL Ausgang des Motors abfrage.
Was sollen denn deine Interrupts überhaupt machen? Laß mich raten?
Drehgeber dekodieren? Wenn ja, macht man das so oder so anders.
Nämlich mit einem Timer-Interrupt und periodischer Abfrage. Siehe
Artikel Drehgeber.
> Das ist, wer hätte gedacht, deutlich unzuverlässiger als ich antizipiert> habe.
Soso, du antipizierst also? Die Germanen erwarten da eher was.
> Es funktioniert gerade mal 20% der Zeit. Die Unzuverlässigkeit> zeigt sich, dass die hallCounter immer ++ gemacht wird, da hallPins[0/1]> immer LOW sind (nicht bestätigt weil Osci nicht vorhanden).
Also reine Spekulation. So sieht eine saubere Problemanalyse aus. Hab
ich voll antizipiert . . .
> Hier (https://www.mikrocontroller.net/articles/Drehgeber) steht, dass> man es nicht so macht, geht aber keine Lösung für mein Problem hervor.
Bist du blind?
> Hier (Beitrag "Re: Drehknopf mit Arduino auslesen") hat jemand> vollständig auf Interrupts verzichtet. Bei einem Umfeld, wo während der> Motor läuft, auch andere Sachen passieren werden (Lichtschranke und> Endschalter auslesen, mit HX711 wiegen etc.) ist es so sicher wie Amen> in der Kirche, dass ich Steps verlassen werde...
Was? Wu meinst wohl eher, daß du Schritte auslassen könntest bzw. nicht
erfassen könntest?
> Mir ist gerade die Kreativität ausgegangen, muss ich gestehen. Mehrere> Arduinos für die 12 Interrupt pins?
Blödsinn^3.
> unsychnron (oder mache ich mir vielleicht zu viele Gedanken?)
Mach es einfach wie der Rest der Welt. Normal mit bewährten Konzepten!
> Vielleicht ist auch Arduino die falsche Lösung?
Nö. Der kann das locker, wenn man nur weiß wie.
> Gibt es eine external> Interrupt extender chip, wie es für die GPIO gibt?
Unsinn.
> Vielleicht wichtige Nebeninfo: Das sind Motoren mit einer Getriebe davor> und haben 8000rpm = 133 triggers pro Sekunde. Sollte jetzt nicht> unbedingt die Arduino in die Knie zwingen...
Schnarchlangsam. Ein 1kHz Timer-Interrupt reicht da locker. Und
wenn es 2-5kHz sein müssen geht das auch.
Roland E. schrieb:> Klar, für einen bare metal MuC ist das Pillepalle. Beim Arduino ist doch> aber noch ein OS dazwischen. Da wird alles über 1kHz zur Glückssache...
Auf einem einfachen AVR-Arduino läuft keinerlei OS, bestenfalls ein 1kHz
Timer.
Roland E. schrieb:> Klar, für einen bare metal MuC ist das Pillepalle. Beim Arduino ist doch> aber noch ein OS dazwischen. Da wird alles über 1kHz zur Glückssache...Beitrag "Re: Arduino Nano, SD Card, PCM"
22kHz PWM Audioausgabe direkt von SD-Karte auf einen normalen Ardino
Uno/Mega.
Soweit ich weiß ist doch bei dem 328P jeder Pin mit dem
PinChange-Interrupt nutzbar oder nicht? Bzw das Flag wird dann für das
entsprechende Portregister gesetzt....
Stefan schrieb:> Klar: Ich würde jetzt mal behaupten, dass 133 Hz * 2 (weil zwei Motoren)> bei einer 20MHz Prozessor erstmal irrelevant sei.
Ja, nur ein paar hundert Hertz sollte Dein 8-Bit-AVR problemlos
schaffen. Tut er das nicht, geht irgendwo in Deinem Code etwas schief.
Aber um in diesem Frequenzbereich zyklische Signale zu erfassen, braucht
man wirklich keine Interrupts (außer einen schnelleren Timer-Interrupt).
Das kann man problemlos mit Polling abwickeln, denn ein paar hundert
Hertz sind, wie hier schon andere schrieben, schnarchlahm.
Harald K. schrieb:> Und, nebenbei, hast Du verifiziert, daß Deine Template-ISR genauso> schnell ist wie zwei separate ISRs?
Das hat mit Templates gar nichts zu tun.
Natürlich sind 2 Funktionen genauso schnell, wie eine Template Funktion.
Schließlich werden auch so 2 Funktionen generiert.
Was aber negativ zu buche schlägt, sind die CallBack des
attachInterrupt(), denn die führen zu Ellen langen Push Pop Kaskaden.
Hallo Stefan,
Stefan schrieb:> Da ich 5 Motoren habe und eigentlich 10 Interrupts benötige aber Arduino> nur 2 und Mega 6, benutze ich eine Mega.
Das ist ein Mißverständnis. Die ATmega-Mikrocontroller haben zwei Arten
von Pin-Interrupts. Zum einen sind da die dedizierten Interrupt-Pins
(INT0 und INT1 beim ATmega328P, INT0 bis INT7 beim ATmega2560). Zum
anderen gibt es die Pin-Change-Interrupt-Pins PCINT0 bis PCINT23 (sowohl
beim ATmega328P als auch beim ATmega2560).
Der Unterschied ist, dass bei den Pin-Change-Interrupts nicht jeder Pin
eine eigene Interrupt-Routine hat, sondern jeweils 8 zusammengefasst
werden. Und außerdem müssen die Pin-Change-Interrupts "händisch"
programmiert werden, weil attachInterrupt() nur die dedizierten
Interrupts unterstützt.
Das ist auf dieser Seite ganz gut erläutert:
http://gammon.com.au/interrupts.
INT6 und INT7 sind auf dem Arduino Mega übrigens nicht nutzbar, weil die
Pins PE6 und PE7 des Mikrocontrollers nicht verbunden sind, siehe hier:
https://content.arduino.cc/assets/MEGA2560_Rev3e_sch.pdf.
Ich dachte, das sei für dich vielleicht interessant, ganz unabhängig von
der Diskussion über den besseren Ansatz mit einem Timer-Interrupt.
LG, Sebastian
Roland E. schrieb:>> Klar, für einen bare metal MuC ist das Pillepalle. Beim Arduino ist doch>> aber noch ein OS dazwischen. Da wird alles über 1kHz zur Glückssache...Falk B. schrieb:> Beitrag "Re: Arduino Nano, SD Card, PCM"> 22kHz PWM Audioausgabe direkt von SD-Karte auf einen normalen Ardino> Uno/Mega.
Da hast du alles Zeitkritische aber am Framework vorbei programmiert
(hätte ich auch gemacht).
Damit hast du den Roland bestätigt:
>> für ... bare metal ... ist das Pillepalle
Jedenfalls lassen sich die I/O Pins alle absolut zuverlässig lesen, auch
mit digitalRead(). Du hast da ein ganz andere Problem. Ich würde die
Signale mit einem Oszilloskop überprüfen. In der Nähe von Motoren sind
Überraschungen nicht selten, vor allem wenn man das noch nicht kennt und
daher das Thema "Leitungsführung" vernachlässigt hat.
Stefan schrieb:> Ich habe ein System mit 6 DC12V Motoren,> jeweils mit einer HAL Sensor.
Hast Du mal das Datenblatt zu diesen unbekannten Sensoren.
Sind die überhaupt digital und entprellt?
Stefan schrieb:> Input für 4 HX711
Wozu sollen diese 24Bit ADC dienen?
Zunächst ein mal vielen Dank für eure Antworten! Jeder einzelne von
euch.
Falk B. schrieb:> praktisch ALLE> Pins können den Pin Change Intzerrupt. Nur daß das eben von der> Aduino-IDE nicht untestützt wird. Muss man selber "per Hand" machen.
Soweit ich es weiß, kann ich immer Input Blockweise die Interrupts
einfügen. Also du meinst:
1) Aktiviere Interrupt für den gesamten PCINT Port
2) Anschließend frage mit if() ab, welche der Pins in dem Block sich von
LOW auf HIGH geändert hat, indem ich die alte Zustände in einer
Variable(eher Array) speichere und abfrage
3) Den neuen Zustand in die Array speichere? So kann ich die benötigte
12 Interrupts erreichen und müsste nicht die günstige Variante machen.
Falk B. schrieb:> Was sollen denn deine Interrupts überhaupt machen? Laß mich raten?> Drehgeber dekodieren? Wenn ja, macht man das so oder so anders.> Nämlich mit einem Timer-Interrupt und periodischer Abfrage. Siehe> Artikel Drehgeber.
Ich muss ehrlicherweise gestehen, dass ich den Code da nicht verstehe.
Das sieht mir nach sehr low level Programmierung aus. Sei es drum, bitte
korrigiere mich, was ich hier falsch sage:
0) Auf external interrupts durch Pin CHANGE/RISING etc. vollständig
verzichten.
1) Motordrehzahl ist 133 Umdrehungen * 127 = Einen Timer mit 16Khz
erstellen (Du hast 1KHz geschrieben, in dem Artikel steht aber Frequenz
* 127. Warum ist 8 ausreichend statt wie in Doc 127?)
2) In dieser Timer die Zustände aller HAL-Pins von allen Motoren
auslesen
3) Je nach LOW / HIGH Zustand der HAL-Pin-Paare den Zähler hoch oder
runter Zählen.
Stimmt das so?
Vielen Dank für deine Unterstützung, Falk. Bitte bedenke, dass ich in
Mikrokontroller-Programmierung dir sicherlich nicht gewachsen bin, das
aber kein Grund darstellt so einen harschen Ton zu haben :)
Falk B. schrieb:> Mach es einfach wie der Rest der Welt. Normal mit bewährten Konzepten!
Wenn man "Arduino encoder tutorial" googled, findet man eigentlich nur
Lösungen mit external interrupt pin Lösungen. :) Das mit dem timer und
abfragen ist eine völlig neue Lösung für mich. Danke für den
Gedankenstoß, werde heute Abend probieren, wenn du die obere drei Punkte
bestätigst / ggf. Gedankenfehler korrigierst.
Falk B. schrieb:> Der Mega hat mehr als genug PWM-Ausgänge, auch wenn man die "manuell"> konfigurieren muss.
Ebenfalls mit Timer? Ich glaube, das Thema Timer muss ich mir genauer
mal ansehen. Das scheint wirklich viele neue Türen zu öffnen, was man
als basic arduino Hobby-Entwickler nie verwendet / kennt.
Harald K. schrieb:> Das kann man problemlos mit Polling abwickeln, denn ein paar hundert> Hertz sind, wie hier schon andere schrieben, schnarchlahm.
Reden wir von polling innerhalb von loop() oder innerhalb eines Timers?
Danke für dein Beitrag!
Arduino F. schrieb:> Was aber negativ zu buche schlägt, sind die CallBack des> attachInterrupt(), denn die führen zu Ellen langen Push Pop Kaskaden.
Arbeitest du dann eher mit der manuellen config weg von
```PCICR |= (1 << PCIE0)``` ? Oder was wäre der gängige Weg, um
Interrupts anzubinden, ohne die Push Pop Kaskaden?
Sebastian W. schrieb:> Ich dachte, das sei für dich vielleicht interessant, ganz unabhängig von> der Diskussion über den besseren Ansatz mit einem Timer-Interrupt.
Ich bin dir wirklich wirklich(!) sehr dankbar, dass du nicht nur "das
ist falsch" schreibst, sondern auch den Anspruch hattest, dass ich was
lerne. Danke Sebastian. Ich habe es in meiner Rechnerchen irgendwann was
ähnliches gefunden. Da habe ich gelernt, man kann den ganzen Block
interrupten. Dass es eine maskierfunktion gibt, hat man nicht wirklich
erzählt. Das öffnet mir neue Wege. Könnte mir vorstellen, später die
sicherheitskritische Stopper / Lichtschrankensensoren damit zu
betreiben.
Sherlock 🕵🏽♂️ schrieb:> Ich würde die> Signale mit einem Oszilloskop überprüfen
Würde ich, wenn ich eine hätte. Ich sollte mir vielleicht langfristig
auch eine Anlegen. Ich schaue erstmal, ob das Problem mit Timers gelöst
wird.
Peter D. schrieb:> Wozu sollen diese 24Bit ADC dienen?
Nicht für die Motorsteuerung, falls die Frage dazu geht. Da hängt
jeweils einen 10kg Load Cell. Ich will das Objekt in der Mitte des
Portals aus diversen Gründen wiegen.
Nochmals danke an euch allen für euren Input!
Stefan schrieb:> Wenn man "Arduino encoder tutorial" googled, findet man eigentlich nur> Lösungen mit external interrupt pin Lösungen. :)
Weil sie alle nur voneinander kopieren, ohne wirklich zu verstehen, was
sie da tun.
Falk ist von einer anderen Generation. Er liest noch Datenblätter und
Fachbücher über grundsätzliche Vorgehensweisen, die es schon lange vor
Arduino gab und gut waren.
Die Menschen müssen dringend wieder dahin zurück kommen, sich über eine
blinkende LED freuen zu können, und das in allen erdenklichen Varianten
durchzuspielen, bevor sie sich an eine Gedanken-gesteuerte KI für ihren
Porsche heran wagen. Jeder (auch die größten Experten) muss ganz unten
Anfangen, um nicht als Luftpumpe zu enden.
Stefan schrieb:>> Pins können den Pin Change Intzerrupt. Nur daß das eben von der>> Aduino-IDE nicht untestützt wird. Muss man selber "per Hand" machen.>> Soweit ich es weiß, kann ich immer Input Blockweise die Interrupts> einfügen. Also du meinst:> 1) Aktiviere Interrupt für den gesamten PCINT Port> 2) Anschließend frage mit if() ab, welche der Pins in dem Block sich von> LOW auf HIGH geändert hat, indem ich die alte Zustände in einer> Variable(eher Array) speichere und abfrage> 3) Den neuen Zustand in die Array speichere?
Ja.
> So kann ich die benötigte> 12 Interrupts erreichen
Die brauchst du für diese Anwendung nicht.
>und müsste nicht die günstige Variante machen.
Hä?
> Falk B. schrieb:>> Was sollen denn deine Interrupts überhaupt machen? Laß mich raten?>> Drehgeber dekodieren? Wenn ja, macht man das so oder so anders.>> Nämlich mit einem Timer-Interrupt und periodischer Abfrage. Siehe>> Artikel Drehgeber.>> Ich muss ehrlicherweise gestehen, dass ich den Code da nicht verstehe.
Dann musst du halt noch was lernen. Und selbst wenn man die Details
nicht versteht, kann man ihn anwenden. Man muss nur periodisch das
Stückchen Software ausführen, für jeden einzelnen Drehgeber einmal. Das
gibt es auch als Beispiel für mehrere Kanäle.
Beitrag "Re: Mehrere Drehencoder gleichzeitig abfragen"> Das sieht mir nach sehr low level Programmierung aus.
Das ist normale Programmierung. Ist halt kein Clicki Bunti.
> korrigiere mich, was ich hier falsch sage:> 0) Auf external interrupts durch Pin CHANGE/RISING etc. vollständig> verzichten.
Auf jeden Fall!
> 1) Motordrehzahl ist 133 Umdrehungen * 127 = Einen Timer mit 16Khz> erstellen
Wieso das denn?
> (Du hast 1KHz geschrieben, in dem Artikel steht aber Frequenz> * 127. Warum ist 8 ausreichend statt wie in Doc 127?)
Weil du was falsch verstanden hast.
1) Der Timer muss mindestens mit der gleichen Frequenz aufgerufen
werden, wie Codewechsel vom Drehgeber kommen. Dein Motor macht max. 133
U/s bzw. ~8000U/min. Wieviel Codes liefert er pro Umdrehung? Sicher
nicht nur einen. Mindestens vier, eher deutlich mehr. Und auch dort
braucht man einen Sicherheitsfaktor von 2-4. Sprich, wenn dein Drehgeber
wirklich nur 1 Code / Umdrehung liefert, dann braucht man ~300-600 Hz
Abtastfrequenz. 1kHz ist eine runde Zahl und technisch kein Problem.
2.) Im Interrut wird der Drehgeber dekodiert und in der kleinen Variable
delta gespeichert. Die ist im Beispiel aber nur 8 Bit incl. Vorzeichen,
kann also nur -128 bis +127 enthalten. D.h. im Extremfall bei voller
Drehzahl gibt es nach 128 ISR-Aufrufen einen Überlauf. Den muss man
durch ausreichend häufiges Auslesen der Variable mittels der Funktion
encode_read() verhindern. Aber diese Mindestfrequenz ist um Faktor 128
KLEINER! Sprich, hier 1kHz/128 ~ 8Hz. D.h. dein Hauptprogramm muss
mindestens mit dieser Frequenz die Zähler auslesen und
weiterverarbeiten.
> 2) In dieser Timer die Zustände aller HAL-Pins von allen Motoren> auslesen> 3) Je nach LOW / HIGH Zustand der HAL-Pin-Paare den Zähler hoch oder> runter Zählen.
Ja.
> Vielen Dank für deine Unterstützung, Falk. Bitte bedenke, dass ich in> Mikrokontroller-Programmierung dir sicherlich nicht gewachsen bin, das> aber kein Grund darstellt so einen harschen Ton zu haben :)
Dann solltest du aber auch nicht so naseweis hier reinschneien und alle
Hinweise aus dem Netz oder den Artikel hier einfach beiseite schieben.
Du solltest eher direkt fragen, wie man es richtig macht.
>> Mach es einfach wie der Rest der Welt. Normal mit bewährten Konzepten!>> Wenn man "Arduino encoder tutorial" googled, findet man eigentlich nur> Lösungen mit external interrupt pin Lösungen. :)
Es gibt viel Müll im Internet.
> Das mit dem timer und> abfragen ist eine völlig neue Lösung für mich.
Aber nicht den Rest der welt. Dieses Thema wirde hier schon jahrelang
bis zum Wahnsinn diskutiert.
>> Der Mega hat mehr als genug PWM-Ausgänge, auch wenn man die "manuell">> konfigurieren muss.>> Ebenfalls mit Timer?
Ja sicher. Ohne Timer keine PWM.
> Reden wir von polling innerhalb von loop() oder innerhalb eines Timers?
Beides. Eher schnell im Timer (einige kHz), eher langsam in der loop()
Funktion (einige Dutzend Hz).
> Ich bin dir wirklich wirklich(!) sehr dankbar, dass du nicht nur "das> ist falsch" schreibst, sondern auch den Anspruch hattest, dass ich was> lerne. Danke Sebastian. Ich habe es in meiner Rechnerchen irgendwann was> ähnliches gefunden. Da habe ich gelernt, man kann den ganzen Block> interrupten.
"interrupten", soso. Früher wurde er unterbrochen. Dieses Denglisch ist
grausam!
http://kamelopedia.net/wiki/Denglisch> Dass es eine maskierfunktion gibt, hat man nicht wirklich> erzählt. Das öffnet mir neue Wege. Könnte mir vorstellen, später die> sicherheitskritische Stopper / Lichtschrankensensoren damit zu> betreiben.
Sicherheitskristische Funktionen, u.a. Endschalter, sollte man eher als
reine Hardware aufbauen, Software ist da deutlich unsicherer.
>> Wozu sollen diese 24Bit ADC dienen?>> Nicht für die Motorsteuerung, falls die Frage dazu geht. Da hängt> jeweils einen 10kg Load Cell. Ich will das Objekt in der Mitte des> Portals aus diversen Gründen wiegen.
Dann sollte man das aber nicht nebenläufig so in einem Beitrag nennen,
denn das verwirrt. Klare Sprache verwenden, kein Denglisch! Siehe
Netiquette.
> Nochmals danke an euch allen für euren Input!
Früher waren das Hinweise. Oder bist du eine Maschine? Oder ein Chat
GPT-Bot?
digitalRead() ist schon eher gemächlich.
Ich habe ein ähnliches Problem (ps2 Mausdaten lesen mittels Interrupt
statt Polling) damals durch Benutzung von
https://github.com/mikaelpatel/Arduino-GPIO gelöst.
Zuvor war oft die Zeit vom Interrupt zum digitalRead()-Ergebnis
anscheinend zu lang, die Empfangsroutine unzuverlässig und regelmässige
resyncs waren normal.
Seit dem Umstieg auf Arduino-GPIO läuft das zuverlässig.
Stefan S. schrieb:> digitalRead() ist schon eher gemächlich.> Seit dem Umstieg auf Arduino-GPIO läuft das zuverlässig.
Wieso zum Teufel "braucht" man eine alternative Bibliothek, um Port-
Register zu lesen?
Mir ist klar, was digitalRead() noch alles so macht, um für dummies den
schnellen Erfolg zu bieten. Aber wenn man schon so weit ist, dass einem
digitalRead() nicht mehr gut genug ist, sollte man sich besser mit dem
Datenblatt befassen, anstatt mit anderen Bibliotheken.
Stefan schrieb:> Ich muss ehrlicherweise gestehen, dass ich den Code da nicht verstehe.> Das sieht mir nach sehr low level Programmierung aus.
Arduino gibt dir mit seiner arg knappen Dokumentation nur das Gefühl,
mehr zu verstehen. Aber in Wirklichkeit verstehst du dein eigenes damit
geschriebenes Werk weniger, als Falks Beispiel. Schau dir mal den
Quellcode von digitalRead() an, dann siehst du was ich meine. Und das
ist erst der Anfang. Das ganze Framework ist alles andere als einfach,
es sieht nur auf den ersten Blick so aus. Und genau deswegen wird es so
oft missverstanden.
Einfach ist das Datenblatt. Damit sollte man anfangen, wenn man
ernsthaft lernen will.
Sherlock 🕵🏽♂️ schrieb:> Wieso zum Teufel "braucht" man eine alternative Bibliothek, um Port-> Register zu lesen?
Portabilität!
Einer der Vorteile der Arduino Welt.
Nicht an jeder Ecke möglich, aber die genannte Lib macht das schon gar
nicht so schlecht.
Was du tust:
Mal wieder aus den Büschen gesprungen kommen, und Arduino Bashing
betreiben.
Sherlock 🕵🏽♂️ schrieb:> Wieso zum Teufel "braucht" man eine alternative Bibliothek, um Port-> Register zu lesen?
"brauchen" tut man das natürlich nicht, aber ich mache das deswegen mit
dem Arduino-Framework, damit ich eben mal schnell einen anderen
microcontroller nehmen kann ohne groß an meinem Code zu schrauben. Daß
das hier für die Hardcore-Assembler-only-Taliban natürlich eine
Gotteslästerliche Sünde ist, dessen bin ich mir bewußt.
Und für diese Portabilität ist die GPIO lib super und auch dafür mag ich
das Arduino Framework, selbst wenn mir nicht alles daran gefällt.
Für mich ist das mit der GPIO Bibliothek "gut genug". Wenn ich Fälle
habe wo das nicht reicht, dann bin ich in der Lage das zu erkennen und
dann habe ich ein paar Bekannte von denen ich weiß daß sie sich mit den
Innereien der ganzen Prozessoren wesentlich besser auskennen als ich und
gebe den "Auftrag" (meistens ist das irgendwas aus dem eh gemeinsamen
Freundeskreis) halt ab.
Ich bin zu alt um noch das Bedürfnis zu verspüren, allem selbst ganz auf
den Grund zu gehen. Mir reicht es im zweifelsfall jemand zu kennen der
das kann.g
Joachim B. schrieb:> dafür wurde digital fast read erfunden> https://www.instructables.com/Fast-digitalRead-digitalWrite-for-Arduino/
Der war flach, der Witz.
Na gut, da fehlt mir jetzt tatsächlich das tiefere Verständnis wie das
ganz genau intern als Maschinencode aus dem Compiler rauskommt, aber
nachdem ich kurz in die Quellen von Arduino-GPIO und dann das DIO2
geschaut hab, kann ich jetzt nicht feststellen, wo das offensichtlich
soviel besser sein soll.
Am Ende ist es nicht anderes als "digitalRead() ist eher gemächlich,
deswegen nimmt man was anderes".
Ich hab mich an das interface von Arduino-GPIO gewöhnt und mir ist es
schnell genug, ob DIO2 noch ein paar nanosekunden rauskitzelt kann ich
nicht beurteilen.
Wenn man das aus dem Arduino library manager (oder von github) holt,
dann kann es auch ein paar boards mehr aber immer noch weniger als
Arduino-GPIO (was in meinem Fall allerdings nicht so wichtig wäre)
Das mit dem "selbst machen ohne Framework" war ja in dem Flachwitz
beschrieben ;-)
Stefan S. (seife)
10.03.2025 08:04
>Na gut, da fehlt mir jetzt tatsächlich das tiefere Verständnis wie das>ganz genau intern als Maschinencode aus dem Compiler rauskommt, aber>nachdem ich kurz in die Quellen von Arduino-GPIO und dann das DIO2>geschaut hab, kann ich jetzt nicht feststellen, wo das offensichtlich>soviel besser sein soll.
Du könntest es praktisch testen: Einfach die verschiedenen Versionen von
digitalRead in eine Schleifen mit 10.000 Mal lesen stecken und mittels
start=micros() die Zeit messen.
Ich habe jetzt das Datenblatt zu Deinen Drehgebern leider nicht
gefunden, aber anhand des Templates rate ich jetzt mal, dass jeder einen
Geschwindigkeits- und einen Richtungsausgang hat. Wenn die, wie üblich,
Open-Drain sind, gibt es auch die Möglichkeit zu schnell statt zu
langsam zu lesen.
Speziell wenn man die Pull-Up-Widerstände arg hochohmig macht. Dann kann
der Drehgeber seinen Ausgang relativ niederohmig und damit zügig nach
low ziehen. Lässt der Drehgeber das Signal dann wieder los, wird es vom
Pull-Up wesentlich langsamer, weil hochohmiger, auf high gezogen.
Kommt also jetzt nach einem Wechsel der Drehrichtung eine Flanke von
high nach low aus dem Geschwindigkeitsausgang, während der
Richtungsausgang jetzt high sein müsste, kann es sein, dass der Puls in
die falsche Richtung gezählt wird, weil das Richtungssignal noch dabei
ist gen high zu kriechen.
Dagegen spricht nur, dass Du in 80% der Fälle Mist misst. Das wäre bei
nur diesem Fehler wesentlich weniger.
Stefan S. schrieb:> (..) Daß das hier für die Hardcore-Assembler-only-Taliban natürlich eine> Gotteslästerliche Sünde ist, dessen bin ich mir bewußt.> Und für diese Portabilität ist die GPIO lib super und auch dafür mag ich> das Arduino Framework, selbst wenn mir nicht alles daran gefällt.
Das hat primär nichts mit Assembler zu tun, es sind einfach nur
Grundlagen in C-Sprache und Digitaltechnik, bei denen man sich weigert,
sie einfach einmal zu erlernen. Hinzu kommt noch oft die beliebte
Aversion, Trägheit oder geradezu phobieartige Panik, Datenblätter zu
öffnen und an der entsprechenden Stelle zu lesen, um die Mechanik der
I/O-Ports des gewählten µControllers zu begreifen; viele Arduinojünger
wissen gar nicht, welcher µC auf dem gekauften Board sitzt und wo er
überhaupt sitzt, insofern erübrigt sich dann auch die Frage nach
irgendeiner entsprechenden Stelle im Datenblatt. In der Regel ist es so,
dass, wenn man es einmal richtig begriffen hat, man es für immer
begriffen hat und dieses Wissen bzw. Vorgehensweise dann immer auf
andere µC übertragen kann. Mit Grundlagen in C ist in diesem Fall das
Anden, Oren und Xoren gemeint – wer sich all dem aber bewusst entziehen
will oder alles verweigert, der sollte vielleicht lieber Tischtennis
spielen oder darf sich dann halt weiter mit Arduino-Pseudo-Befehlen, die
übrigens ganze Bildschirme mit Asseblerbefehlen füllen (können),
herumärgern und wundern, das zeitkritische Codeteile nicht wie gewünscht
funktionieren werden – richtig in C geschrieben generiert der Compiler
an so einer Stelle in der Regel nur eine Zeile Assemblercode. So eine
Verandung oder Verorung schreibt man (in C) dann nur einmal als
#define-Zeile und verwendet im Code immer wieder nur die
selbstausgedachten, lesbaren Synonyme wie LED1_On oder Buzzer_AUS, was
natürlich viele auch nicht tun, weil sie diese Art des Codeschreibens
gar nicht kennen oder kennengelernt haben, da viele Beispiele im Netz
ziemlich dumm geschrieben sind und jeder von jedem den generierten
Blödsinn kopiert und weiterverbreitet. Es gibt natürlich noch andere
Arten und Methoden, die Portausgänge eines µControllers atomar zu
beeinflussen – das beste Beispiel dafür sind die STM32 oder die neuen
AVR-Familien, wo man die Innereien grundlegend für diese Zwecke komplett
überarbeitet und dafür ausgelegt hat. Wer aber weiter Arduino spielen
will, der darf das selbstverständlich weiter tun und man sollte ihn am
besten auch gar nicht daran hindern. Wem das richtige Ansprechen der
Ports egal ist, dem sind in der Regel auch andere Dinge (im Code, Design
und Aufbau) so ziemlich egal bzw. in seinem Ermessen nicht so relevant,
was hier vermutlich als Kombination dieser Dinge schön zusammengelaufen
ist – diese Analogie trifft leider immer wieder zu. Wie auch immer – ich
wünsche jedem arduinoverliebten viel Spaß mit digitalWrite, digitalRead
& Co, denn den wird er früher oder später bestimmt in irgendeiner Form
schon bekommen.
Das ist wohl auch eine Frage auf welchem Abstraktions-Level man sich
wohl fühlt. Ich fahre auch Auto ohne mich mit Verbrennertechnik
auszukennen. Bei uC komme ich von der Hardwareseite und programmiere
meine AVRs folgerichtig in C mit dem Datenblatt daneben. Und, wie zuvor
beschrieben kann ich dann meine Variablen und Co-Prozeduren genauso
benennen wie es mir am besten gefällt. Das ist ein nicht unwesentlicher
Anteil an der Programmierarbeit. Das ist natürlich nicht jedermanns
Sache, abstraktere Hochsprachen mit den entsprechenden Bibliotheken
haben unbestreitbar ihren Platz.
Gregor J. schrieb:> das richtige Ansprechen der Ports
Und du bist der Dominator, welcher bestimmen darf, was "richtig" ist.
Natürlich darfst du Arduino (und alle seine User) für blöd halten.
Aber lass dir gesagt sein, dass es in weiten Teilen C++ ist.
Nur die Basis Funktionen des Frameworks sind in C, z.B. digtalWrite()
und seine Kumpels.
C alleine ist da also nicht der Heilsbringer.
Ok ich habe jetzt die letzten Tage damit verbracht, das neue Wissen hier
aus dem Forum umzusetzen. Jetzt funktioniert es, aber ohne Timer ISR,
sondern weiterhin mit external Interrupts. Schreibe gleich mehr zu
meiner Reise.
Ich habe dank euch gelernt, dass Arduino Framework langsam ist und dass
es Möglichkeiten gibt, das Framework umzugehen. Dafür bin ich dankbar.
Es öffnete sich für mich eine ganz neue Dimension der Arduino
Entwicklung!
Es gibt aber ein Grund, weshalb Frameworks existieren. Bessere
Code-Lesbarkeit, einfachere Code-Wartung, deutlich einfachere Einstieg
für größere Teams. Es gibt ein Grund, weshalb wir nicht mehr in COBOL
oder Assembler programmieren. Derzeit dominieren eher
Programmiersprachen wie Javascript, Java oder C#.
Kann mir keiner Erzählen, dass dieser Code hier
1
TCCR0 = (1<<WGM01) | (1<<CS01) | (1<<CS00);
2
OCR0 = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5);
3
TIMSK |= 1<<OCIE0;
leserlicher und verständlicher wäre als
1
initTimer(int prescaler);
Selbst die Variablennamen verstoßen den Clean Code. Verstehe aber, dass
es eine technische Einschränkung ist, mit dem man leben muss.
Sei es drum, man spielt die Hand, die geteilt wurde. Ich habe zunächst
ein ISR Erstellt und mit 3KHz. Aber den habe ich einfach nicht zum
Laufen bekommen. Vielleicht können wir gerne auch da ansetzen:
1
void setupTimer() {
2
noInterrupts(); // Disable global interrupts while setting up the timer
Wobei ich schon kopieren des Codes merke, dass mein Zähler nicht 3 Khz
läuft, sondern nur 300 Hz (16000/3/8 = 666, nicht 6666). Trotzdem hat
ein timerCalled++ hat bei einer serial.println() außerhalb der timer
und innerhalb des loop() 1 ausgegeben.
Deswegen habe ich auf die Idee von Falk eingesprungen: CHANGE Trigger
über Port und Masking:
Auch hier blieb called bei 0. Wenn ich aber innerhalb der loop
digitalRead(22) oder sogar (PINA & (1 << PA0)) ausgeben lasse, sehe ich
aber den Sprung von 0 auf 1.
Ich habe es jetzt gelöst, in dem ich keine digitalReads mehr verwende,
sondern direkt auf die Register zugreife (PINA & (1 << PA0)) //
digitalRead(22)
Damit kann ich später bei refactorings etc. weiterhin arduino code
suchen und profitiere von Leistung der direkt auslesen. Auch das
aufteilen von template function hat unerwartet performance gebracht...
1
void handleEncoderMotor0()
2
{
3
if ((PINA & (1 << PA0)) == LOW) // digitalRead(22)
4
{
5
hallCounters[0]--;
6
}
7
else
8
{
9
hallCounters[0]++;
10
}
11
}
Sherlock 🕵🏽♂️ schrieb:> Die Menschen müssen dringend wieder dahin zurück kommen, sich über eine> blinkende LED freuen zu können
Was ist mit Fortschritt? Man sollte eher doch auf die Arbeit der letzten
Generation aufbauend weiterentwickeln, nicht?
Falk B. schrieb:> Ja sicher. Ohne Timer keine PWM.
Das habe ich mir ebenfalls angeschaut. Interessanter Ansatz. Hätte ich
den Timer zum Laufen bekommen, wäre das eine feine Sache. Zwar brauche
ich nicht mehr PWM Pins, ist aber ein top Wissen. Danke dafür Falk!
Falk B. schrieb:> Sicherheitskristische Funktionen, u.a. Endschalter, sollte man eher als> reine Hardware aufbauen
Magst du mir erklären bzw in die Richtung richtiger Literatur
weiterverweisen bitte? Dankeschön :)
Sherlock 🕵🏽♂️ schrieb:> Einfach ist das Datenblatt. Damit sollte man anfangen, wenn man> ernsthaft lernen will.
Das ist der Unterschied zwischen ernsthaft lernen und lernen, wie man es
anwendet. Man muss kein Verständnis von Verbrennermotoren haben, Stutz,
Spur zu verstehen oder Einflüsse verschiedener Öle und Ölfüllmengen
kennen, um ein Auto zu fahren. Es hilft! Aber es ist nicht notwendig.
Flunder schrieb:> Ich habe jetzt das Datenblatt zu Deinen Drehgebern leider nicht> gefunden, aber anhand des Templates rate ich jetzt mal, dass jeder einen> Geschwindigkeits- und einen Richtungsausgang hat
Hallo Flunder,
wie im originalpost beschrieben, sind das nur zwei HALL sensoren, die
einen Trigger auslösen, wenn der Magnet in deren Richtung gedreht ist.
Also gibt es zwei "Buttons" (C1 & C2), die entweder gleichzeitig HIGH
sind oder nur eine HIGH andere LOW. Wenn beide HIGH, dreht es sich in
Uhrzeiger, wenn der C1 HIGH und C2 LOW, dann dreht es sich gegen die
Uhrzeiteger.
Ich danke euch vielmals für euren Input und investierte Zeit. Ich habe
sehr viel über Arduino Entwicklung und neue Alternativen von euch
gelernt und ist sicherlich nicht das letzte mal, dass ich bei einem
Problem mich melde! Schönen Tag noch!
Stefan schrieb:> Auch hier blieb called bei 0.
Man kann aus den Codeschnipseln leider nicht erkennen, ob die zur
Kommunikation zwischer ISR und Hauptprogramm genutzten Variablen als
volatile markiert sind, und ob die Zugriffe auf diese Variablen im
Hauptprogramm entsprechend abgesichert sind.
LG, Sebastian
Stefan schrieb:> Auch hier blieb called bei 0.Sebastian W. schrieb:> als volatile markiert sind,
Die ATOMIC Makros sind bestens geeignet.
Sie sperren die Interrupts und bauen die nötige Memory Barrier
Volatile ist nicht nötig oder angebracht, da der Wert ja nicht von der
Hardware geändert wird, sondern in der ISR
Stefan schrieb:> Das ist der Unterschied zwischen ernsthaft lernen und lernen, wie man es> anwendet. Man muss kein Verständnis von Verbrennermotoren haben, Stutz,> Spur zu verstehen oder Einflüsse verschiedener Öle und Ölfüllmengen> kennen, um ein Auto zu fahren. Es hilft! Aber es ist nicht notwendig.
Wenn du an den Registern und an der Hardware rum fummelst, brauchst du
das Wissen aus dem Datenblatt.
Ohne Wenn und Aber!
Sonst bleibt es ein dümmliches stochern im Nebel.
Falk B. schrieb:> Oder bist du eine Maschine? Oder ein Chat> GPT-Bot?
Funfact: als jemand, der in der AI-Branche relativ viel arbeitet. Der
neue Turing-Test für AI-Chatbots ist witzigerweise political
correctness. Ich würde aber ungern mein Account verlieren ...
Stefan schrieb:> Ok ich habe jetzt die letzten Tage damit verbracht, das neue Wissen hier> aus dem Forum umzusetzen. Jetzt funktioniert es, aber ohne Timer ISR,> sondern weiterhin mit external Interrupts. Schreibe gleich mehr zu> meiner Reise.
Schlecht. Der Timer ist das Mittel der Wahl. Er hat auch noch andere
Vorteile. U.a. bietet er eine genaue Zeitbasis für deine Software, die
einfacher und leistungsfähiger ist als das millis() vom
Arduino-Framework.
> Kann mir keiner Erzählen, dass dieser Code hierTCCR0 = (1<<WGM01) |> (1<<CS01) | (1<<CS00);> OCR0 = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5);> TIMSK |= 1<<OCIE0;> leserlicher und verständlicher wäre alsinitTimer(int prescaler);
Das hat keiner behauptet.
> Sei es drum, man spielt die Hand, die geteilt wurde. Ich habe zunächst> ein ISR Erstellt und mit 3KHz. Aber den habe ich einfach nicht zum> Laufen bekommen. Vielleicht können wir gerne auch da ansetzen:
Und wo sehe ich deine ISR?
> void setupTimer() {> noInterrupts(); // Disable global interrupts while setting up the> timer> // Timer1 Configuration> TCCR1A = 0; // Reset Timer1 Control Register A> TCCR1B = 0; // Reset Timer1 Control Register B> TCNT1 = 0; // Reset Timer1 Counter Register
Unnötig. Einmal komplett setzen reicht.
> const uint16_t COMPARE_MATCH_VALUE = 6665; // 16.000 KHz 3 KHz 8> (Prescaler) -1> OCR1A = COMPARE_MATCH_VALUE;> // Set Timer1 to CTC mode (Clear Timer on Compare Match)> const uint8_t CTC_MODE = (1 << WGM12);> TCCR1B |= CTC_MODE;> // Set prescaler to 8 (16 MHz / 8 = 2 MHz timer clock)> const uint8_t PRESCALER_8 = (1 << CS11);> TCCR1B |= PRESCALER_8;> // Enable Timer1 Compare Match A Interrupt> const uint8_t ENABLE_TIMER_INTERRUPT = (1 << OCIE1A);> TIMSK1 |= ENABLE_TIMER_INTERRUPT;> interrupts(); // Enable global interrupts> }
Dass ist akademischer Firlefanz.
> ein timerCalled++ hat bei einer serial.println() außerhalb der timer> und innerhalb des loop() 1 ausgegeben.
Ja, super. Und wo sehen WIR deinen VOLLSTÄNDIGEN Quelltext? Siehe
Netiquette!
>> Sicherheitskristische Funktionen, u.a. Endschalter, sollte man eher als>> reine Hardware aufbauen>> Magst du mir erklären bzw in die Richtung richtiger Literatur> weiterverweisen bitte? Dankeschön :)
Puhhh, gute Frage. Kann ich dir leider so einfach nix nennen. Und in
Normen für Maschinensicherheit will man auch nicht lesen, das ist eine
Sprache und Welt für sich.
> wie im originalpost beschrieben, sind das nur zwei HALL sensoren, die> einen Trigger auslösen, wenn der Magnet in deren Richtung gedreht ist.
Das klingt nach den Lagesensoren eines BLDC Motors.
Falk B. schrieb:> Das klingt nach den Lagesensoren eines BLDC Motors.
Genauso ist es!
Falk B. schrieb:> Puhhh, gute Frage. Kann ich dir leider so einfach nix nennen. Und in> Normen für Maschinensicherheit will man auch nicht lesen, das ist eine> Sprache und Welt für sich.
Ja. Ich dachte mir vielleicht Interrupts. Als Endschalter gibt es bei 3D
Drucker eher als Positionsmelder, wohingegen CNC tatsächlich als Not-Aus
funktioniert (zumindest mein billig 3018).
Falk B. schrieb:> VOLLSTÄNDIGEN Quelltext
Nicht auf git committed, weil nicht funktioniert. Somit ist es für immer
weg. Ich versuche heute Abend nach der Arbeit mal ein Unabhängiges ISR
erneut aufzusetzen mit volatile variable. Wenn ich die
nicht-Funktionalität reproduzieren kann, werde ich es hier posten.
Falk B. schrieb:> einfacher und leistungsfähiger ist als das millis() vom> Arduino-Framework
Glaube ich sofort! Ich probiere heute Abend nach der Arbeit nochmal und
poste meine Resultate. Danke Euch allen
Stefan schrieb:> Das ist der Unterschied zwischen ernsthaft lernen und lernen, wie man es> anwendet. Man muss kein Verständnis von Verbrennermotoren haben, Stutz,> Spur zu verstehen oder Einflüsse verschiedener Öle und Ölfüllmengen> kennen, um ein Auto zu fahren. Es hilft! Aber es ist nicht notwendig.
Die Register sind das Lenkrad.
Stefan schrieb:>> Puhhh, gute Frage. Kann ich dir leider so einfach nix nennen. Und in>> Normen für Maschinensicherheit will man auch nicht lesen, das ist eine>> Sprache und Welt für sich.>> Ja. Ich dachte mir vielleicht Interrupts.
Die allermeiste Software wird als unsicher angesehen. Sicherheits gibt
es meistens nur durch echte, einfache, robuste Hardware.
Sicherheitszertifizierte Software ist sehr aufwändig und teuer.
> Nicht auf git committed, weil nicht funktioniert. Somit ist es für immer> weg. Ich versuche heute Abend nach der Arbeit mal ein Unabhängiges ISR> erneut aufzusetzen mit volatile variable. Wenn ich die> nicht-Funktionalität reproduzieren kann, werde ich es hier posten.
Siehe Anhang.
Stefan schrieb:> Kann mir keiner Erzählen, dass dieser Code hier
1
TCCR0 = (1<<WGM01) | (1<<CS01) | (1<<CS00);
2
OCR0 = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5);
3
TIMSK |= 1<<OCIE0;
> leserlicher und verständlicher wäre als
1
initTimer(int prescaler);
Du vergleichst dabei Dinge, die sich nicht vergleichen lassen. Die
oberen Zeilen konfigurieren mehrere Funktionen des Mikrocontrollers. Die
untere ist die definition einer Funktion, die so alleine erst mal gar
nichts macht.
Wenn du vergleichst, musst du den Inhalt der Funktion mit zeigen oder
wenigstens detailliert dokumentieren. Und dann ergibt sich, dass die
Funktion wahrscheinlich sogar komplexer ist, als die drei einzelnen
Zeilen. Mit einem einzigen Aufrufparameter (int prescaler) sind ja
nicht mal alle Parameter abgedeckt, die du mit den drei Zeilen
einstellst.
Durch Weglassen von Funktionalität sieht die Funktion einfach aus. Wenn
man dann auch noch möglichst viele Details in der Dokumentation weg
lässt, bist du beim Design-Prinzip von Arduino.
Schau dir als Beispiel mal die Doku von pinMode() an.
https://docs.arduino.cc/language-reference/de/en/functions/digital-io/pinMode/
Die Modi beziehen sich offenbar auf eine bestimmte µC Serie. Es ist aber
nicht angegeben, welche Pins welche Modi unterstützen. Wer dann meint,
er braucht kein Datenblatt/Referenzhandbuch zu lesen, der bleibt schnell
stecken.
Beim ESP8266 haben z.B. alle Pins den zusätzlichen Modus INPUT_PULLDOWN.
Nur einer nicht, der kann nur INPUT_PULLDOWN_16. Versucht man ihn mit
INPUT_PULLDOWN oder INPUT_PULLUP zu konfigurieren, aktiviert man
irgendeine völlig andere Funktion. Der Pin ist dann jedenfalls kein
Input mehr. Von solchen Fallstricken hat so ziemlich jeder
Mikrocontroller etwas zu bieten, der moderner als die alten 8 Bit AVR
ist.
Wen etwas erfahrener ist, mag einwenden, dass der ESP8266 kein
offizielles Arduino Target ist und man daher in dessen eigener Doku
lesen muss. Bitteschön:
https://arduino-esp8266.readthedocs.io/en/latest/reference.html#digital-io
Dort ist es besser dokumentiert (war nicht immer so). Aber glaube mir,
auch diese Doku lässt viele Details aus. Z.B schlagen hier immer wieder
Leute auf, die nicht dahinter kommen, warum ihr ESP nicht booten will.
Dabei sind alle Voraussetzungen bekannt. Man darf sich nur nicht
einbilden, dass Arduino vollständig dokumentiert sei.
Gut, dass man bei Arduino nicht gezwungen ist, alles mit den
Bibliotheken zu machen. Man kann immer low-Level programmieren, wenn man
will oder muss.
Stefan schrieb:> Was ist mit Fortschritt? Man sollte eher doch auf die Arbeit der letzten> Generation aufbauend weiterentwickeln, nicht?
Beides wird gebraucht. Der eine braucht mehr Grundlagen, der andere mehr
High-Level.
> Man muss kein Verständnis von Verbrennermotoren haben, Stutz,> Spur zu verstehen oder Einflüsse verschiedener Öle und Ölfüllmengen> kennen, um ein Auto zu fahren.
Um Computer zu benutzen, muss man sie nicht programmieren können. Ohne
Grundlagen kann man sie aber nicht gut programmieren. Ich sehe doch
ständig auf der Arbeit, wie die Framework Gurus oft keinen blassen
Schimmer haben, wie viel Last ihre teilweise dummen Konstrukte auf dem
Server produzieren. Die merken das nicht mal, weil sie immer nur alleine
manuell testen. Wenn da 1000 User gleichzeitig aktiv sind, kommt es auf
ein Megabyte mehr oder weniger durchaus an.
Falk B. schrieb:> Dass ist akademischer Firlefanz.
Schön gesagt. Aber heutzutage muss man ja schon für jeden Kommentar und
sprechende Namen dankbar sein.
Falk B. schrieb:> Siehe Anhang.
Ok funktioniert. Vielen Dank! Ich erstelle jetzt ordentliche Module für
die Timer - das Projekt wird etwas größer und Ordnung könnte ich gut
gebrauchen) Ich baue es jetzt um, wo ich mit diesem Timer die Resultate
der PINs 2,3,22,24 etc. auslese und anschließend die Bewegungsmuster der
HALLs abbilde statt externe Unterbrecher (attachInterrupt)!
Vielen Dank. Ich dokumentiere anschließend meine Resultate hier!
Sherlock 🕵🏽♂️ schrieb:> wahrscheinlich sogar komplexer ist, als die drei einzelnen Zeilen.
Es ist mit Sicherheit komplexer. Muss mich aber nicht darum kümmern. Das
ist ja die Idee eines Frameworks. Ein Trade-Off eben. Ich sehe es aber
jetzt nach diesem Thread ein, dass dieser Weg für die
Enduser-Softwareentwicklung zwar in Zeitalter der starken Hardware in
Ordnung ist, in der µC-Embedded-Software allerdings einfach nicht
tragbar ist.
Sherlock 🕵🏽♂️ schrieb:> wenn man will oder muss.
Der Einstieg in die Arduino Welt ist nun mal einfacher als
Registerschieberei.
Stefan schrieb:> Der Einstieg in die Arduino Welt ist nun mal einfacher als> Registerschieberei.
Ich habe noch nie verstanden warum "einfach" ein Kriterium ist. Will ich
was einfaches staple ich Bauklötze. Warum will jeder immer "einfach"?
Stefan schrieb:> Es ist mit Sicherheit komplexer. Muss mich aber nicht darum kümmern. Das> ist ja die Idee eines Frameworks. Ein Trade-Off eben.
Korrekt.
> Ich sehe es aber> jetzt nach diesem Thread ein, dass dieser Weg für die> Enduser-Softwareentwicklung zwar in Zeitalter der starken Hardware in> Ordnung ist, in der µC-Embedded-Software allerdings einfach nicht> tragbar ist.
Stimmt so nicht. Denn erstens liegt dein Ursprungsproblem nicht in der
Nutzung von digitalRead(). Das ist zwar deutlich langsamer als der
direkte Portzugriff, ist aber mit ca. 2-3us immer noch schnell genug für
die meisten Anwendungen. Und zweitens geht auch bei Embedded System der
Trend schon lange zum Hardware Overkill. Man wirft massiv
leistungsstarke Hardware auf relativ kleine Probleme, um mit Frameworks
schnell ein Ergebnis zu erreichen. Das ist TEILWEISE OK, aber oft auch
nicht.
> Der Einstieg in die Arduino Welt ist nun mal einfacher als> Registerschieberei.
Das ist der Sinn der Sache! Man muss sich nur deren Grenzen bewußt sein!
Cyblord -. schrieb:>> Der Einstieg in die Arduino Welt ist nun mal einfacher als>> Registerschieberei.>> Ich habe noch nie verstanden warum "einfach" ein Kriterium ist. Will ich> was einfaches staple ich Bauklötze. Warum will jeder immer "einfach"?
Weil das nun mal ein natürliches Empfinden ist. Kaum einer will was
EXTREM schweres am Anfang machen, denn damit schreckt man die
allerweisten Leute ab. Klar, wenn man es idiotensicher macht, zieht man
viele Idioten an.
Das Stichwort lautet Lernkurve.
https://de.wikipedia.org/wiki/Lernkurve
Außerdem ist der innere Antrieb, schwierige, herausfordernde Dinge zu
tun, heute in der Masse deutlich geringer. Generation Infotainment.
Konsumieren ist einfacher und "angenehmer".
Also ich habe jetzt es auf Timer dank Falks Hilfe umgestellt. Das
einzige Limit ist die sehr niedrig eingeschätzte Abtastrate. Alles unter
8kHz ist zu langsam für die beiden Motoren, die jeweils mit 133Hz drehen
und pro Umdrehung 110 Triggers verursachen.Nachdem ich den Denkfehler,
dass ich pro Sekunde ca 14.000 triggers habe mit einer 64 prescaler
16kHz timer gelöst habe, funktioniert es einwandfrei.
Für die zukünftigen, die auf diesem thread stoßen, sieht die ganze
Lösung wie folgt aus:
Falk B. schrieb:> Siehe Anhang.
timer.cpp beinhaltet die init_timer Funktion von Falk. Zusätzlich
beinhalteter die ISR:
Dieser ISR wird später noch von den Entitäten
hochRunterController.handlerHochRunterTimerInterrupt(); so wie für links
rechts aufrufen. Daher eine ISR ruft mehrere Funktionen auf. Ich habe
mich gegen eine inline funktion entschieden hier, weil ich
Leisungseinbußen befürchte.
Die TimerInterrupt sieht dann wie folgt aus:
die get_____Hall() Funktionen sind einfache inline Funktionen:
1
inline bool getVorZuruck1PrimaryHall() { // 2
2
return PINE & (1 << PE4);
3
}
Später kann man anhand von der hallCounters eben PID durchziehen und die
DC Motoren präzise steuern.
Bei Fragen stehe ich gerne zur Verfügung; bleibe auf diesem Thread
weiterhin in Beobachtung.
Danke an alle, die geholfen haben
Bei solch langen Variablen- und Funktionsnamen geht mir
das Herz auf ....
Ich würde sagen: wenn du die Namen stichhaltig auf die
Hälfte oder ein Drittel kürzt verstehst du dein Programm
sicherlich besser.
Stefan schrieb:> Dieser ISR wird später noch von den Entitäten> hochRunterController.handlerHochRunterTimerInterrupt(); so wie für links> rechts aufrufen.
Hä? Die ISR wird von den "Entitäten" (warum nur dieses Wort?) aufgerufen
oder die ISR soll die Funktionen (oder meinetwegen "Methoden")
aufrufen?
> Daher eine ISR ruft mehrere Funktionen auf.
Also doch Funktionen. Lass die "Entitäten" weg, dann wird vielleicht
auch Deine Formulierung verständlicher.
> Ich habe mich gegen eine inline funktion entschieden hier,> weil ich Leisungseinbußen befürchte.
Gerade das hier ist ein Beispiel, wo eine Inlinefunktion
Leistungeinbußen vermeiden hilft, weil nämlich der Overhead, den der
Compiler beim Funktionsaufruf erzeugt, dadurch entfallen kann.
Wastl schrieb:> Ich würde sagen: wenn du die Namen stichhaltig auf die> Hälfte oder ein Drittel kürzt verstehst du dein Programm> sicherlich besser
Was ist deine Empfehlung?
1
cphs[MA] // array holding current hall states of primary pin
statt
1
currentPrimaryHallStates[VOR_ZURUCK_MOTOR_AMOUNT]
Diese Diskussion führe ich wenn dann mit meiner junior Entwicklern. Ihr
seid besser als das. Trotzdem empfehle ich dir die Lektüre Clean Code
von mipt Verlag.
Harald K. schrieb:> weil nämlich der Overhead, den der> Compiler beim Funktionsaufruf erzeugt, dadurch entfallen kann.
Würde aber die Hinteraneinanderreihung der vier Funktionen nicht dazu
führen, dass es ein großer Block Code ohne Stack ausgeführt wird? Ernst
gemeinte Frage. Die Speicheroptimierung durch inline Funktionen habe ich
neu entdeckt und mir fehlt die praktische Erfahrung.
Stefan schrieb:> Also ich habe jetzt es auf Timer dank Falks Hilfe umgestellt. Das> einzige Limit ist die sehr niedrig eingeschätzte Abtastrate. Alles unter> 8kHz ist zu langsam für die beiden Motoren, die jeweils mit 133Hz drehen> und pro Umdrehung 110 Triggers verursachen.
Das war ja deine (Falsch)aussage, daß da nur ein Code/Umdrehung
reinkommt. Hatte mich sehr gewundert. Normale BLDC Motoren mit 3 Phasen
haben 3 Codes/U. Wenn das ein einfacher Drehgeber ist, hat der
natürlich mehr Auflösung.
> Nachdem ich den Denkfehler,> dass ich pro Sekunde ca 14.000 triggers habe mit einer 64 prescaler> 16kHz timer gelöst habe, funktioniert es einwandfrei.
Du hast so odert so keine "Triggers", sondern bestenfalls Codes.
> Für die zukünftigen, die auf diesem thread stoßen, sieht die ganze> Lösung wie folgt aus:>> Falk B. schrieb:>> Siehe Anhang.>> timer.cpp beinhaltet die init_timer Funktion von Falk. Zusätzlich> beinhalteter die ISR:> ISR(TIMER1_COMPA_vect)> {> vorZuruckController.handleVorZuruckTimerInterrupt();> }
Toller Name . . .
> Dieser ISR wird später noch von den Entitäten> hochRunterController.handlerHochRunterTimerInterrupt(); so wie für links> rechts aufrufen. Daher eine ISR ruft mehrere Funktionen auf. Ich habe> mich gegen eine inline funktion entschieden hier, weil ich> Leisungseinbußen befürchte.
Jaja, grman Angst. Ein echter, deutscher Ingenieur würde sowas MESSEN,
denn dann hat man eien sichere, OBJEKTIVE Aussage. Denn deine Vermutung
isst falsch. Das Aufrufen von Funktionen in einer ISR kostet mehr
CPU-Last als eine Inline Function . . .
> Die TimerInterrupt sieht dann wie folgt aus:> void VorZuruckController::handleVorZuruckTimerInterrupt()> {> bool currentPrimaryHallStates[VOR_ZURUCK_MOTOR_AMOUNT] =
VOR_ZURUCK_MOTOR_AMOUNT, jaja, Degnglish at its Pest . . .
> {getVorZuruck1PrimaryHall(), getVorZuruck2PrimaryHall()};> bool currentSecondayHallStates[VOR_ZURUCK_MOTOR_AMOUNT] => {getVorZuruck1SecondaryHall(), getVorZuruck2SecondaryHall()};> for (int i = 0; i < VOR_ZURUCK_MOTOR_AMOUNT; i++)> {> if (previousPrimaryHallPinResults[i] == LOW &&> currentPrimaryHallStates[i] == HIGH)> {> if (currentSecondayHallStates[i] == LOW)> {> hallCounters[i]--;> }> else> {> hallCounters[i]++;> }> }> previousPrimaryHallPinResults[i] = currentPrimaryHallStates[i];> }> }
Deine Dekodierung eines Drehgebers ist immer noch Unfug.
Lernresistenz?
> Danke an alle, die geholfen haben
Naja, du hast weniger gelernt als du glaubst.
Stefan schrieb:> Was ist deine Empfehlung?> cphs[MA] // array holding current hall states of primary pin>> statt> currentPrimaryHallStates[VOR_ZURUCK_MOTOR_AMOUNT]>> Diese Diskussion führe ich wenn dann mit meiner junior Entwicklern. Ihr> seid besser als das. Trotzdem empfehle ich dir die Lektüre Clean Code> von mipt Verlag.
Du bist mir ja ein gaaanz Schlauer! Hast gestern das Buch gelesen und
verkündest heute schon endgültige Wahrheiten! Respekt!
Stefan schrieb:> Was ist deine Empfehlung?> cphs[MA] // array holding current hall states of primary pin>> statt> currentPrimaryHallStates[VOR_ZURUCK_MOTOR_AMOUNT]
Es mag Dich überraschen, aber es gibt einen Mittelweg.
Stefan schrieb:> void VorZuruckController::handleVorZuruckTimerInterrupt()
Kann der VorZuruckController noch andere VorZuruck-Interrupts auswerten?
Wenn nein: Dann reicht
> void VorZuruckController::handleTimer()
Falk B. schrieb:> Deine Dekodierung eines Drehgebers ist immer noch Unfug.> Lernresistenz?
Wie würdest du es denn machen?
Und ich bitte dich. Bevor du mir den Artikel mit das hier schickst...
1
int8_t code, diff, tmp;
2
3
tmp = ENCODER_PIN;
4
code = 0;
5
if ( tmp & PHASE_A ) code = 3;
6
if ( tmp & PHASE_B ) code ^= 1; // convert gray to binary
7
diff = last - code; // difference last - new
8
if( diff & 1 ) { // bit 0 = value (1)
9
last = code; // store new as next last
10
enc_delta += (diff & 2) - 1; // bit 1 = direction (+/-)
11
}
erkläre es gerne. Dieser Code ist 0 Verständlich und kaum Wartbar für
ein µC Programmier-Laie
Viele Grüße
Stefan schrieb:>> Deine Dekodierung eines Drehgebers ist immer noch Unfug.>> Lernresistenz?>> Wie würdest du es denn machen?
Richtig.
> Und ich bitte dich. Bevor du mir den Artikel mit das hier schickst...> int8_t code, diff, tmp;> tmp = ENCODER_PIN;> code = 0;> if ( tmp & PHASE_A ) code = 3;> if ( tmp & PHASE_B ) code ^= 1; // convert gray to binary> diff = last - code; // difference last - new> if( diff & 1 ) { // bit 0 = value (1)> last = code; // store new as next last> enc_delta += (diff & 2) - 1; // bit 1 = direction (+/-)> }>> erkläre es gerne. Dieser Code ist 0 Verständlich
Stimmt. Das ist ultrakompakter Code mit vielen Tricks bei der
Optimierung.
>und kaum Wartbar für> ein µC Programmier-Laie
Ist das ein Maßstab, daß Laien sowas warten können? Laien und Profis
benutzen Bibliotheken als Black Box, d.h sie benutzen die Funktion von
außen und kümmern sich nicht um den internen Aufbau und Funktion. Das
reicht meistens.
Wer es schöner und verständlicher will, nimmt den klassischen Code mit
Tabelle. Verständlich und wartbar, auch wenn da so gut wie nie was
gewartet werden muss.
https://www.mikrocontroller.net/articles/Drehgeber#Dekoder_f%C3%BCr_Drehgeber_mit_wackeligen_Rastpunkten
Dort gibt es auch die Tabelle ohne wackelige Rastpunkte. Die ist am Ende
kaum langsamer als die Superfreakoptimierung.
Sherlock 🕵🏽♂️ schrieb:> Lieber lang als zu kurz.
Zwischen lang und viel zu lang liegen aber Welten.
Und wie ich oben beschrieb, im vorliegenden Fall auch komplett
überflüssige Redundanz.
Harald K. schrieb:> Sherlock 🕵🏽♂️ schrieb:>> Lieber lang als zu kurz.>> Zwischen lang und viel zu lang liegen aber Welten.>> Und wie ich oben beschrieb, im vorliegenden Fall auch komplett> überflüssige Redundanz.
Das sehe ich anders.
"Lange" Symbole sind nicht redundant, sondern beschreiben möglichst
genau, was ihre Job ist. Deswegen sind sie eben nicht redundant. Sie
enthalten für Menschen brauchbare und nützliche Information.
Sie sind also höchstens redundant für die Toolchain. Aber auch "kurze"
Symbole sind für die in aller Regel noch ziemlich redundant. Wenn man
hier konsequent auf Maschinenlesbarkeit optimieren wollte, müsste man
konsequenterweise Symbole nach dem Muster "Irgendein nichtnumerischer
Kennbuchstabe" + irgendeine Zahl verwenden. Ist natürlich völlig
idiotisch, weil überhaupt nicht mehr menschenlesbar. Deswegen macht das
auch niemand. Ich gehe davon aus: Nichtmal du machst das so...
Alles andere ist nur eine Frage des Komforts. Benutzer von modernen IDEs
haben kein Problem mit langen Symbolen: Die Auto-Vervollständigung nimmt
ihnen die Tipparbeit ab (zumindest nach dem ersten Erscheinen im scope).
Sprich: Nur die Ewiggestrigen, die heute noch allein mit völlig dummen
Editoren hantieren, haben damit ein Problem. Aber: was geht uns das
Elend dieser Zurückgebliebenen an?
Deren schwachsinnige Lösung ist halt: Halte es so kurz wie (nach eigenem
Empfinden) möglich. Was natürlich die unerträgliche Arroganz dieser
Leute offen legt. Wenn jemand anders den Quelltext lesen muß, ist
nämlich keinesfalls voraussetzbar, dass er die kruden (und vermutlich
nirgendwo hinterlegten) Regeln zur Bildung von Symbolen des
ursprünglichen Programmierers kennt oder auf Anhieb nachvollziehen kann.
Um das zu können, muß man mindesten schon recht tief im Projekt stecken.
Und oft reicht aber nicht mal das, weil der HERR Programmierer selber
die Sache selber schon nicht konsequent durchgezogen hat, es also keine
Möglichkeit gibt rückwirkend die "Regeln" zu ermitteln, nach denen er
die Symbolnamen festgelegt hat.
So sieht das aus.
Ob S. schrieb:>> Zwischen lang und viel zu lang liegen aber Welten.>>>> Und wie ich oben beschrieb, im vorliegenden Fall auch komplett>> überflüssige Redundanz.>> Das sehe ich anders.>> "Lange" Symbole sind nicht redundant, sondern beschreiben möglichst> genau, was ihre Job ist. Deswegen sind sie eben nicht redundant. Sie> enthalten für Menschen brauchbare und nützliche Information.
Das kann so sein. Aber der Übergang von nützlicher Information zu
Geschwätzigkeit ist fließend.
> Sie sind also höchstens redundant für die Toolchain.
Davon war nie die Rede! Für den Compiler ist die Länge von Namen für
Objekte vollkommen schnuppe. Wenn man will, kann man den sogar in UTF-8
in chinesisch compilieren lassen!
> Alles andere ist nur eine Frage des Komforts. Benutzer von modernen IDEs> haben kein Problem mit langen Symbolen: Die Auto-Vervollständigung nimmt> ihnen die Tipparbeit ab (zumindest nach dem ersten Erscheinen im scope).
Stimm, ist aber gar nicht das Problem. Sondern die Geschwätzigkeit und
die damit ab einem bestimmten Punkt sinkende Lesbarkeit!
Früher hat man Sprachen wie Pascal & Co für ihre Geschwätzigkeit
kritisiert. Teilweise zu recht.
> Deren schwachsinnige Lösung ist halt: Halte es so kurz wie (nach eigenem> Empfinden) möglich.
Das ist, korrekt angewendet, auch die richtige Zeisetzung! Der Name
einer Variable bzw. Funktion sollte so kurz wie möglich und gleichzeitig
so aussagekräftig wie möglich sein. Daß man da einen Kompromiss finden
muss und das individuell zu verschiedenen Ergebnissen führt, ist klar.
> Was natürlich die unerträgliche Arroganz dieser> Leute offen legt. Wenn jemand anders den Quelltext lesen muß, ist> nämlich keinesfalls voraussetzbar, dass er die kruden (und vermutlich> nirgendwo hinterlegten) Regeln zur Bildung von Symbolen des> ursprünglichen Programmierers kennt oder auf Anhieb nachvollziehen kann.
Von kruden, unverständlichen Abkürzungen war nie die Rede. Du erfindest
nur Aussagen, die nie gemacht wurden, um sie dann "glanzvoll"
widerlegen zu können. Ein uralter Trick.
> Um das zu können, muß man mindesten schon recht tief im Projekt stecken.> Und oft reicht aber nicht mal das, weil der HERR Programmierer selber> die Sache selber schon nicht konsequent durchgezogen hat, es also keine> Möglichkeit gibt rückwirkend die "Regeln" zu ermitteln, nach denen er> die Symbolnamen festgelegt hat.
Solche Projekte gibt es, aber die sind nicht eine Sekunde das Thema
hier. Setzen, Sechs! Thema verfehlt!
> So sieht das aus.
Nö.
Falk B. schrieb:> Solche Projekte gibt es
Ja. Viel zu viele davon gibt es. Es ist eher die Regel als die Ausnahme.
> Thema verfehlt!
Nein. Eben weil das so ist, wie es ganz offensichtlich ist, passt das
nur zu genau zum Thema.
Harald K. schrieb:>> "Lange" Symbole sind nicht redundant, sondern beschreiben möglichst>> genau, was ihre Job ist.>> Betrachte bitte das genaue Beispiel, das ich meine.>> Beitrag "Re: Arduino: digitalRead in Interrupt ist unzuverlässig"
In der Tat. Da ist es eher kindliche Naivität, die amüsiert.
Jeder Programmierer mit ausreichend Erfahrung und ohne ideologischen
Schaden, welcher Art auch immer, würde das deutlich anders nennen. Z.B.
1
voidServo::control()
2
voidPosRegler::Zyklus()
Denn das ist ein "Vor Zurück Controller", ein Positionsregler,
neudeutsch Servo.
https://de.wikipedia.org/wiki/Servo
Servus! ;-)
Die Kunst der sinnvollen Namensgebung erfordert Übung. Kurz und prägnant
sollten Namen sein, keine Romane oder Lebensgeschichten und auch kein
Beamtendeutsch! Aber auch keine autistischen Abkürzungen oder Fragmente
aus der 8.3 DOS-Dateinamenszeit!
Harald K. schrieb:> Ob S. schrieb:>> Das sehe ich anders.>>>> "Lange" Symbole sind nicht redundant, sondern beschreiben möglichst>> genau, was ihre Job ist.>> Betrachte bitte das genaue Beispiel, das ich meine.>> Beitrag "Re: Arduino: digitalRead in Interrupt ist unzuverlässig"
Habe ich mir angesehen. Und ich würde sagen, dass im gegebenen Kontext
für einen Menschen handleVorZuruckTimerInterrupt() deutlich
aussagekräftiger ist als handleTimer().
Man sieht sofort: es geht um die Bewegung und die Quelle des Ereignisses
ist ein Timer-Interrupt (der eigentlich von seiner Natur her mit einer
Bewegung nix zu schaffen hat, sondern hier konkret halt dazu benutzt
wird, eben diese zu steuern)
Cyblord -. schrieb:> Ich habe noch nie verstanden warum "einfach" ein Kriterium ist
weil es sinnvoller ist als jedesmal das Rad neu zu erfinden Räder nutzen
ist.
Deswegen sind ja auch Arduino LIBs erfunden worden, unabhängig davon
haben auch ander C LIBs für Standardlösungen im Netz eingestellt.
Es ist wirklich nicht sinnvoll alles immer selbst zu erfinden außer zum
Lernen, aber die menschliche Restlebenzeit ist nun mal endlich.
Man kann die alten Griechen, Araber, Inder nur danken für Mathematik,
denn alles selber rausfinden? Man lebt ja nicht in einer Tonne und hat
Gönner die einem ein Leben sponsorn.
Ob S. schrieb:> Habe ich mir angesehen. Und ich würde sagen, dass im gegebenen Kontext> für einen Menschen handleVorZuruckTimerInterrupt() deutlich> aussagekräftiger ist als handleTimer().
Du hast geflissentlich unterschlagen, daß das "VorZuruck" bereits im
Klassennamen steht.
Und ein Timerinterrupt ist das nicht, sondern nur eine Funktion, die aus
einem Timerinterrupt heraus aufgerufen wird. Die sollte man also besser
nicht "Interrupt" nennen.
Eine ansatzweise verständigen Menschen ist es geläufig, daß Funktionen
aus einem Timerinterrupt heraus aufgerufen werden, und dann ist der Name
"handleTimer" nahliegender und aussagekräftiger.
Wenn man "leichte" oder "inklusive" Programmierung verwenden möchte, um
genauer zu erklären, was hier passiert, und auch wirklich jeden
mitzunehmen, müsste man die Funktion
Ob S. schrieb:> Ja, durchaus. Was spricht dagegen?
Der gesunde Menschenverstand eines Programmierers. Aber Geschätzigkeit
liegt ja im Trend, auch in der IT.
Norbert schrieb:> Nicht jeder ist im Besitz eines 32:9 Monitors. ;-)
Schon mal was von einer Laufschrift gehört? ;-)
So einen Müll haben wir in einer unserer "Software" für ne
Gerätesteuerung. Dort ist die Versionsnummer der Software mit Datum und
Zeit dargestellt. Und weil das nicht auf den Bildschirm 16:9(!) passt,
scrollt das in der Zeile. Auf so einen Schwachsinn kommen nur
Softwerker! Unfähige Softwerker, die nur rumspielen, anstatt ihren Job
zu machen.
Ob S. schrieb:> Ja, durchaus. Was spricht dagegen?
Im Grunde genommen haben wir das falsch betrachtet, denn hier gehört
natürlich auch noch der Instanzenname des Objekts dazu.
Der Threadstarter schrieb
wir haben herausgefunden, daß der bessere Name für die Funktion (oder
"Methode")
FunctionToHandleVorZuruckCalledFromTimerInterruptEvery1000msec()
ist, also
Was jetzt noch unbefriedigend ist, ist der Instanzenname. Der lautet
genauso wie der Klassenname, und unterscheidet sich nur durch einen
Kleinbuchstaben:
Ich bedanke mich recht herzlich für diese Diskussion, ob meine Variablen
und Funktionen so heißen, wie es manchen von euch gefällt. Ich habe
diese Diskussion zur Kenntnis genommen und mit dem Hauptproblem
unbeeinflusst weitergemacht.
@Falk: Danke für den Verweis auf die Tabelle. Korrigiere mich bitte,
wenn ich was falsch verstanden habe! Ich versuche das ganze zu erlernen
:)
Das Beispiel versucht die Zustandsänderungsverhalten vorher zu berechnen
und anschließend das nur auslesen und addieren. Eine andere Tabelle
versucht die physikalisch übliche Prellungen abzufangen, also sogenannte
debouncing für die Drehgeber zu betreiben.
Erst schiebt er den letzten Zustand um 2 bits (wenn es noch kein Zustand
gab, dann 00 nach 2 links = immer noch 00)
Dann maskiert er den überlauf, damit wir nur den letzten Zustand haben
(& 0x0F)
Dann ließt der den PINA Register PA1 (auf mega wäre das Pin 23) und
speichert in den 1. bit
Dann ließt der den PINA Register PA3 (auf mega wäre das Pin 25) und
speichert in den 0. bit
Dann addiert er das Ergebnis aus der Tabelle für diese vier bit Resultat
in die Positionsvariable.
Soweit so gut. Warum macht man das? Bei einer Normalverteilung der
Prellungen geht es ja in beide Richtungen und die Wahrscheinlichkeit für
hintereinander folgende einseitige Prellungsfehler ist gering genug,
dass es nicht auffallen dürfte. Ein Kopplungsfehler würde sich ebenfalls
in Grenzen halten, wenn überhaupt. Ist das Auslesen von einem Array
deutlich schneller als zwei if abfragen?
Ich frage mich, ob es sich wirklich lohnt den Code unendlich
unverständlicher zu machen, wenn es sich hierbei um 1-2 µs handelt.
Viele Grüße
Stefan schrieb:> Das Beispiel versucht die Zustandsänderungsverhalten vorher zu berechnen> und anschließend das nur auslesen und addieren.
Nein. Es ist eine Statemachine, die ein exaktes Verhalten auf die
Eingangssignale zeigt.
> Eine andere Tabelle> versucht die physikalisch übliche Prellungen abzufangen, also sogenannte> debouncing für die Drehgeber zu betreiben.
Ja. Ist aber auch eine state machine, nur mit leicht anderem Verhalten.
> Erst schiebt er den letzten Zustand um 2 bits (wenn es noch kein Zustand> gab, dann 00 nach 2 links = immer noch 00)> Dann maskiert er den überlauf, damit wir nur den letzten Zustand haben> (& 0x0F)
Den letztzen + neuen.
> Dann ließt der den PINA Register PA1 (auf mega wäre das Pin 23) und> speichert in den 1. bit> Dann ließt der den PINA Register PA3 (auf mega wäre das Pin 25) und> speichert in den 0. bit> Dann addiert er das Ergebnis aus der Tabelle für diese vier bit Resultat> in die Positionsvariable.
Das Schieben und einlesen ist der Zustand und Richtungsvektor der State
Machine. Die beiden alten Bits sind der IST-Zustand (Code), die beiden
neuen Bits die Richtung (+/- oder nix).
> Soweit so gut. Warum macht man das?
Um die beiden Richtungssignale korrekt zu dekodieren.
> Bei einer Normalverteilung der> Prellungen geht es ja in beide Richtungen und die Wahrscheinlichkeit für> hintereinander folgende einseitige Prellungsfehler ist gering genug,> dass es nicht auffallen dürfte.
Darum geht es gar nicht. Mit Wahrscheinlichkeiten hat das nix zu tun.
> Ein Kopplungsfehler würde sich ebenfalls> in Grenzen halten, wenn überhaupt. Ist das Auslesen von einem Array> deutlich schneller als zwei if abfragen?
Es ist vor allem logisch korrekt. Deine Abfragen sind es nicht. Du hast
das Thema Drehgeber und Dekodierung von Graycode noch nicht verstanden.
> Ich frage mich, ob es sich wirklich lohnt den Code unendlich> unverständlicher zu machen, wenn es sich hierbei um 1-2 µs handelt.
Der Code ist mit Tabelle ist sehr gut verständlich. Der komische vom
Peter sind seine typischen Mikrooptimierungen, die praktisch wenig
bringen aber aus der guten, alten Assemblerzeit stammen, wo man jeden
Takt und jedes Byte geschätzt hat.
Roland F. schrieb:> Johann L. schrieb:>> ...und hier nochmal mit einem aktuellen Compiler...>> Welcher genau ist das?
v14
Wobei es die kürzeren ISR Prolog+Epilog bereits ab GCC v8 / Binutils
v2.29 gibt.
Im Code habe ich "last" als uint8_t definiert anstatt als int8_t. Der
Wert ist ja in [0, 15], und mit int8_t bekommt man dann ein
überflüssiges Sign-Extend beim Berechnen der Array-Adresse.
...eventuell könnte auch das der Compiler optimieren. Muss ich mir mal
anschauen.