Hallo Leute. Ich habe eine Frage zur Vorgehensweise bei der Entwicklung eines komplizierteren Programms für uC. Und zwar geht es darum wie man diverse Software Module (=Dienste) die oft gleichzeitig laufen sollen und verschiedene gegenseitige Abhängigkeiten haben, mittels Interrupts und main-routine am besten implementiert. z.B. Es sei eine Applikation die verschiedener Dienste bedarf. Einige Dienste (A) sollen in regelmäßigen Abständen aufgerufen werden. Einige Dienste (B) werden von extern oder von software aufgerufen. Und wieder andere Dienste (C) sind allgemeine Dienste oder Hintergrunddienste, z.B. eine interaktive Menü-geführte Benutzersteuerung (User Interface). Was tut man in die main routine und was verteilt man auf interrupts? Einige A-Dienste kann man gut mit Timer Interrupts erschlagen, für andere weniger zeitkritische A-Dienste wäre es auch nicht schlecht sie in einen main-loop zu bauen, da man u.U. nicht soviele T-IRQs hat oder? B-Dienste mit Externen IRQs, software IRQs oder durch normale Prozeduraufrufe. C-Dienste sind aber nun schwierig zu implementieren da sie für lange Zeit oder ständig laufen müssen. Also kann man sie als mainroutine vorsehen, dann hat man aber keinen loop mehr, und alle A-Dienste müssen in Timer Interrupts versorgt werden. Außerdem kann die mainroutine auch nur einen C-Dienst, z.b. das User-Interface enthalten. Einen weiteren C-Dienst müsste man auch mit Timer-Interrupts stückeln. Andere Alternativen laufen auf einen multitasking Kern hinaus oder auf Pointer-Orgien, was aber wieder zu viel des Guten wäre. Also wie macht es der erfahrene Entwickler? Tut er in fast allen Fällen immer das User-Interface ins main und alles andere in Interrupts? Oder würde er standardmäßig einen mainloop für A-Dienste verwenden und eine eventuelle Benutzersteuerung in die Interrupts stecken, und wie genau würde er dies machen? Und die Abhängigkeiten würde man wohl über Globalvariablen im RAM steuern. Und nun zur Benutzersteuerung: Welche Verfahren würde der uC Veteran für Benutzereingaben verwenden? Einen Ext. Interrupt wenn irgendeine Taste gedrückt wurde, worauf hin die Taste eingelesen wird, oder einen ständigen poll aller Tasten, was aber Rechenzeitverschwendung ist (Und was wohl bedingt dass die Benutzersteuerung nicht in Interrupts ist). Eine Tastenblockade muss dabei auch noch implementiert werden. Letztlich gibt es das Problem der Interrupt-Prioritäten. Da diese fest definiert sind, müsste man um andere Priotäten zu erziehlen die Interrupts erst nur flags setzen lassen und dann in main (nur wenn dies ein loop ist) oder in einer Timerinterrupt Routine neu aufrufen. Oder was für Alternativen gibt es? Das alles vor dem Hintergrund eines ausreichenend großen AVRs (Ich möchte z.B. ATmega 64 verwenden) Ich hoffe ihr könnt mir viele Tips geben wie ihr das löst, oder Quellen aufzeigen wo derartige Probleme angegangen werden... Danke für alle Antworten, mfg Moritz
Als erstes:Bin kein Profi-Entwickler;) Hmm, generell ist blos zu sagen interrupts so kurz wie möglich halten hatte da neulich schon bekanntschaften.Und keine funktionen in den interrupts aufrufen dann muss der uc alle möglichen register sichern. Wenn deine A dienste nicht absolut zeitkritisch sind würd ich die wie das userinterface und die tastenabfrage in ne main-loop stecken und von nem timer-interrupt aus triggern.Alles zeitkritische dann in interrupt rein. cu Flo (bitte fehler von mir korrigieren will auch was lernen)
Hi, Hast Du schonmal über den einsatz eines RTOS (Real Time Operating System) nachgedacht? Das ist in etwa so wie wie eine kleines Windows für Microcontroller. Du kannst hier einzelne eigenständige Tasks programieren welche alle für sich eine eigene main loop haben. Um die zuteilung der rechenzeit sowie die speicherzuteilung und interruptzuordnung kümmert sich das Betriebssytem oder Scheudler. Genau für die Anforderungen die du beschreibst wurden diese RTOS entwickelt und es giebt inzwischen einige freie und ettliche komerzielle zb. embos von segger: http://www.segger.com/embos.htm Ich habe schon komplexere Programme mit derartigen Betriebssytemen realisiert und ich finde es eine erhebliche erleichterung. Gruß Frank
Schon zwei interessante Antworten ;). Zu Florian: Wenn ich das Userinterface in main tue, kann das aber ohne weiteres kein loop mehr werden, weil die Menüsteuerung ja ständig auf neuen input wartet. Der Programmcounter muss auch dort stehen bleiben damit der "Menüzustand" nicht verloren geht. Um dies zu erreichen müsste ich den PC abspeichern, dann weiter den mainloop laufen lassen, und später per EXT-Int oder so beim nächsten Tastendruck wieder aufrufen. Entspricht das deiner Idee? Wird das oft so gehandhabt? Oder kann man den Menüzustand anders bewahren? Zu Frank: Vielen dank für deinen Tip! Im Prinzip ist das ja der Multitasking-Kern. Das Problem was ich sehe ist nur das der Rechenzeit die es verbraucht. Kannst du/jemand eine grobe Zahl sagen wieviel % der Rechenleistung für die RTOS-Verwaltung draufgeht? Ich möchte nämlich evtl. mehrere geschachtelte PWMs benutzen, und dann die vielen Timer-Interrupts. Da muss ich froh sein wenn die main-routine überhaupt noch was abbekommt, fürchte ich. mfg Moritz
Hi wieso blockiert ein Menüsystem den main-loop? Du solltest dich mal mit dem Prinzip der State-Machines auseinandersetzen. Da kannst du ein sehr komplexes Menü in main() realisieren und kommst doch alle parr µs einmal rum. Von einem fremden OS auf so kleinen µC halte ich persönlich nichts. IMHO ist es besser sich da was eigenes selber zu programmieren. Matthias
Meine Erfahrungen mit RTOS-Systemen sind auch sehr gut. Die Rechenzeit die der Multitasker braucht, ist nicht das Problem: je nach Applikation kannst Du mit 1 - 5% der Rechenleistung rechnen. Diese Zahl muss aber auch in Relation zum Projekt gesehen werden, auch ein auf Häppchen verteilter Main-loop braucht Rechenzeit. Das größte Problem auf einem kleinen mc ist meiner Ansicht nach der RAM-Verbrauch durch die Stacks. Was richtig schön ist beim Multitasking: Dein Projekt teilt sich in mehrere kleine Teile auf, die Beeinflussung der Einzel-Programme ist viel geringer als bei anderen Ansätzen. Einen interessanten Multitasking-Ansatz (open-source) habe ich übrigens hier gefunden: http://home.eunet.no/~jen/newebsrv.html Stefan
Ich würde auch zu RTOS raten... Habe mit der 8051er Familie und dem Keil RTX-51 Tiny gute erfahrungen gemacht! Vorteilhaft ist die eigenständige Entwicklung von "Systemkomponenten" ohne langwieriges beachten vn Abhängigkeiten!
Hallo Moritz! Im Prinzip hast du dir das eh richtig überlegt. Du musst dir nur im klaren sein, was wann gemacht werden muss. Bei meinem (allerdings ersten µC-) Projekt läuft alles interrupt-gesteuert, wobei in der Interrupt routine nur wenig gemacht wird, um schnell wieder rauszukommen. Meist werden globale Flags gesetzt, auf diese Flags reagiert dann die Mainloop (die aber eh meist im sleep hängt). Ich polle eine Tastaturmatrix 128x in der Sekunde, die paar Befehle brauchen (pro Sekunde) praktisch keine Rechenzeit und kann deshalb in der Interrupt-Routine gemacht werden. Das Hauptprogramm hat dann immer in der Variablen "key" die aktuelle Taste (bzw. " " wenn sie losgelassen wurde). Die Taste entscheidet dann über die Zustände, die das Programm hat. Im Menü häng ich zwar rum, bis es beendet ist, aber ich versäume nix, da ich alles wichtige im Interrupt mache. Natürlich kannst du auch durch ein Menü durchlaufen wenn eine Taste gedrückt ist, musst nur dafür sorgen beim nächsten Durchlauf richtig weiterzumachen. Also als eine State-Machine aufbauen. Ich komme eigentlich aus der Linux/GTK+ Ecke, deswegen ist es für mich nicht so schwer ereignisgesteuert zu programmieren.
Bin ebenfalls kein Profi-Entwickler, aber vielleicht sind das noch nuetzliche Informationsquellen: RTOS: Eine Liste von RTOS fuer AVR ("freie" und kommerzielle) findet im "Tools"-Bereich von avrfreaks.net. Welches "das Beste" vermag ich nicht zu sagen. Zustands-Maschine: Der C-Quellcode der AVR-Butterfly-Application (google findet) zeigt die Implementierung einer State-Machine. Auch dafuer mag es bessere Loesungen geben, aber fuer AVR habe zumindest ich noch keinen aehnlich umfangreichen und frei zugaenglichen Quellcode gefunden. Beide Ansaetze haben Vor- und Nachteile. Bei RTOS gibt man die Kontrolle ueber die "Main-Loop" an den Sheduler, verliert damit etwas Rechnenzeit aber gewinnt "Komfort". Spaetestens wenn man Systemresoucen in mehreren Tasks ansteuert (Timer etc.) sind die Einzelkomponenten aber nicht mehr ganz so eigentstaendig und eine Art "Sychronisation" ist erforderlich. Evtl. mit einem eigenen Task der die Systemkomponente abstrahiert. Für die gestellte Aufgabe mit "mehreren PWMs" mag das krtisch werden, wenn man diese in Software implementiert. Die Entwicklung mittels Zustands-Machine ist mglw. etwas komplexer, aber es wird keine Rechnezeit mit "task-switching" verbraucht (was bei schnellen Controllern mglw. keine Rolle spielt) und kein Platz fuer das RTOS benoetigt. Aber die State-Machine selbst benoetigt ja auch "ein paar Bytes". Die mir bekannten AVRs haben keine einstellbaren Interruptprioritaeten.
Die Lösung ist folgende: Man kennzeichnet den Zustand, in dem man gerade ist, nicht durch die Stelle im Programm, an der man gerade steht. Da steht man nämlich wirklich fest, wie in Stein gemeißelt und kann keine anderen Sachen mehr nebenbei machen. Sondern man merkt sich diesen Zustand in einer Variablen, und verzweigt dann in der Main-Loop entsprechend dieser Variable in die einzelnen Funktionen. Hier ein kleines Beispiel: http://www.specs.de/users/danni/appl/soft/c51/thclock/index.htm Da kennzeichnet die Variable "menu", ob man nun gerade die Temperatur anzeigt, die Weckzeit-Minuten stellt usw. Zusätzlich wird in der Main-Loop die Tastenbetätigung geprüft und der gerade aktiven Funktion mit übergeben. Trotzdem laufen natürlich die Temperaturmessung und Zeit-,Datumszählung ungehindert weiter. Da es in diesem Beispiel nur wenige Tasten und Menüpunkte sind, habe ich beides in eine Variable addiert. Das spart dann die separate Tastenauswertung in den jeweiligen Menüpunkten. Ansonsten sind nach diesem Prinzip aber auch wesentlich größere und komplexere Menüstrukturen realisierbar. Man muß eigentlich nur das Denken etwas umstellen, d.h. die Programme immer so schreiben, das sie zum Main zurückkehren, wenn gerade nichts mehr zu tun ist und das man sich merkt, wo man beim nächsten Durchlauf fortsetzen will. Peter
Danke für die vielen hilfreichen Antworten. Also Entweder Statemaschine oder RTOS. Die Statemaschine kann man doch im Prinzip auch mit Zeiger der den State representiert realisieren oder? Was ist der Vorteil einer Statemaschine gegenüber Zeiger? Zum RTOS habe ich noch einige kurze Fragen: - Ab wieviel Ram lohnt es sich? Ich will den ATmega64 (16MHz) mit 1kb RAM einsetzen. davon möchte ich gerne etwa 400byte für meine App. haben. - Ich vermute der Rechenzeitverbrauch des RTOS hängt stark von der Frequenz der Task-switches ab. Mein schnellster Task mit höchster Priorität soll ein möglichst exakt getimter Aufruf mit einer Freq. von etwa 1.6-16Khz (Je nach dem wieviel Potential noch ist) sein und mindestens mehrere hundert (eher 1000) Zyklen verbrauchen. Somit würde dieser einzelne Task einen Großteil der Rechenzeit beanspruchen. Ich kann mir nur schwer vorstellen das bei einer solchen Anwendung nur 1-5% für RTOS draufgehen. Was meint ihr, ist es möglich/sinnvoll? mfg Moritz
16khz würde ich weder mit main-loop noch mit Multitasking machen, sondern im Interrupt. 1000 Zyklen sind schon ein Wort, bei 16khz bedeutet das exakt 100% Auslastung ... das ist ein Fall zum Software-Optimieren. Ich würde es per Timer-IR machen, im IR den eigenen Timer-IR disablen und alle anderen freigeben. Alles andere dann im Hauptprogramm, da musst Du Dich eben entscheiden zwischen main-loop und multitasking. Wieviel RAM Du beim Multitasking brauchst, kommt auf Deine Applikation an. Pro Tasking mind. 35 Byte RAM für Register plus lokale Variablen und Unterprogrammaufrufe, je nach Task unterschiedlich. Wenn DU wie oben beschrieben, während einem IR andere IR freigibst, muss die IR-Schachtelung auch beim Stack berücksichtigt werden. Über den Daumen: 600 Byte sollten für 5-8 Tasks reichen. Stefan
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.