PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Viele Aufgaben gleichzeitig machen



Christoph2
21.03.2009, 23:21
Hallo!
Ich habe einen roboter mit einem sharp entfernungssensor, der von einem Schrittmotor gedreht wird.
Alle 50 Microsteps wird ein Entfernungswert erfasst, d.h. 32 Entfernungswerte auf 180°.
Außerdem ist der Controller noch für das Fahren, Spannungen, Lichtstärke, ... und tausend andere Sachen zuständig.

Ich bin Anfänger, darum weiß ich nicht genau, wie ich das programmieren soll, dass er alles gleichzeitig machen kann.
Also jetzt habe ich die enfernungssensor-dreh-steuerung so gemacht, das er einfach in einer endlosschleife folgende schritte macht:
- Schrittmotor-Controller über i2c den Befehl geben, dass er 50 schritte drehen soll
- Die Aktuelle Schrittmotor position abfragen, und warten bis der Schrittmotor seine Position erreicht hat
- Mit der AD-Wandlung beginnen
- Warten bis die AD-Wandlung fertig ist
...und alles wieder von vorne.

Das war mal das Testprogramm für den Drehturm, bei diesem Test wurden alle anderen Funktionen vernachlässigt. Jetzt muss ich aber diese anderen funktionen wieder dazugeben, aber ich weiß nicht wie. Diese ganzen warteschleifen zerstören mir mein ganzes Programm.

Ich glaube die lösung sind Interrupts, nur ich weiß nicht wie ich das mit denen machen soll. Ich glaube, das warten auf den AD wandler kann ich leicht mit einem interrupt umgehen, weil ich ja einstellen kann, dass der AD-Wandler einen Interrupt auslöst, wenn er fertig ist. Dann müsste ich nicht auf das AD-Wandler-fertig-Flag pollen.

Wenn ich 100ms warten will, aber nebenbei auch was machen will, wie kann ich das machen? Kann ich einen Timer machen mit einem Interrupt auf diesen Timer, dann weiß ich in welchen Abständen der Interrupt ausgelöst wird. Dann zähle ich in der Interruptroutine eine globale variable hoch, und dies frage ich dann in meinem programm irgendwie ab und kann so mit einer if abfrage warten, bis irgendein wert erreicht ist, und dann irgendeine aktion machen?

Geht das oder hab ich überhaupt nichts kapiert?
Meine Idee klingt jedenfalls ziehmlich unprofessionell.

lg Christoph

Hubert.G
22.03.2009, 09:14
Nein, deine Idee ist absolut nicht unprofessionell. Das arbeiten mit "wait" wäre es, das hast du aber schon selbst festgestellt.

oberallgeier
22.03.2009, 10:06
Hi,

der Sharp gibt das Ergebnis eines Messzyklus alle 40 ms heraus. Da Du vermutlich nicht weißt, wann ein Messzyklus des Sharp beginnt, solltest Du für genaue Messwerte 80 ms auf einer neuen Position warten und erst dann den ADC starten. Andernfalls könntest Du den letzten aktuellen Wert des Sharp mit dem ADC einlesen.

Warum 80 ms? Nehmen wir im schlechtesten Fall an, Du erreichst die neue Sensorposition gerade zu Beginn eines angelaufenen Messzyklus - dann wird das Ergebnis dieses Zyklus nicht korrekt sein => also warte knapp 40 ms bis der nächste Zyklus startet. Nun beginnt der Sharp die neue, korrekte Messung - die ist nach weiteren 40 ms fertig. Jetzt bekommt der ADC korrektes Futter . . . .

Wenns bei mir schnell gehen soll, lasse ich in solchen Fällen den ADC frei laufen, lese den ständig ein und freu mich halt "irgendwann" über einen neuen Messwert. Dieses Verfahren ist aus der Schublade quick&dirty, klar.

Christoph2
22.03.2009, 11:17
Danke für die Antworten, ich hab das oben beschriebene mal als "programm" geschrieben:


volatile uint8_t zaehler;

(Interrupt routine)
zaehler=zaehler+1; //prescaler so einstellen, dass alle 1ms zaehler erhöht wird

(main)
...
...
if(ad-wandlung-fertig-flag==1)
{ ad-wert speichern;
setpos(schritte); //Befehl geben, dass er 50 schritte drehen soll
}
else
{ if(ap==tp) //wenn aktuelle position==target pos dann weitermachen
{ if(zähler ist 80 mal erhöht worden) //ka wie ich das mach
{ ad-wandlung starten;
}
}
}
...
...

Das mit dem ADC immer aktivieren hab ich jetzt nicht gemacht, weil ich sonst nicht weiß wann er fertig ist mit der wandlung, weil wenn ich ihn immer eingeschaltet lasse setzt er das flag ja nicht wenn er fertig ist glaub ich.

Kann mein "programm" so funktionieren?
Wie mache ich die Abfrage, ob zaehler 80 mal erhöht worden ist?

lg christoph

Hubert.G
22.03.2009, 11:34
ISR(ADC_vect){ /* Interrupt auslösen wenn ADC-conversion komplett*/
Messwert=ADC; /*Register auslesen, */
flag.ADC_Mess=1; /* Flag messen fertig */

}
ISR(TIMER1_COMPA_vect){ /*Takt 1 mSek*/
Zähler ++;

}

if(flag.ADC_Mess){
ADC_Messwert abarbeiten
}

if(Zähler >=80){
Mach was
}
So könnte das etwa aussehen.

