Hallo,
ich habe hier für einen Roboter einen adxl202 und einen
Modellbaukreisel, jeweils mit PWM-Ausgang.
Vom adxl benötige ich nur eine Achse, daher lassen ich den anderen
Ausgang des adxl unbeschaltet.
Mir geht es nun darum, mit einem Mega8 das PWM-Signal des adxl und des
Kreisels einigermassen genau zu messen.
Das adxl-Signal wird etwa alle 3.75 ms Sekunden wiederholt (leicht
schwankend), der Kreisel wiederholt sein Signal alle etwa 20ms mit einer
Länge von 1-2ms.
Derzeit habe ich den ADXL am INT0, und den Kreisel an ICP.
Da der ICP ja den Zeitpunkt der Flanke im zugehörigen Register festhält,
habe ich damit kein Problem.
Beim INT0 jedoch geht das nur, indem ich im zugehörigen Interrupt den
Timer auslese.
Funktioniert soweit ganz gut und genau.
Da ich nun aber noch andere Interupts verwenden möchte, befürchte ich,
dass meine Messung des ADXL ungenau wird, da ja evtl die zusätzlichen
Interrupts
den INT0 für unbestimmte Zeit verzögern können (Ist "Softwarejitter" die
richtige Bezeichnung dafür ?)
Hat jemand evtl eine Idee, wie ich die Pulbreite trotz anderer
Interrupts relativ (soll heissen: so genau wie möglich :-) genau messen
kann ?
Eine Messung der Analogausgänge des adxl scheidet für mich aus diversen
Gründen zunächst aus.
lg, Frank
hallo.
schau mal im datenblatt auf seite 15 bzw. such nach "nested interrupts".
du kannst dafür sorgen, daß kein anderer interrupt deinen INT0, aber der
INT0 jeden anderen interrupt unterbrechen kann.
da der INT0 außerdem die höchste priorität nach dem RESET hat, wirst du
auch kein problem kriegen, wenn zwei interrupts gleichzeitig anliegen.
gruß
michael
Hm, das wäre also ein "sei" so früh wie möglich in den zusätzlichen
Interrupts. Damit würden mir aber immer noch einige Takte verloren gehen
wenn ich das richtig verstanden habe, und ich würde einen kleinen
"Mess-Jitter" (sorry, ich kenne nicht den richtigen Begriff dafür)
haben. Der wäre nicht sonderlich hoch, wohl je nachdem wieviele "push"
mir der GCC an den Anfang der Interrupts einfügt, und evtl (das muss ich
erstmal testen) wäre das ausreichend.
Dennoch: Gibt es noch eine bessere Möglichkeit ?
lg, Frank
Man könnte es über Polling realisieren:
Einen Timer mit einer möglichst feinen Auflösung eine ISR aufrufen
lassen, in der die beiden Eingänge abgefragt werden. Da guckt man dann
nach, ob sich was geändert hat.
Hannes Luxx hat so einen Fernsteuerbaustein (oder sowas in der Richtung)
realisiert.
Polling.. hm. da habe ich aber doch das gleiche Poblem mit dem "Jitter".
Nämlich wenn der Timer-ISR durch einen anderen ISR verzögert wird.
Oder habe ich Dich falsch verstanden ?
Kann man vielleicht mit einigen Gattern die beiden PWM's verknüpfen so
dass man als Output für den ICP-Eingang einen kurzen Puls bekommt, wenn
sich einer der Level ändert ? Die wirklichen Zustand, ob 0 oder 1 kann
man ja dann "irgendwann später" ohne große Timinprobleme erfassen.
Mir ist nur grade ein Rätsel wie so eine Schaltung aussehen müsste :-)
Hab grade folgendes Entdeckt:
#include <avr/interrupt.h>
//...
void XXX_vect(void) __attribute__((interrupt));
void XXX_vect(void) {
//...
}
Damit scheint der erste Befehl einer ISR ein "sei" zu sein, damit wird
das ganze also nur um 1 Takt verzögert. Das reicht dicke.
@Michael: Danke für den Tip.
Frank
@ Frank (Gast)
>Damit scheint der erste Befehl einer ISR ein "sei" zu sein, damit wird>das ganze also nur um 1 Takt verzögert. Das reicht dicke.
Nicht ganz. Der Sprung in den Interrupt dauert auch 4 Takte, macht
zusammen 5.
MFG
Falk
Was müsste ich machen, um
vor dem "sei" ein Flag zu setzen ?
Dann könnte man in Abhängigkeit des Flags ggf. einige Takte zum
Messergebnis hinzuaddieren. Ich vermute, das geht dann nur noch mit
Inline-Assembler, oder gibt es einen Weg, das dem GCC beizubringen ?
Wie würde soewtas (Inline-assembler) aussehen ? An dieser Stelle
verlassen mich meine GCC-Kenntnisse...
lg, Frank
hallo frank.
wenn du genau weißt, wie viele takte du "verlierst", könntest du die
natürlich nachträglich beim ergebnis berücksichtigen.
kenne jetzt deine anwendung nicht im detail, aber ich meine, für
"einigermaßen genau" mußt du solche klimmzüge nicht machen.
gruß
michael
Hi Michael,
stimmt schon, aber wenn es mit wenig Aufwand besser geht, warum sollte
ich diese Möglichkeit nicht nutzen :-)
Jedenfalls wäre diese Lösung "perfekt", genauer geht es nicht mehr. Und
da die einzige Änderung halt ein bischen umschreiben der Software ist,
nehme ich "Klimmzüge" gern in Kauf, der Aufwand ist vermutlich
vernachlässigbar, und selbst wenn sich herausstellt, dass es unnötig
ist, habe ich zumindest so einiges dabei gelernt. Und das ist doch fast
noch wichtiger als das Endergebnis :-)
Vielleicht kommte ja dabei als Nebeneffekt ein nützliches Codeschnippsel
für die Codesammlung heraus, das wäre doch auch was.
lg, Frank
Hab mal geforscht, hier das Ergebnis für die, die es interessiert.
So müsste es gehen. Ist aber noch nicht getestet, das kann ich erst
daheim.
Mithilfe dieser Konstruktion erhält man dann für INT0 und INT1 die
gleiche Genauigkeit was timerstände angeht wie mit ICP. Man benötigt nur
einen Wrapper.
Wenn es getestet ist, schreibe ich das in die Codesammlung.
XXX durch den gewünschten ISR ersetzen:
Hallo Frank,
ich sehe das mit dem "sei" etwas problematisch. Damit schaltest Du ALLE
Interrupts frei, inkl. dem, der gerade aufgerufen wird. Es kann also
passieren, dass ein IR sich selbst unterbricht. Das kann von problemlos
bis zum Stack-Overflow alle möglichen Auswirkungen haben und ist sehr
schwer debugbar.
Ich würde eher ein Multiplexen der beiden PWM-Eingänge vorschlagen, so
wie Du es in Deinem zweiten Post auch schon gefragt hast:
>>...gibt es evtl. einen "Kniff", mit dem ich beide Signale auf den>>ICP-Pin legen kann ?
Der Input-Capture lässt sich sowohl vom ICP-Pin als auch vom
Analog-Comparator triggern. Ich würde beide Geräte an diese beiden
Inputs schalten und abwechselnd einlesen. Bei den von Dir angegebenen
Timings müsstest Du eigendlich beide Werte alle 20ms updaten können.
Viele Grüße, Stefan
@ Stefan Kleinwort (sk)
>ich sehe das mit dem "sei" etwas problematisch. Damit schaltest Du ALLE>Interrupts frei, inkl. dem, der gerade aufgerufen wird. Es kann also>passieren, dass ein IR sich selbst unterbricht.
Nöö, das passiert nur bei denen, die das jeweilige Interrupt Flag nicht
automatisch per Harware löschen. Z.B. der UART RXC.
MFG
Falk
Falk Brunner wrote:
> @ Stefan Kleinwort (sk)>>>ich sehe das mit dem "sei" etwas problematisch. Damit schaltest Du ALLE>>Interrupts frei, inkl. dem, der gerade aufgerufen wird. Es kann also>>passieren, dass ein IR sich selbst unterbricht.>> Nöö, das passiert nur bei denen, die das jeweilige Interrupt Flag nicht> automatisch per Harware löschen. Z.B. der UART RXC.
Schon klar.
Ich habe geschrieben: "es kann passieren". Z.B. bei solchen Sachen wie
dem UART.
Aber auch, wenn die IR-Routine zu lang ist bzw. die IR-Frequenz zu hoch.
Und am allerbesten: auf dem Schreibtisch passt es, im Einsatz ist aber
eine Kleinigkeit anders und der Stack läuft über.
Richtig sauber ist das meiner Meinung nach nur, wenn der eigene IR vor
dem "sei" disabled wird.
Viele Grüße, Stefan
Ich hatte noch einen Denkfehler :o)
Der Messwert muss natürlich nur korrigiert werden, wenn der INT0 vor
dem "sei" des anderen Interrupts ("XXX") auftritt.
Daher nun folgende, auf den ersten Blick etwas merkwürdig aussehende
Konstruktion (sbi sei cbi) :
Der Messwert muss um 4 (jmp)+ 2( sbi) + 1 (sei) = 7 Takte korrigiert
werden.
Das "reti" am Ende kann man auch durch ein "ret" ersetzen, das ist in
diesem speziellem Fall dasselbe.
Ok, ich habe es nun getestet.
Funktioniert prima.
Ich hatte vorher nicht bedacht, dass mein Input-Capture ja auch den
ISR für INT0 beeinflusst und mich über ziemliches Rauschen gewundert.
Ich kam trotz "8-point moving average filter" kaum auf 1 Grad nutzbare
Genauigkeit (der ADXL202 ist ein Beschleunigungssensor, ich benutze ihn
zur Bestimmung des Winkels zum Boden).
Jetzt habe ich den Input-Capture ISR und einen zusätzlichen Timer-ISR
wie oben implementiert, und trotz des zusätzlichen Timer-ISR flackern
nur noch die Nachkommastellen des Winkels ein bischen.
Es ist also mindestens um den Faktor 5 - wenn nicht mehr, genau habe ich
es noch nicht untersucht- besser geworden.
lg, Frank
Hallo!
Ich habe bei der Diskussion mitgelauscht. Bin Was AVR angeht eigentlich
schon ziemlich fit, vorallem in ASM. In C habe ich einige kleinere
Sachen gemacht und kenne mich auch aus, aber nicht sehr gut. Deshalb
frage ich mich ob jemand die oben geschriebenen Zeilen ein bisschen
kommentieren könnte. So einige Dinge fallen mir da auf, die ich noch nie
benutzt/gebraucht habe und die mir auf den ersten Blick auch nicht
gleich trivial vor kommen:
Was bedeutet "__attribute__"? Hab ich wahrscheinlich schon mal in einer
Bibliothek die ich eingebunden habe gesehen.
Die Dinger mit "naked" und so sind mir auch schleierhaft...
Über ein par kurze Kommentare zu den paar Zeilen wäre ich sehr dankbar.
Hi Ste,
ist schon spät, darum nur kurz:
Mit Assembler brauchst du solche Hilfskonstruktionen bzw. den Wrapper
nicht, da reicht ein kurzer Abschnitt "sbi, sei, cbi". Das ganze
drumherum dient nur dazu, dem GCC das verständlich zu machen.
Attribute "naked" bedeutet, dass der Compiler keinen Prolog/Epilog um
die Funktion erzeugt, in diesem Fall verhindet es, dass - obwohl es sich
beim Wrapper um eine Interrupt-Routine handelt (was durch die anderen
beiden Attribute gekennzeichnet wird) erstmal das sreg, r1 usw auf den
Stack gesichert wird.
Das setzen des Flags (sbi blahblah..) hat denn Sinn, dass man es im ISR
für INT0 abfragen und ggf. den Timerwert um 7 Takte "nach unten"
korrigieren kann.
Der Wrapper ruft dann eine "normale" ISR auf.
An dieser Stelle kommt übrigens eine "Warning" "missspelled interrupt
handler". Die kann man aber ignorieren, weil es in diesem Fall Absicht
(Unterstrich vor dem Namen) ist.
Wenn jemand einen Tip hat, wie ich die Warning ignorieren kann...