Forum: Mikrocontroller und Digitale Elektronik Interrupt & Lookup Table - eine gute Idee?


von Sef C. (sefco)


Lesenswert?

Hallo zusammen,

ich verwende in einem ferngesteuerten Modellauto einen ATMEGA2560 der 
die (Servo-)PWMs des Empfängers auswertet und damit LEDs steuert.
Allerdings "lese" ich die PWMs nicht einfach mit, sondern gebe sie dann 
wieder verändert an den Motor aus. Damit kann ich beispielsweise einen 
"Kindermodus" realisieren, mit dem das Auto dann einfach langsamer 
fährt.
Bisher habe ich die Lenk-Servo PWM nicht verändert, was ich nun gerne 
tun würde da das Auto brutal schnell lenkt. Ich würde gerne das 
Lenkverhalten beliebig verändern, z.B. mit einer Lookup Table in der ich 
für eine gemessene Pulsweite dann eine alternative Pulsweite erzeuge. 
Der Zusammenhang kann nicht-linear sein, weshalb mir gerade nur eine 
Lookup Table einfällt.

Da in meinem ATMEGA2560 fast alles interruptbasiert abläuft, frage ich 
mich, ob eine Lookup Table eine gute Idee ist und wie ich sie 
realisieren soll. Kann man das bspw. mit SWITCH CASE implementieren, 
oder dauert das in einer ISR zu lange?

Schönen Dank für die Tipps!

von Wolfgang (Gast)


Lesenswert?

Sef C. schrieb:
> Kann man das bspw. mit SWITCH CASE implementieren,
> oder dauert das in einer ISR zu lange?

Eine Look-Up Tabelle wird schnell, wenn du den empfangenen Wert als 
Index für die Tabelle verwendest.

von Thomas Z. (usbman)


Lesenswert?

Sef C. schrieb:
> Kann man das bspw. mit SWITCH CASE implementieren,
> oder dauert das in einer ISR zu lange?

ein guter Compiler wird selbst entscheiden ob er eine switch  Anweisung 
in if /else wandelt oder per Tabelle macht. Generell ist es so, dass 
wenige eng zusammenliegende Werte per Tabelle efektiver sind. In anderen 
Fällen wird ein binärer Baum aus if/else gebildet.

von re (Gast)


Lesenswert?

Sef C. schrieb:
> ob eine Lookup Table eine gute Idee ist

... ist meistens die schnellste Methode, etwas zu berechnen, eben weil 
man nichts rechnen muss. Oft eine gute Idee, kostet eben 
Speicherplatz.


> Kann man das bspw. mit SWITCH CASE implementieren,

Eigentlich nicht.

Lookup-Tabellen gehen so, dass Du ein Array mit den Zielwerten anlegst, 
wo Du dann nur noch mit einem Index hineingehst:

| ausgangs_pwm_wert = tabelle[ eingangs_pulsweite_in_mikrosekunden ];

Der 2560 hat ja sogar hinreichend RAM um das direkt so zu machen, wenn 
man geschickt skaliert.

Normalerweise legt man aber die Tabelle eher im reichlich vorhandenen 
PROGMEM ab und greift per "pgm_read_word()" drauf zu. Dauert etwas 
länger, aber da die Servos sowieso nur alle 20ms einen neuen Steuerpuls 
kriegen hat man reichlich Zeitbudget.

hth
(re)

von ... (Gast)


Lesenswert?

Wenn du die Reaktionszeit des Lenkservo verändern willst, musst du 
Änderung limitieren. Ein typisches Servo braucht für einen Sprung von 
1000µs auf 2000µs ca. 300ms. Wenn du mit 50Hz arbeitest kannst z.b. nur 
100µs Schritte pro 20ms zulassen. Allerdings will man idR dass das Servo 
dem Steuerbefehl so rasch wie möglich folgt...
Wenn du hingegen eine Expo-Funktion nachrüsten willst, da der Sender 
dies nicht beherrscht, ist eine LUT sicher eine gute Wahl. Ich sehe 
allerdings nicht, warum die Berechnung des neuen PWM-Wertes in einen 
Interrupt muss.

