Hallo Ich bastel derzeit an meinem ersten Roboter, und abgesehen von den - in anderen Threads besprochenen - Problemen läuft es auch schon ganz gut. Bisher habe ich lediglich eine minimale Software zum Testen der einzelnen Komponenten auf dem Controller, möchte aber nun meine definitive Software entwickeln. Zum Roboter: Angetrieben wird der Roboter durch 2 Räder auf beiden Seiten, die je einzeln auf einem gehackten Servo montiert sind. Die Räder sind gelocht, mit einer Lichtschranke wird die zurückgelegte Strecke von jedem Rad erfasst. Gesteuert wird das ganze von einem Atmega 8, der zurzeit auf 4 MHz läuft, programmiert in Assembler. Als Stromversorgung dient ein 4.8 V Akku aus vier Zellen. Vielleicht kommt noch eine Kollisionserkennung, ein Display, ein Linienfolger oder eine kleine Tastatur hinzu. Nun überlege ich mir, ob die ganze Software in einer grossen Schleife ablaufen soll, d.h. es wird in jedem Schleifendurchlauf der Wert der Lichtschranken abgefragt und geschaut, ob die Steuersignale für die Servos auf high bzw. low gesetzt werden müssen (anhand eines Timers oder Berechnung der Programm-Laufzeit). Oder soll ich es mit Interrupts machen? Die Lichtschranken würden dann bei jedem Loch ein externes Interrupt auslösen. Die Servos würden über ein Timer-Interrupt gesteuert etc. Sehe ich es richtig, dass eine Kombination aus Schleife und Interrupts ein bisschen gefährlich ist? Gruss Michael
Prinzipiell kannst du das gesamte Programm mit Interrupts erlegen, wobei die länge der Interrupt Service Routinen nicht zu lang sein sollte. Servos kann man wunderbar per OutputCapture ansteuern. Die Drehzahl(Periodendauer)/Geschwindigkeit kann man per InputCapture messen. Die beiden Sachen und eine Tastatur-Entprellung kann man mit nur einem Timer erledigen, wobei der Timer bei jedem Überlauf ein Flag setzt, das in der Hauptschleife abgefragt wird. Jedesmal, wenn das Flag gesetzt ist, vergleicht man den aktuellen Zustand der Eingänge mit dem vorhherigen, und reagiert entsprechend (Entprellroutine von Peter Dnnegger in der Codesammlung). Gefährlich wird es nur, wenn du dir nicht vorher darüber klar bist, was so alles in deinem Programm passieren kann...
Ich strikturiere meine AVR-ASM-Programme meist so: - Reset: Initialisierung aller I/Os und genutzter HW-Features (wird nur 1 mal durchlaufen und "fällt" in die Hauptschleife) - Hauptschleife (Mainloop): Jobs erledigen, deren Steuerflags gesetzt waren, wenn alle Jobs abgearbeitet sind, dann in Sleep-Mode fallen (nach jedem Interrupt erfolgt ein neuer Durchlauf, bis alle Job-Flags abgearbeitet und gelöscht sind) - Timer-Interrupt (meist alle 1..20ms): Tastenentprellung, diverse Softwaretimer (Uhr, Blinker...) handeln, Job-Flags setzen, falls es etwas zu tun gibt, ISR sehr kurz halten (wird vom Timer in regelmäßigen Zeitabständen ausgelöst) - Weitere Timer-Interrupts (OC, ICP, OVF...) diverse zeitabhängige Jobs durch setzen der Jobflags "anmelden" (erledigt Mainloop) (wird von Timern ausgelöst) - ADC-Interrupt: Messwerte einlesen und sichern, nächste Messquelle einschalten, evtl. Jobflag setzen (wird vom ADC ausgelöst, wenn er fertig ist) - andere Interrupts (RX, Ext, Pinchange...): Daten sichern, falls erforderlich, Jobflag für Mainloop setzen, dadurch ISR sehr kurz halten (ausgelöst durch diverse Hardware) - Jobs: Routinen, die bestimmte Aufgaben erledigen, z.B.: - ein Zeichen aus dem Ringbuffer an UART senden - ein Zeichen aus dem Ringbuffer an das LCD ausgeben - eine Berechnung ausführen - ... (wird von Mainloop aufgerufen, falls Jobflag gesetzt war, löscht Jobflag und erledigt Job) Man richtet sich ein (oder mehrere) Register ein, in denen jedes Bit für einen Job steht (Jobflags). Erkennt irgendein Programmteil (meist ein Interrupt), dass ein bestimmter Job ausgeführt werden muss, dann wird nur das Jobflag gesetzt und evtl. die zur Ausführung erforderlichen Daten (Timerstand, ADC-Wert, Bitmuster am Port...) eingelesen und bereitgestellt. Die Mainloop ruft dann den betreffenden Job auf. ...
Hallo @Hannes: Diese Methode hört sich sehr interessant an! Allerdings ist im Vorfeld sicher eine präzise Planung des Programms notwendig. Dann dürfte es aber auch problemlos möglich sein, weiter Komponenten zu integrieren, was bei einer reinen mainloop und bei einer unsorgfältigen Interruptprogrammierung schwieriger sein dürfte. Gruss Michael
also zwei kommilitonen und ich haben einen ähnlichen robo mit einem hc08 gebaut (etwas langsamer bei 4 MHz). Wir haben da die Rad-Sensoren per input-capture gezählt (du könntest auch einen flanken-getriggerten interrupt nehmen) und dann noch einer gewissen zeit (durch einen timer-überlauf bestimmt) die werte verglichen mit einem soll-wert, dadurch konnten wir die geschwindigkeit regeln und das ding auch ordentlich gerade aus fahren lassen. Die motoren hingen über einem motor-treiber an je einem pwm-ausgang. Das ging eigentlich ganz gut (10cm abweichung auf 15m) besonders wenn man bedenkt, dass das linke getriebe schon so ausgeklappert war, dass der robo mit gleicher einstellung aber ohne regelung nicht am anderen zimmer-ende angekommen, sondern an die seitliche wand gefahren ist. den rest (tasten abfragen) haben wir dann in einer endlos-schleife gemacht. prinzipiell könnte man hier auch einen interrupt nehmen und die taster über dioden an einen interrupt anschließen und dann wenn es soweit ist, einfach den zustand abfragen
> Diese Methode hört sich sehr interessant an! Allerdings ist im > Vorfeld sicher eine präzise Planung des Programms notwendig. Eben nicht... Einen Zeittakt (Pulsschlag) braucht man eigentlich in fast jedem Programm. Also wird erstmal (neben Reset-Sequenz und leerer Mainloop mit Sleep) ein Timer-Interrupt programmiert. Dessen Intervall hängt davon ab, was er steuern soll. Für eine Uhr bietet sich 10ms an, damit kann man auch gut Taster entprellen. Ist LCD-Ausgabe dabei, nehme ich gern 1ms, setze bei jedem Aufruf das Jobflag für die Zeichenausgabe, erhöhe bei jedem 10ten Aufruf (Software-Vorteiler) die Uhr (Hundertstelsekunde) und entprelle Taster. Willst du deine Räder (Lichtschranken) abfragen, dann ermittle erstmal die erwartete Frequenz. Es ist durchaus möglich, das mit Polling zu erledigen. Ich lese z.B. mit Polling (100kHz) mehrere Kanalimpulse von RC-Fernsteuerungen ein. Aber zurück zum Programm-Grundgerüst... Reset-Sequenz, leere Mainloop mit Sleep, Timer-Interrupt mit fester (geeigneter) Frequenz... Ein oberes Register für Jobflags reservieren (heißt bei mir oft "flags", könnte aber auch "jobs" oder "tasks" heißen). Dann für jede Aufgabe, die der MC erledigen soll, analysieren, wodurch der "Auftrag" ausgelöst werden soll (Interrupt, Timeout...) und Routine schreiben, was gemacht werden muss. Und natürlich ein zugehöriges Jobflag einrichten (mit Abfrage und bedingtem Sprung in Mainloop). Und dabei immer darauf achten, dass nirgends Warteschleifen erforderlich werden. Das Programm so strukturieren, dass sofort zur Mainloop gesprungen wird, wenn ein Job jetzt nicht erledigt werden kann, denn es gibt sicherlich genug Anderes zu tun. Und so kann man eine Aufgabe nach der anderen hinzufügen... > Dann dürfte > es aber auch problemlos möglich sein, weiter Komponenten zu > integrieren, Das ist ja auch Zweck der Übung. Und da der Timer den Takt angibt, wird die meißte Zeit in der Mainloop gepennt. Kommt etwas Arbeit dazu (weitere Jobs), so bleibt das Timing der bereits funktionierenden Programmteile erhalten, es reduziert sich lediglich die Sleep-Zeit. In den ISRs ist natürlich das SREG zu sichern und es sind möglichst einige Register nur für ISRs zu reservieren, das macht die ISRs etwas schneller, so dass sie sich nicht gegenseitig behindern. ...
Hallo Ähm...der Begriff ISR sagt mir jetzt nichts. Routinen, welche die Interrupts behandeln? ;-) Noch zu den Rädern: Es hat 8 Löcher in jedem Rad für die Lichtschranke, nur damit man sich ca vorstellen kann, wie gross der Aufwand ist. Die Räder haben einen Radius von 3 cm. Gruss Michael
_I_nterrupt _S_ervice _R_outine, also der Programmteil, den der Controller bei Auftreten eines Interrupts anspringt, sofern dieser Interrupt freiggeben ist.
> Es hat 8 Löcher in jedem Rad ...
Ein geknacktes Servo schafft kaum mehr als 1..2 Umdrehungen pro
Sekunde, das macht maximal 16Hz. Somit kann man die Lichtschranken ohne
Weiteres zusammen mit eventuellen Bedienungstasten und Endschaltern auf
einen Port legen und mittels Entprellung nach Peter Dannegger im
10ms-Takt (100Hz) einlesen. Die Auswertung (Zählung der Wegstrecke...)
kann dann anhand der Tastenflags (tfl bzw. key_press) als Job der
Mainloop erfolgen (auch Tastenflags der Entprellung können als Jobflags
genutzt werden).
Der Haupttimer müsste also in deinem Fall mit 100Hz (10ms, 10000 Takte
bei 1MHz Prozessortakt) laufen, da hast du zwischen den Interrupts alle
Zeit der Welt...
Gleichzeitig sparst du dir durch die Entprellung eine
Hardware-Entprellung. Die Routine entprellt 8 Eingänge gleichzeitig.
...
...
Hallo Ich lasse also den Haupttimer (Welchen am optimalsten?) alle 10ms ein Interrupt (Da habe ich auch wieder grössere Auswahl - welches würdet ihr nehmen?) auslösen, dieses setzt ein Job-Flag für die Tastenauswertung. Wenn dieses Flag gesetzt ist, wird im nächsten Mainloop-Durchlauf der Tasten-Port eingelesen und je nach deren Zustand weitere Jobflags gesetzt? Alle 20 ms löse ich dann noch ein Flag aus, dass die Steuerleitung der Servos auf high setzt. Dieses muss ich 1-2 ms später wieder runter nehmen. Da sehe ich jetzt einen Knackpunkt. Ich könnte natürlich mittels Schleife ein Delay erzeugen, aber dann hängt der Controller 2 ms. Ich könnte aber auch einen zweiten Timer mit seinem Interrupt benützen. Welchen Timer und welchen dazugehörigen Interrupt würdet ihr dazu verwenden? Gruss Michael PS: Wegen der Frage, welche Timer und Interrupts ich verwenden soll: Ich bin nicht etwa zu faul, ins Datenblatt zu schauen, aber mir fehlt jede Erfahrung, was man mit welchem Timer machen könnte, das ich später evtl. noch brauche und daher lieber einen anderen 'verbrate'...
Die Tastenentprellung ist so kurz und schnell, die kann in der ISR ausgeführt werden. Welchen Timer du nimmst, hängt davon ab, welche anderen Timer-Features (PWM etc.) du benötigst. Nimm den, auf den du am leichtesten verzichten kannst. Schau dir mal das hier an, das erzeugt auch Servoimpulse, ist aber kein Roboter: http://www.hanneslux.de/avr/mobau/7ksend/7ksend02.html Es wird dir aber vermutlich helfen, das von mir vorgeschlagene Konzept zu verstehen. Die dort enthaltene Tastenentprellung ist aber nicht komplett, eine komplette findest du hier: http://www.hanneslux.de/avr/zuenduhr/index.html speziell im Quelltext der Zünduhr. ...
Hallo nochmals Zwei Fragen kommen mir gerade noch in den Sinn: 1. Welche Register würdet ihr für die Flags nehmen? Solche aus R0-R15 oder eher diejenigen ab R16? 2. Wie kann ich am schnellsten ein Flag auslesen und entsprechend Verzweigen? Also mir kommt da nur gerade ein ANDI 0x00000001 und dann ein BRNE in Frage. Dann verliere ich aber den Inhalt meines Registers bzw. muss es vorher sichern. Nicht gerade effizient. Oder ich könnte die Bits jeweils nach rechts schieben und dann das Carry-Flag abfragen. Gruss Michael
Hi SBRC oder SBRS und danch ein (r)jmp oder (r)call lässt das Register unbeschädigt. MfG HG
Für Einzelbitoperationen besser die unteren Register nehmen. Nicht, weil sie dafür besser geeignet sind, sondern weil die erweiterten Funktionen der oberen Register dafür nicht gebraucht werden. Entweder arbeitest du mit dem T-flag bst Rr, b //lädt Flag nach T und kann dann weiterverzweigt werden oder direkt mit sbrc/sbrs Rr, b
1. Ein oberes Register (r16..) wegen Zugriff mit Konstanten. 2. Schau dir die Mainloop meiner Programme (obige Links) an. Es gibt den Befehl sbrc/sbrs, der fragt exakt ein Bit ab und überspringt den nächsten Befehl (oder eben nicht). Beispiel: mainloop: sbrc flags,flag01 ;überspringe, wenn Bit "flag01" 0 ist rjmp job01 ;springe zur Jobroutine... sbrc flags,flag02 ;überspringe, wenn Bit "flag02" 0 ist rjmp job02 ;springe zur Jobroutine... sleep ;geh pennen rjmp mainloop ;nochmal... flag01: ;erste Jobroutine cbr flags,1<<flag01 ;Jobflag löschen ;... ;mache deine Arbeit rjmp mainloop ;fertig... Das geht auch mit rcall/ret, rjmp hat aber den Vorteil, dass die Mainloop solange abgearbeitet wird, bis alle Jobs erledigt sind. Mit der Reihenfolge in der Mainloop kann man Prioritäten setzen, die oberen Einträge werden bevorzugt behandelt, die unteren erst, wenn alle darüberliegenden fertig sind. Manchmal ist es auch sinnvoll, einen oder mehrere Jobs mit rcall/ret anzuspringen/zurückzuspringen, nämlich dann, wenn die darunterliegenden Jobs auch eine Chance bekommen sollen. Es kommt dabei immer auf die Situation an. ...
> Für Einzelbitoperationen besser die unteren Register nehmen.
Hmmm...
SBR/CBR geht aber nur mit oberen Registern... - Oder habe ich jetzt was
verpasst?
Bit- & Bytebruch...
...HanneS...
sbr/cbr geht nur mit den oberen Registern, da hast du recht. Und wenn du mal den OP-code von sbr7ori bzw cbr/andi vergleichst, fällt dir vielleicht was auf :-). Die Rede war von sbrc/sbrs!
> OP-code von sbr7ori bzw cbr/andi
Ja, das ist mir klar, Gleiches gilt auch für die Befehle zur
SREG-Manipulation. Man kann einunddasselbe Ding unterschiedlich nennen.
;-)
Die Bits im Register "flags" müssen ja nicht nur ausgewertet werden,
sondern auch gesetzt und gelöscht. Das Setzen geschieht meinst in einer
ISR, da muss es schnell gehen, deshalb bevorzuge ich obere Register für
diesen Zweck. Ansonsten gehe ich mit den oberen Registern auch recht
geizig um.
Gruß...
...HanneS...
Nu sei mal nicht zu luxig, äh geizig. Das kostet dich einen einzigen Takt, unter der Voraussetzung, dass das sreg sowieso gesichert ist: set //T=1 bld bit_adresse wenns auf den einen Takt ankommt, hast du den falschen MC gewählt :)
Gut, danke für den Rat. Beim T-Bit bin ich nämlich noch nicht richtig angekommen, nutze ich bisher sogut wie garnicht, gerade mal in Peters Entprellroutine für eine Taste... SREG sichere ich inzwischen grundsätzlich, wenn es erforderlich ist. Der AVR ist schon der richtige Controller für mich, meine Projekte sind recht klein und überschaubar. Und es ist nicht kommerziell, sondern nur zum Hobby. Gruß... ...HanneS...
"SREG sichere ich inzwischen grundsätzlich, wenn es erforderlich ist." Der ist gut! Was bedeutet das eigentlich?? :-)
Das bedeutet, dass ich darauf verzichte, wenn die gesamte Arbeit in den ISRs gemacht wird (manchmal erlaubt das Timing das) und in der Mainloop nur gepennt wird. Dann sind der Mainloop die SREG-Flags egal, dann sichere ich sie auch nicht. Meist kommentiere ich dann aber die Befehle nur aus. Beispiel: Software-PWM mit mehreren Kanälen, da geschieht alles (PWM generieren, ADC auslesen) im Timer-Int. SREG-Sicherung hatte ich ganz am Anfang "grundsätzlich" vergessen und erst durch dieses Forum bemerkt. Deshalb das "inzwischen grundsätzlich". 8-) ...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.