Besserwessi
22.03.2009, 11:43
Einen Zähler bis 80 kann manoft besser rückwärts machen, also von 80 runter bis 0. Der test ist einfach den Wert des Zählers anschauen. Oft ist das auch dann sinnvoll wenn man ohnehin einen andernen Wert jedesmal erhöht.

Felix G
22.03.2009, 12:06
Vielleicht noch zwei Hinweise um die Zuverlässigkeit und die Lesbarkeit des Codes zu verbessern:

1. Falls man längere Zeiten messen möchte, muss man logischerweise eine größere Timer-Variable nehmen, z.B. 16 oder sogar 32 Bit. Hier können aber Probleme auftauchen, da der AVR mehr als einen Befehl braucht um eine solche Variable zu lesen. In diesem Fall würde ich dringend eine Zugriffsfunktion wie GetTime() oder sowas empfehlen, die den Lesezugriff atomar durchführt (Interrupts aus, Variable lesen, Interrupts wieder an), denn sonst wirst du sicher früher oder später mal über seltsame, sporadisch auftretende Bugs stolpern.

2. Mach dir doch noch eine Funktion wie TimedOut(Start, End, Timeout) die dir überprüft, ob die gewünschte Zeitspanne bereits abgelaufen ist oder nicht. Das macht den Code noch etwas leichter lesbar, und außerdem muss man so wenger Tippen ;). Die kann dann auch gleich einen evtl. aufgetretenen Überlauf (Start > End) berücksichtigen (das ist sinnvoll um durch einen Überlauf ausgelöste Bugs zu vermeiden).

Christoph2
22.03.2009, 13:42
Ich will mir eh noch ein paar funktionen machen, aber ich verstehe die Abfrage noch nicht so richtig.

Wenn Zähler jetzt eine 8 bit Variable ist, zählt er von 0 bis 255 in 1ms Schritten.

Wenn der Zähler gerade bei 40 ist, und dann kommt diese If Abfrage


if(Zähler >=80)
{ Mach was
}

Dann wartet er bis der Zähler bei 80 ist, dann wird 255-80=175ms das "mach was" ausgeführt, und dann wieder 80 ms Pause gemacht, usw...

Wenn ich aber 80ms warten will, dann irgendwas abarbeiten will, was ca. 0,5ms dauert, und dann wieder 80ms warten will, wie soll das gehen?
Ich weiß ja nicht wie lange das abarbeiten dauert, 0,5ms sind nur geschätzt.

lg Christoph

albert32
22.03.2009, 14:57
Hallo Christoph2, ich habe hier interessantes entdeckt ...

http://www.mikrocontroller.net/topic/132436#new

Bindet Assemblerroutinen ein!

Solche Multitaskingprogrammierung, wird unter anderem sehr gut beschrieben von Manfred Schwabel - Schmidt, im Buch „Systemprogrammierung für AVR - Mikrocontroller".

lg

albert

Hubert.G
22.03.2009, 15:11
Christoph2
Die "if" Abfrage wartet nicht, wenn die Abfrage nicht wahr ist, wird nach der geschwungenen Klammer weitergemacht.

Christoph2
22.03.2009, 16:20
ja ich weiß, sorry war schlecht formuliert.
Ich wollte sagen:

Wenn ich aber 80ms nicht das machen will, was in den Klammern steht, dann das in den klammern abarbeiten, was ca. 0,5ms dauert, und dann wieder 80ms lang die Klammer überspringen will.
Ich weiß ja nicht wie lange das abarbeiten dauert, 0,5ms sind nur geschätzt.

lg Christoph

Hubert.G
22.03.2009, 16:52
Das Problem das du jetzt hast, ist mir nicht klar. Riesenlange Funktionen stören natürlich.
In 10msec machst du bei 8MHz Takt immer noch 80000 Programmschritte. Da kannst du eine Menge abarbeiten.

Christoph2
22.03.2009, 17:46
immer wenn ich jetzt "warte" schreibe, meine ich nicht nix tun wie in einer _delay_ms funktion, sonder was anderes tun, zB fahren, andere entfernungswerte abarbeiten, geschwindigkeiten berechnen, usw...

- Nachdem ich den Befehl zum drehen gegeben habe, warte ich bis die neue position erreicht ist
- dann warte ich 80ms
- dann lese ich den AD-wert ein und berechne die entfernung (dauert angenommen 1ms)
- dann arbeite ich das restliche programm ab (i2c ausgaben, eingaben, fahren, andere entfernungswerte abarbeiten, geschwindigkeiten berechnen, usw...)
Das dauert vll auch ein paar ms.
Dann ist der zaehler sagen wir bei 85.

- es wird wieder der Befehl zum drehen gegeben
- es wird gewartet, bis die position erreicht ist (ca. 100ms)
Der zaehler ist beim wert 185.

- jetzt sollte er 80ms warten, das tut er aber nicht, weil die Abfrage lautet:

if(Zähler >=80)
{ lese AD-wert ein und berechne entfernung
}

...und zähler ist bei 185, also >=80
er liest also gleich den neuen AD-wert ein, obwohl er 80 ms warten sollte.

lg Christoph

Hubert.G
22.03.2009, 18:52
Naja, so geht es natürlich nicht,
if(Zähler >=80)
{ lese AD-wert ein und berechne entfernung
Zähler = 0;
}
Dieses Zähler=0; gehört natürlich auch da hinein, war für mich aber selbstverständlich.
Genau so wie du ein Flag zurücksetzen musst, wenn es abgerufen wird.

Christoph2
23.03.2009, 20:51
ahh ja stimmt sorry ich war wohl gestern ein bischen gestört...

lg christoph