von Thomas (kosmos)


Lesenswert?

ich würde einfach eine Änderung nur zulassen wenn eine bestimmte Zeit 
rum ist. Und diese Änderung auf kleine Werte begrenzen.

Angenommen Momentanwert ist 20, nun trudelt ein Wert von 255 ein. Ich 
schaue nun ob die Zählvariante einen bestimmten Wert erreicht hat, setze 
diese zurück und erhöhe um den Wert um 5 statt direkt auf 255. Diese 
Zählvariante kannst du ja in einer vorhanden Interruptroutine einbauen 
die sowieso regelmäßig angesprungen wird. Außerdem könntest du den 
Lenkwinkel auch generell oder geschwindigkeitsabhängig begrenzen. Je 
nachdem welche Werte du alles auswertest.

von Sef C. (sefco)


Lesenswert?

... schrieb:
> Wenn du hingegen eine Expo-Funktion nachrüsten willst, da der Sender
> dies nicht beherrscht, ist eine LUT sicher eine gute Wahl.

Genau sowas ist der Plan.

... schrieb:
> Ich sehe
> allerdings nicht, warum die Berechnung des neuen PWM-Wertes in einen
> Interrupt muss.

Ich messe die tatsächliche Pulsbreite mit einem Timer overflow counter, 
der nach einem externen Interrupt gestartet wurde. Der Timer overflow 
kommt nach 16 us, sprich ich bekomme 62 bis 125 Timer overflows für 
Pulse zwischen 1 und 2 ms.
Der Timer muss noch andere Dinge tun und meine anderen Timer sind auch 
schon gut ausgelastet, von daher löse ich das so.

re schrieb:
> | ausgangs_pwm_wert = tabelle[ eingangs_pulsweite_in_mikrosekunden ];

Wenn meine Eingangsindizes eben 62 - 125 sind, müsste ich 62 Werte 
speichern die ich gar nicht brauche. Geht problemlos?

von re (Gast)


Lesenswert?

Sef C. schrieb:
> Wenn meine Eingangsindizes eben 62 - 125 sind, müsste ich 62 Werte
> speichern die ich gar nicht brauche. Geht problemlos?

Warum denn nicht? Platz hat der 2560 dafür reichlich. Die Tabelle 
interessiert es nicht, ob die Werte jemals brauchen wirst, oder nicht, 
das macht keinen Unterschied.

Um Speicherplatz zu sparen, würde man aber eher vorher vom Index eben 
diese 62 subtrahieren (Bereichsprüfung ist dann nützlich).

(re)

von Sef C. (sefco)


Lesenswert?

re schrieb:
> Um Speicherplatz zu sparen, würde man aber eher vorher vom Index eben
> diese 62 subtrahieren (Bereichsprüfung ist dann nützlich).

Lieber 62 unnötige Werte oder regelmäßig eine Subtraktion? Mir 
erscheinen die unnötigen Werte besser.

: Bearbeitet durch User
von Baendiger (Gast)


Lesenswert?

Sef C. schrieb:
>> Um Speicherplatz zu sparen, würde man aber eher vorher vom Index eben
>> diese 62 subtrahieren (Bereichsprüfung ist dann nützlich).
>
> Lieber 62 unnötige Werte oder regelmäßig eine Subtraktion? Mir
> erscheinen die unnötigen Werte besser.

Je nachdem ob du genug Speicherplatz hast oder nicht. Letztlich hast du 
den Speicherplatz mit dem Kauf des Prozessors bezahlt - also nutze ihn 
ruhig aus.

von Sef C. (sefco)


Lesenswert?

Baendiger schrieb:
> Je nachdem ob du genug Speicherplatz hast oder nicht. Letztlich hast du
> den Speicherplatz mit dem Kauf des Prozessors bezahlt - also nutze ihn
> ruhig aus.

Top, danke!

von A. S. (Gast)


