Hallo! Ich bastele an einem kleinem Fahzeug/Roboter. Soll erst einmal fahren und Hindernissen ausweichen. Beides geht in die tichtige Richtung, aaaber: Ich verwende zur Steuerung einen ATMEGA8, Schrittmotoren und GP2D12-Infrarotsensoren. Die brauchen ein paar ms um zu messen. In dieser Zeit stoppen die Motoren natürlich bei sequentiellem Programmablauf, da die Steuersignale nicht generiert werden. Wie löst man solche Probleme üblicherweise? Kann man beim ATMEGA8 eine Art Nebenläufigkeit erreichen?
externen schrittmotortreiber oder dc-motoren verwenden
Ich lasse bei meinem Roboter den ADC für die Infrarotsensoren im Interrupt-Modus laufen. Dabei wird nach jeder abgeschlossenen Wandlung die ISR aufgerufen. Dort wird dann das Ergebnis in einer globalen Variable (aufpassen wegen atomarem Zugriff!) abgelegt und auf den nächsten Kanal gewechselt. Im Hauptprogramm kann ich mir dann jederzeit den neusten Wert aus der globalen Variable holen.
Uwe ... schrieb: > Ich lasse bei meinem Roboter den ADC für die Infrarotsensoren im > Interrupt-Modus laufen. Dabei wird nach jeder abgeschlossenen Wandlung > die ISR aufgerufen. Dort wird dann das Ergebnis in einer globalen > Variable (aufpassen wegen atomarem Zugriff!) abgelegt und auf den > nächsten Kanal gewechselt. Im Hauptprogramm kann ich mir dann jederzeit > den neusten Wert aus der globalen Variable holen. ... und das wäre ja dann so etwas wie Nebenläufigkeit, oder? Cool, das klingt gut. Ich werde mich da einlesen. Danke auch für den Code!
Ich löse Nebenläufigkeiten in Mikrocontrollern, indem ich alle Aufgaben in Zustandsautomaten programmiere, die strikt Message basierend arbeiten. dadurch kriegt man Nebenläufigkeiten sehr gut in den Griff. Siehe: http://consulting.astade.de/workshop.htm
Die Motorensteuerung kannste auch in einem Timerinterrupt laufen lassen, dann muss sich das Hauptprogramm darum nicht kümmern.
Also ich versuche gerade die Lösung mit dem ADC Interrupt, bin aber irgendwie nicht in der Lage dazu, trotz des guten Beispielcodes von oben. Um zu Überprüfen, ob der Interrupt ausgelöst wird, wollte ich eine LED blinken lassen. Offenbar wird der Interrupt nicht ausgelöst. Wenn ich das ADIE-Bit lösche, kann ich Messungen vornehmen. Seht ihr einen Fehler?
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | #include <util/delay.h> |
4 | #include <stdint.h> |
5 | #include "motor.h" |
6 | #define AUSWEICHENTFERNUNG 70
|
7 | #define MESSUNGEN_ENTFERNUNG 3
|
8 | uint8_t entf=0; |
9 | |
10 | ISR(ADC_vect) |
11 | {
|
12 | PORTC^=32; |
13 | _delay_ms(10); |
14 | ADCSRA |= (1<<ADSC); |
15 | }
|
16 | |
17 | |
18 | void init() |
19 | {
|
20 | DDRD = 0xFF; //Port D ist Ausgang |
21 | DDRB = 0xFF; |
22 | DDRC = (1 << DDC5); //Port C ist Eingang, bis auf Pin 5 |
23 | ADMUX|=((1<<REFS0) | (0<<REFS1) | (1<<MUX0) | (0<<MUX1) | (0<<MUX2) | (0<<MUX3) | (1<<ADLAR)); |
24 | ADCSRA|=((1<<ADEN) | (1<<ADSC) | (1<<ADIE) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)); |
25 | }
|
26 | |
27 | |
28 | |
29 | |
30 | int main(void) |
31 | {
|
32 | sei(); |
33 | init(); |
34 | |
35 | while(1) |
36 | {
|
37 | |
38 | }
|
39 | return 0; |
40 | }
|
Na gut ich nehm alles zurück. Es geht. _delay_ms(10) war wohl ein bisschen wenig um das zu sehen. Motoren laufen aber immer noch nicht ganz rund, wenn auch besser als vorher.
Wenn du Nebenläufigkeit erreichen willst, darfst du kein _delay_ms() verwenden! Es blockiert dir für die Verzögerungszeit den gesamten µC. Wenn Wartezeiten von mehr als einigen µS nötig sind, dann sollten sie über einen Timer realisiert werden. Und der Tipp von Thomas ist auch gut. Ich löse selbst viel mit FSMs (also Zustandsautomaten). Damit kann man komplett auf Pollingschleifen verzichten, die dir ebenfalls die Nebenläufigkeit vermiesen würden. Das benutze ich unter anderem für meine Ultraschallsensoren, bei denen es ja bis zu 60ms dauert, bis das Ergebnis da ist. Statt hier einfach solange zu warten, bis der Wert da ist, geht die FSM in einem Wartezustand. Bei jedem Durchlauf der Hauptschleife wird geprüft, ob sich was getan hat. Wenn ja, wird in den Zustand gewechselt, der den Wert einliest. Ansonsten wird mit der nächsten Aufgabe (z.B. der Bearbeitung einer anderen FSM) weitergemacht. Somit wird nie auf irgendein Ereignis gewartet, und der µC ist nur mit "sinnvollen" Dingen beschäftigt.
@Erdnuckel Für Dein Problem gibt es eigentlich nur 3 mögliche Lösungen: 1. Intelligenter Einsatz von Interrupt Routinen. 2. Ein Message Passing framework und alles in Zustandsmaschinen. 3. Ein Betriebssystem mit Multiprozessing und/oder Multithreading. Da Punkt 3 wegen der Größe des Prozessors wohl ausscheidet, musst Du Dich zwischen 1 und 2 entscheiden. Von 1 rate ich Dir ab, weil: a) Interrupt Programmierung andere Schwierigkeiten aufwirft (Race Condition) b) Interrupt Programmierung fast "undebugbar" ist c) Du innerhalb der Interrupt Routinen über kurz oder lang auch "Zustände" brauchen wirst, die Du dann in irgendwelchen globalen Variablen "versteckst" bis Du den Überblick völlig verloren hast. Ein "Profi" kriegt das auch mit Interrupts hin. Aber wenn Du mit sowas noch keine Erfahrung hast, dann stehen Dir viele frustrierende Wochen bevor. Für ein Projekt, wie Du es vorhast, kann ich Dir das Framework von Astade nur wärmstens empfehlen. Gerade weil Du mit Deinen Programierkünsten noch so am Anfang stehst und eine "Anleitung" zu gutem Code bräuchtest. Was Du gewinnst: a) Keine Interrupts und damit keine Race Conditions b) Das Trace Framework lässt Dich immer sehen, was Dein Code macht, Du brauchst nicht mehr mit LEDs zu blinken. c) Das Framework gibt Dir eine Struktur für Dein Programm vor. Das hilft Dir am Anfang Ordnung zu halten. Allerdings kostet es Dich auch was: a) Du musst dich in das Tool "Astade" einarbeiten Je nachdem, wie schnell Du bist, wird Dich das 3-5 Tage kosten (Oder Du nimmst an dem angebotenen Workshop teil). Am Ende hast Du aber ein mächtiges Werkzeug in der Hand. Ein Beispiel: Auf meinem AtMega128 läuft ein Webserver der gleichzeitig 4 Verbindungen behandeln kann. Jede Verbindung kann viele verschiedene Ereignisse haben (Verbindung aufbauen, anfrage, Antwort ..) und die ganzen Fehlerbehandlungen und Timeouts die das braucht. Die Software hat keine Interrupts und keine delay Anweisung. Und das beste: Sollte ich jetzt noch gleichzeitig einen Schrittmotor steuern müssen, dann mache ich einfach noch eine Maschine dazu ;-)
@Thomas: Also erst mal danke für die Analyse! Es geht bei mir um ein Hobby-Freizeitprojekt, also ist mein momentanes Ziel nicht mit Kanonen auf Spatzen zu schießen. Dein Hinweis mit FSMs ist sehr interessant, vorläufig werde ich aber versuchen mit Interrupts auszukommen, ggf weiß ich ja dann, wie ich weitermachen kann. Thomas schrieb: > Gerade weil Du mit Deinen > Programierkünsten noch so am Anfang stehst und eine "Anleitung" zu gutem > Code bräuchtest. Sehr interessant. Will ich gar nicht bestreiten. Jetzt interessiert mich aber, was genau du meinst? Beziehst du dich auf meinen Codeschnipsel? Ich wäre gespannt auf ein paar Beispiele. Thomas schrieb: > Allerdings kostet es Dich auch was: > a) Du musst dich in das Tool "Astade" einarbeiten > > Je nachdem, wie schnell Du bist, wird Dich das 3-5 Tage kosten (Oder Du > nimmst an dem angebotenen Workshop teil). Am Ende hast Du aber ein > mächtiges Werkzeug in der Hand. Nimms mir nicht übel, aber kann es sein, dass du hier ein bisschen Werbung für deinen Workshop machen möchtest?
Erdnuckel schrieb: > Nimms mir nicht übel, aber kann es sein, dass du hier ein bisschen > Werbung für deinen Workshop machen möchtest? kommt mir auch so vor. Erdnuckel Das brauchst Du aber auch nicht.
Erdnuckel schrieb: > Also erst mal danke für die Analyse! Es geht bei mir um ein > Hobby-Freizeitprojekt, also ist mein momentanes Ziel nicht mit Kanonen > auf Spatzen zu schießen. Dein Hinweis mit FSMs ist sehr interessant, > vorläufig werde ich aber versuchen mit Interrupts auszukommen, ggf weiß > ich ja dann, wie ich weitermachen kann. Wie Du meinst. Erdnuckel schrieb: > Sehr interessant. Will ich gar nicht bestreiten. Jetzt interessiert mich > aber, was genau du meinst? Beziehst du dich auf meinen Codeschnipsel? > Ich wäre gespannt auf ein paar Beispiele. Bitte, ich wollte Dir nicht zu nahe treten. Nein es bezieht sich nicht auf Deine Codeschnipsel. Aus Deiner Herangehensweise ans Projekt habe ich das geschlossen. Nicht respektlos gemeint. Ich dachte einfach, vermuten zu können, dass programmieren eher ein neues Feld für Dich ist und Du sagst ja selbst, dass ich richtig geraten habe. Erdnuckel schrieb: > Nimms mir nicht übel, aber kann es sein, dass du hier ein bisschen > Werbung für deinen Workshop machen möchtest? Nicht für den Workshop, aber für das Tool möchte ich werben. Ich benutze es selbst, halte es für sehr gut und bin stolz darauf. Warum sollte ich es dann nicht empfehlen, das ist schließlich der Sinn eines Forums, das man Erfahrungen austauscht. Das Tool ist Open Source, also kostenlos. Du siehst, ich will mich nicht an Dir bereichern. Vielleicht hätte ich mir die Bemerkung zum Workshop verkneifen sollen. Die dazu gehörende Website ist aber im Augenblick die einzige die ich habe, wo speziell auf Mikrocontroller eingegangen wird.
Hallo Erdnuckel, also ich habe ein sehr ähnliches Projekt, dabei hat der Roboter zwei Schrittmotoren, zwei Sharp-Sensoren und noch ein Funkmodul. Das Funkmodul überträgt die Roboterdaten nach Anfrage vom PC ca. alle 100 ms. Am Anfang hatte ich auch das Problem, dass bei der Übertragung der Daten die Schrittmotoren ruckelten, da das Empfangen in einem Interrupt war und natürlich die Einstellung der Schrittmotorgeschwindigkeit in den Timerinterrupts waren. Lösung: Der Interrupt von dem Funkmodul blieb, aber die Schrittmotoren liefen mit den Timer im CTC Mode ohne Interrupt. Sollte die Geschwindigkeit geändert werden, so mußte ich lediglich die Register OCR0 und OCR2 ändern, beides sind 8Bit-Counter (Umsetzung in einem ATMega 32): // Konfiguration vom PWM 0 DDRB |= (1<<PB3); // CLOCK DDRC |= (1<<PC0); // DIR DDRC |= (1<<PC1); // ENABLE TCCR0 |= (1<<WGM01) | (1<<COM00); // CTC PWM TCCR0 |= (1<<CS00) | (1<<CS02); // Prescaler 1024 TCNT0 = 0; OCR0 = 20; // Konfiguration für PWM 2 DDRD |= (1<<PD7); // CLOCK DDRD |= (1<<PD6); // DIR DDRB |= (1<<PD5); // ENABLE TCCR2 |= (1<<WGM21) | (1<<COM20); // CTC PWM TCCR2 |= (1<<CS20) | (1<<CS21) | (1<<CS22); // Prescaler 1024 TCNT2 = 0; OCR2 = 20; Das Auslesen der Sharp-Sensoren ließ ich einfach in der Main-Schleife, genauso wie der Kommandointerpreter für das Funkmodul. Die Motoren liefen sauber und die Kommunikation war schnell. Auch das automatische Verfahren im Raum war bei mir nur ein asynchroner Regelalgorithmus in der Main-Schleife gewesen, dies war absolut ausreichend schnell, ich hatte sogar noch ein Delay eingefügt. Für präzise Anwendungen muss hier sicherlich ein Timerinterrupt verwendet werden. Gruß Marvol
Danke nochmal für eure Hilfen und Beispiele! Im Moment klappt so alles. Ich denke ich bin jetzt auf einem guten Weg.
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.