Ich bin neu in der AVR Programmierung und habe gerade mein STK500 und STK501 bekommen. Ich bin allerdings berflich Softwareentwickler und arbeite seit 15 Jahren mit C++. Mir ist aufgefallen, dass es einen in meinen Augen sehr komischen Stil gibt, die AVR Programme zu schreiben. Im Prinzip ist der Kern eines Programms immer eine große Endlosschleife. Das klappt ja auch hervorragend, wenn ich ein paar Ports habe, und will diese dann mnit ein bischen Logik verknüpft auf andere Ports ausgeben. Aber: Ich habe mir jetzt gerade ein LCD Modul beschafft und einen Adapter für den STK500 gebastelt. Jetzt ist mir aufgefallen, das ich zwar die Ansteuerung für das LCD prima programmieren kann, wegen dem Timing der AVR aber während dieser Zeit nichts anderes mehr macht als Schleifen absitzen usw. Will ich gleichzeitig I/O machen wird die geschichte schon richtig schwierig. Durch das Warten auf den LCD Controller bekomme ich im Prinzip riesige Latenzzeiten in die Abwicklung der I/O Routinen. Mein Durcsatz reduziert sich also auf das langsamste Gerät am Controller. Grundsätzlich passiert das doch eigentlich immer, wenn ich 2 unterschiedliche Devices am AVR betreibe, die entsprechende Pausen für ihr Timing benötigen. Ich habe hierfür keine vernünftige Lösung gesehen. Insbesondere die C Bibliotheken sind für dieses Problem anscheinend gar nicht vorbereitet. Ich stelle mir vor, dass solch ein Programm als Zustands-Maschine abgebildet wird. (Das kann naiv sein, gebe ich ja zu). Das formale Beschreibungsmitte wäre dann ein Petri Netzwerk. Ich habe das in anderem Kontext (Multitasking auf dem PC) bereits für Simulationen verwendet. Gibt es hierfür einen Standard Ansatz (also ein Design Pattern)?
Bei LC-Displays ist die sache furchtbar einfach. Das Display muss/kann ja garnicht bei jedem Schleifendurchlauf angesprochen werden. Wenn du z.B. einen Wert auf dem Display anzeigen willst: if (AlterWert != NeuerWert) { LCD_Anzeige_erneuern(); } Nun ändert sich der Inhalt der Variable vielleicht jede Sekunde, die übrigen Durchläufe übergeht er einfach das IF-Statement und rennt durch den ürbigen Code. Das gleiche gilt natürlich auch für andere Devices... So wird eine angeschlossene MMC z.B. natürlich nur ausgelesen wenn bedarf besteht - nicht bei jedem Durchlauf des Programms.
Das Prinzip der Mainloop ist eigentlich, die einzelnen Tasks schnell genug zu machen, damit keine unzulässig hohe Verzögerung eintritt. D.h. einfach größere Tasks in viele kleinere Abschnitte unterteilen. Eventuell benötigt man dazu ein Byte, was sich merkt, mit welchem Programmpunkt man beim nächsten Aufruf fortsetzen muß. Im Falle des LCD legt man am besten einen gleich großen Puffer im RAM ab und jede Ausgabe erfolgt in diesen Puffer. Die Ausgaberoutine testet dann immer nur einmal das Busy-Bit und gibt ein Byte aus dem Puffer aus. Das sollte dann schnell genug sein. Alternativ kann das Busy-Testen und zyklische Ausgeben des Puffers zum LCD auch im Timerinterrupt erfolgen. Also immer, wenn im Programmablauf Wartezeiten auftreten, dann die nächste Task an die Reihe lassen. Peter
@Thomas: Klar, kannste sicher gern tun, aber solch ein Automat hat die unangenehme Eigenschaft, praktisch nicht mehr zu pflegen zu sein. Wenn Du zeitkritische Dinge zu erledigen hast, funktioniert das sicher am besten, wenn man die jeweiligen Ereignisse interruptgesteuert verwaltet. Die Mainloop fragt dann nur noch nach, welche Interrupts gerade anliegen und führt die entsprechenden Aktionen aus (bzw. kann sich ggf. in einen wohlverdienten avr_sleep() zurückziehen, wenn gerade nichts zu tun ist ;-). Die einzelnen Aktions-Routinen dürfen natürlich auf keinen Fall mehr irgendwelche Warteschleifen enthalten (und die ISRs erst recht nicht), sondern sie schieben die jeweilige Aktion nur an und setzen dann einen Trigger, der irgendwann zu einem Interrupt führt. Wird nur schwierig für Geräte, die nicht selbst interrupten können (je nach Anschlußvariante kann ein LCD dazu gehören), dort muß man sich dann evtl. mit einem geeigneten Timer aus der Patsche helfen.
Am besten baust Du für jeden "Task" eine Statusmaschine auf. Diese Statusmaschine muß ihren aktuellen Status innerhalb einer vorher festgelegten Zeit abarbeiten können. Dann synchronisierst Du diese "Tasks" mit einer festen Umlaufzeit, z.B. mit einem Timer. Beispiel: while(1) { /* Timerflag wird bei Timerüberlauf auf 1 gesetzt */ while (Timerflag == 0) {;} Timerflag = 0; /* Jetzt hast Du eine feste Umlaufzeit */ ... LCD_Maschine(); IO_Maschine(); ... } In der LCD-Maschine hast Du jetzt einen switch, z.B. so: switch (LCDStatus) { ... case WARTEN_BIS_LCD_FERTIG: if (Flag_LCD_fertig) { LCDStatus = LCD_FERTIG; } break; case LCD_FERTIG: ... } Der Funktion LCD_Maschine() kann also seelenruhig auf das LCD warten, verbraucht dabei aber kaum Prozessorzeit. War's verständlich? Grüße, Sebastian
Ach so: die Benutzung eines der vorhandenen Mini-Betriebssysteme wäre ja vielleicht auch eine sinnvolle Variante.
Ich bin jetzt bei Timer Interrupts gelandet. Ich verwende den Timer0 als Interruptquelle und habe in C auch schon eine Service Routine geschrieben. Hat sich eigentlich jemand mit den typischen Stack Manipulationen für Multitasking Betrieb auseinandergesetzt? Wenn es gelingt, die Rücksprungadesse - sprich den Kontext neu zu setzen, dann steht preemptivem Multitasking nichts mehr im Weg. Das Bedeutet es sind Threads möglich. Ich habe gesen, das es Echtzeit Betriebssysteme für den AVR gibt. Das interessiert mich aber nicht, da ich den Mini Kernel auch selber schreiben kann. Wer hat bereits Erfahrung mit der notwendigen Stack Manipulation und dem Verwalten der Rücksprungadessen? Ich teste momentan auf dem AT90S8515, der beim STK500 dabei ist. den ATMEGA128 vom STK501 hab ich noch nicht gesteckt. Ok für alle Skeptiker: RAM frisst das schon, das weiß ich auch!
Schau Dir exakt diese Echtzeitbetriebssysteme an, eben die, die diese geschrieben haben (es dürfte deren kaum mehr als eine Handvoll geben) haben sich auch mit genau Deinen Fragen auseinandergesetzt.
Hallo zusammen, ich habe eine kleine Testroutine für einen preemtiven Taskwechsel geschrieben (ohne Kontextsicherung usw). Das hat mit dem ursprünglichen Problem zwar zu tun, ist aber wesetlich allgemeiner. Da ich Interesse daran habe das breiter zu diskutieren beginne ich einen neuen Thread
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.