Lesenswert?

Thomas Z. schrieb:
> ein guter Compiler wird selbst entscheiden ob er eine switch  Anweisung
> in if /else wandelt oder per Tabelle macht. Generell ist es so, dass
> wenige eng zusammenliegende Werte per Tabelle efektiver sind. In anderen
> Fällen wird ein binärer Baum aus if/else gebildet.

Der Compiler kann zwar entscheiden, wie er ein switch/case umsetzt. Er 
kann aber nicht erkennen, dass in diesem Fall die Geschwindigkeit 
essentiell ist.

Wenn LUT, dann auch wirklich als LUT anlegen und nicht auf den Compiler 
verlassen.

Sef C. schrieb:
> Lieber 62 unnötige Werte oder regelmäßig eine Subtraktion? Mir
> erscheinen die unnötigen Werte besser.

Düfte keine Rolle spielen, da Du eh eine Bereichsprüfung brauchst. Und 
je nach Art der Erzeugung der LUT kann ein kleinerer Bereich auch 
sinnvoll sein.

von ... (Gast)


Lesenswert?

hmm. ich halte ja auch nicht viel von 12bit Servoauflösung, aber 6bit 
dürfte etwas wenig sein. Je nach Geometrie der Anlenkung bist du da ja 
bei ca. 1.5° Schrittwsiten. Die typischen Trimmungsschritte älterer 
Anlagen lagen eher bei 2-5us. Bist du wirklich sicher dass du Expo 
brauchst, und nicht einfach nur eine unzureichende Neutralposition wegen 
zu geringer Auflösung hast? 8bit können selbst Billigfabrikate idR 
sauber stellen. Aber mit 3-4 Trim-Klicks Schrittweite, kannst du ev. gar 
nicht erst geradeaus fahren...
Wenn irgendwie möglich, würde ich eher per input capture die Impulsdauer 
zumindest auf 10bit messen, anschließend die Umrechnungen durchführen 
und die Impulserzeugung aktualisieren. Die paar us Latenzzeit dürften zu 
vernachlässigen sein, sonst hätte das multi-wii projekt auch nie 
funktioniert.

von Stefan F. (Gast)


Lesenswert?

Ich hatte mal anhand eines R/C Autos ausprobiert ob 8-bit Timer für 
Modellbau-Servos reichen, weil es mir aus dem Bauch heraus zu wenig 
vorkam. Es hat sehr gut funktioniert, sowohl die Lenkung als auch der 
Antrieb. Ich habe die Abstufungen nicht gespürt.

Ich muss dazu sagen, dass ich mit meinem 8-bit Timer allerdings 5 Servos 
Round-Robin angesteuert habe. Die Zyklus-Zeit für einen Timer-Durchlauf 
war daher nicht 20ms sondern 8ms, was die Auflösung für jeden einzelnen 
Servo um Faktor 5 verbesserte.

Wenn mehr als 8-bit zur Verfügung stehen, würde ich das nutzen. Aber 
wenn nicht, ist es keine Schande.

von Thomas F. (igel)


Lesenswert?

Sef C. schrieb:
> Allerdings "lese" ich die PWMs nicht einfach mit, sondern gebe sie dann
> wieder verändert an den Motor aus.

> z.B. mit einer Lookup Table in der ich für eine gemessene Pulsweite dann
> eine alternative Pulsweite erzeuge.

Habe sowas vor ein paar Jahren schon mal auf einem Atmega328 gemacht. 
Allerdings habe ich den Ausgabewert nicht in der ISR berechnet, sondern 
im Hauptprogramm. Wenn ich mich recht erinnere wurde die Routine zur LUT 
dort im 1ms Zeitraster aufgerufen. Hat damals wunderbar funktioniert.

Code war in Assembler, weshalb er dir wohl wenig nützt.

von Jakob (Gast)


Lesenswert?

Wenig Zeit für die Interrupt-Routine und genug Reserven im SRAM:
Dann ist die LUT genau richtig!

Wenig SRAM und genug Zeit: Im Interupt ausrechnen.

Notfalls kann man auch in der Interrupt-Routine, vor der Ausrechnerei 
den Interrupt für zeitkritische Routinen wieder freigeben.

Oder das Ausrechnen per Flag-Benachrichtigung im Hauptprogramm
nachziehen.

Oder, oder ...

Entweder man programmiert gern klug und resourcensparend, oder man nimmt
gleich was Überdimensioniertes (Raspi o.b.) ....

von A. S. (Gast)


Lesenswert?

Jakob schrieb:
> Wenig SRAM und genug Zeit: Im Interupt ausrechnen.

Eigentlich gar nicht im Interrupt

von Axel R. (axlr)


Lesenswert?

Input Capture für die µs und Flags setzen im Interrupt. Ausrechnen, 
Schalten und Walten (ua. Flags zurücksetzen), dann in der Main. LUT im 
Flash.

Wie schaut denn deine SW aus? Sowas haben hier schon viele programmiert. 
Vllt. kann man da was "optimieren". Mega88 oder AtMega16L; etwas von der 
Größe war damals(tm) mit das gebräuchlichste für "sowas". Niemand wäre 
auf die Idee gekommen, dafür einen AtMega2560 zu verwenden ...

von Klaus W. (mfgkw)


Lesenswert?

Sef C. schrieb:
> Wenn meine Eingangsindizes eben 62 - 125 sind, müsste ich 62 Werte
> speichern die ich gar nicht brauche. Geht problemlos?

In C geht doch jede Sauerei...


Leg dir die 125-62+1 = 64 Werte in ein Feld:
1
// oder anderer Typ statt uint16_t, je nachdem was du brauchst
2
const uint16_t  meineLUT_0_bis_63[64] = 
3
{
4
      100,   // lookup-Wert für Index 62
5
      101,   // lookup-Wert für Index 63
6
...
7
      166,   // lookup-Wert für Index 124
8
      157,   // lookup-Wert für Index 125
9
};

Dann einen Zeiger, der 62 Elemente unter das Feld zeigt:
1
const uint16_t   * const meineLUT_62_bis_125 = meineLUT_0_bis_63 - 62;

Jetzt kannst du mit dem Zeiger auf meineLUT_62_bis_125[62] bis 
meineLUT_62_bis_125[125] zugreifen.

Die Elemente daneben sind natürlich tabu...

Der Zeiger meineLUT_62_bis_125 zeigt jetzt zwar auf Speicher, der dich 
nichts angeht. Solange du aber den richtigen Index nimmst, passt alles 
und du hast keinen Speicher vergeudet (außer den paar Byte für den 
Zeiger, wenn der Compiler das nicht beim Optimieren wegputzt).

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

re schrieb:
> Normalerweise legt man aber die Tabelle eher im reichlich vorhandenen
> PROGMEM ab und greift per "pgm_read_word()" drauf zu.

Man kann auch mehrere Tabellen zur Compilezeit berechnen lassen und im 
Flash ablegen. Zur Laufzeit schaltet man dann nur bequem zwischen den 
Tabellen um, indem man einen Pointer auf die Startadresse setzt. Man muß 
dann nichts mehr neu berechnen.
Das Laden einer Pointervariablen kostet kaum Zeit. Ob man das im Main 
oder im Interrupt macht, ist daher egal.
Die Berechnung kann man z.B. in Excel machen lassen und dann includen.

von Dentaa (Gast)


Lesenswert?

Sef C. schrieb:
> Bisher habe ich die Lenk-Servo PWM nicht verändert, was ich nun gerne
> tun würde da das Auto brutal schnell lenkt. Ich würde gerne das
> Lenkverhalten beliebig verändern, z.B. mit einer Lookup Table in der ich
> für eine gemessene Pulsweite dann eine alternative Pulsweite erzeuge.
> Der Zusammenhang kann nicht-linear sein, weshalb mir gerade nur eine
> Lookup Table einfällt.

Es gibt auch Fernbedienungen die das schon können und sich der Lookup an 
der Fernbedienung online umstellen lässt.

von Jakob (Gast)


Lesenswert?

A. S. (achs) schrieb
> Eigentlich gar nicht im Interrupt

Warum nicht? Weil es die Mama verboten hat?

Es gibt Programme, die bestehen aus einer leeren Main-Schleife und
einer Interruptroutine. Im 10 ms - Interrupt kann ich doch Berechnungen
z.B. über <= 9 ms machen lassen...

Klar, wer nicht weiß, wie man Resourcen handhabt, hält sich besser
an simple Faustregeln. Wer allerdings weiß, was er/sie macht, kommt
damit schneller, höher, weiter!

von re (Gast)


Lesenswert?

Peter D. schrieb:
> re schrieb:
>> Normalerweise legt man aber die Tabelle eher im reichlich vorhandenen
>> PROGMEM ab und greift per "pgm_read_word()" drauf zu.
>
> Man kann auch mehrere Tabellen zur Compilezeit berechnen lassen und im
> Flash ablegen. [...] Die Berechnung kann man z.B. in Excel machen
> lassen und dann includen.

D'accord, das ist für µController sowieso ein universell brauchbares 
Mantra, alles, was irgendwie zur Compilezeit erledigt werden könnte, 
auch tatsächlich zur Compilezeit zu erledigen und nicht erst zur 
Laufzeit. Kostet fast nix und spart oft Ärger.

Auch wenn es im hier vorliegenden Fall von lumpigen 62 Werten ziemlich 
egal ist und der 2560 ziemlicher Overkill ist, wenn er nicht noch was 
anderes zu tun hat.



Jakob schrieb:
> A. S. (achs) schrieb
>> Eigentlich gar nicht im Interrupt
>
> Warum nicht? Weil es die Mama verboten hat? [...]
> Im 10 ms-Interrupt kann ich doch Berechnungen z.B. über <= 9 ms machen

Im Prinzip ja, aber ... das ist imho nur sinnvoll, wenn man 
ausschließlich für das hier und jetzt programmiert und spätere 
Programmerweiterungen kategorisch ausschließt.

Wo dann plötzlich doch mehrere Interrupts gebraucht werden, oder das 
Latenz-Budget doch geringer ist, weil die Berechnungen doch 
umfangreicher werden oder die Interrupts doch mit höherer Frequenz 
kommen, oder ...

Ohne jetzt "Cargo-Cult-Programming" propagieren zu wollen, aber wenn man 
vorher auf Mama gehört hat und alles aus Gewohnheit schön sauber in 
Top-Halves und Bottom-Halves getrennt hat, dann hat man später ein 
Problem weniger.

(re)

von A. S. (Gast)


Lesenswert?

Jakob schrieb:
> Wer allerdings weiß, was er/sie macht, kommt
> damit schneller, höher, weiter!

Der TO wollte die LUT als Switch/Case anlegen. Wenn Du Dir sicher bist, 
dass er weiß, was er/sie macht, dann OK.

von re (Gast)


Lesenswert?

A. S. schrieb:
> Der TO wollte die LUT als Switch/Case anlegen.

... ein Indiz dafür, dass wir alle irgendwie aneinander vorbei reden.

Vielleicht hatte der TO gar keine wirkliche Lookup-Table im Sinn, 
sondern eher eine Tabelle mit Stützstellen für eine Interpolation und 
bloss ein falshces Wort benutzt.

Dann würden auch die ganzen Bemerkungen wegen der Rechenzeit einen Sinn 
ergeben, die bei einer Lookup-Table (im Sinne des etablierten Begriffs) 
ja eigentlich sowas von gegenstandslos sind.

@TO?


(re)


(Wie auch immer: Es hat schon seinen guten Grund, dass kaum wer auf die 
Idee kommt, hier etwas anderes als eine echte Lookup-Table nehmen zu 
wollen.)

von Stefan F. (Gast)


Lesenswert?

A. S. schrieb:
> Der TO wollte die LUT als Switch/Case anlegen.

re schrieb:
> ... ein Indiz dafür, dass wir alle irgendwie aneinander vorbei reden.

Wieso das? Sef ist in mehreren Beiträgen auf den Ansatz mit dem Array 
eingegangen. Von switch/case hat er hingegen nichts mehr geschrieben.

Damit ist die Sache für mich klar: switch/case ist vom Tisch.

von Rainer V. (a_zip)


Lesenswert?

Sef C. schrieb:
> Bisher habe ich die Lenk-Servo PWM nicht verändert, was ich nun gerne
> tun würde da das Auto brutal schnell lenkt. Ich würde gerne das
> Lenkverhalten beliebig verändern, z.B. mit einer Lookup Table in der ich
> für eine gemessene Pulsweite dann eine alternative Pulsweite erzeuge.

Ist eigentlich immer noch Overkill. Schreib doch mal, wie die 
Fernsteuerung mit dem Auto überhaupt aussieht. Wie du das schreibst, 
könnte da für die Lenkung überhaupt kein "richtiges" Servo drin sein. 
Die ganz billigen "Fernsteuerdinge" haben da wirklich nur 
hau-ruck-rechts-links drin, aber eben kein Servo. Auf der anderen Seite 
kann ich mir natürlich nur schwer vorstellen, dass du in so ein 
"Primitivding" einen gestandenen Controller reinbaust. Danke und Gruß, 
Rainer

von Stefan F. (Gast)


Lesenswert?

Ich habe mal für meinen Sohn (als er noch sehr klein war) bei einem R/C 
Auto 2 von 6 Akkus durch ein Stück Draht ersetzt, damit es langsamer 
fährt und lenkt.

von max123 (Gast)


Lesenswert?

hallo
könnte man nicht mit map();
arbeiten?
file:///C:/Program%20Files/WindowsApps/ArduinoLLC.ArduinoIDE_1.8.49.0_x8 
6__mdqgnx93n4wtt/reference/www.arduino.cc/en/Reference/Map.html

von Klaus W. (mfgkw)


Lesenswert?

Gib mal bitte deine Festplatte C:\ frei, damit wir das Lesen können...

von W.S. (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ich habe mal...

Naja.. ich habe mal (als ich noch klein war) mit Teddybär und 
handgeschobenem Holzauto gespielt. Ob nun der Nachwuchs mit 
ferngesteuerten Spielzeug-Autos klüger wird, wage ich zu bezweifeln.

W.S.

von Stefan F. (Gast)


Lesenswert?

W.S. schrieb:
> Naja.. ich habe mal (als ich noch klein war) mit Teddybär und
> handgeschobenem Holzauto gespielt. Ob nun der Nachwuchs mit
> ferngesteuerten Spielzeug-Autos klüger wird, wage ich zu bezweifeln.

Darum ging es nicht. Es ging um den Tipp, dass manche R/C Autos wie 
gewünscht langsamer werden und trotzdem gut funktionieren, wenn man 
einfach die Batteriespannung senkt.

von max123 (Gast)


Lesenswert?


von Stefan F. (Gast)


Lesenswert?

max123 schrieb:
> könnte man nicht mit map() arbeiten?

Nein, weil

Sef C. schrieb:
> Der Zusammenhang kann nicht-linear sein, weshalb mir gerade nur eine
> Lookup Table einfällt.

von Rainer V. (a_zip)


Lesenswert?

Stefan ⛄ F. schrieb:
> Es ging um den Tipp, dass manche R/C Autos wie
> gewünscht langsamer werden und trotzdem gut funktionieren, wenn man
> einfach die Batteriespannung senkt.

Und das wird hier sicher auch die Lösung sein. Denn es kann sich ja nur 
um ein "Primitivding" handeln. Ein gutes Auto mit Steuerung hätte das 
alles als Einstellmöglichkeit schon drin. Kauf ein neues Ding zum 
Spielen...
Gruß Rainer

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.