Hallo Leute, ich würde gerne in meinen Bastelprojekten uC/OS-II einsetzen. Für nichtkommerzielle Zwecke darf man das ja, also habe ich es mir runtergeladen. Doch nun sind im dem Zip-Archiv nur ein paar c-Dateien. Was soll ich denn nun damit anfangen? Wo ist der Code, der die Tasks switcht? Allgemein, gibt es irgendwie ein Tutorial, wie man an die Portierung de uC/OS-II heran geht? Ich stehe im Moment ziemlich auf dem Schlauch und die paar Application Notes auf der Micrium Website helfen mir auch nicht so sehr weiter. Wer hat selber uC/OS-II im Einsatz und kann mir ein bisschen weiter helfen? Ich würde mich über Hilfe von euch sehr freuen. Vielen Dank schon im Voraus!
Hi, was fuer einen Mikrocontroller verwendest du du? Hab vor ner Weile uC/OS auf nem AVR und ARM verwendet. Es gibt plattform spezifische und plattform unabhaengige Dateien. Die plattform spezifischen waren schwer auf der Homepage zu finden, wie ich mich erinnere. Man muss nur aufpassen, das der Port auch zum Compiler passt, da verschiedene c Compiler intern verschiedene Register verwenden. Wenn man selbst eine Portierung realisieren will, sollte man vieleicht einen Blick in das passende Buch zu uC/OSII werfen. Ohne die Funktionsweise der context switches zu verstehen, koennte es ziehmlich schwierig sein. Hoffe das hilf ein bisschen. Cheers Daniel
Hi, J.J. Labrosse (Erfinder von uc/OS) hat ein schönes Buch dazu geschrieben. Da steht drin, wie man das portiert. Mußt dich aber beeilen, das wird es nicht mehr lange zu kaufen geben, da eine neues Buch erschienen ist, das bezieht sich aber auf die uc/OS-III Version. Nur uc/OS-II ist frei für private Zwecke. ISBN: 978-1578201037 Weiter im Anhang eine sehr detailierte Beschreibung, wie das OS auf ARM portiert wurde. Für ATMEGA128 gibt es bei Micrium auf der Homepage eine fertige Portierung (hab ich aber noch nicht genutzt). www.micrium.com -> Download -> Ports Du mußt dich dort anmelden (kostet nichts). Alles was mit Tasks zu tun hat steht im Modul os_task.c. Der Taskswitch ist dort aber nicht drin, das ist ein Modul, welches zu dem Port der jeweiligen CPU gehört. 900ss
Daniel, ich möchte das OS auf einem ARM720T laufen lassen. mlp, leider führt mich Suchergebnis Nr. 4 auf eine Seite, wo ich Games downloaden kann? :-O Sieht ziemlich mysteriös aus. 900sss, danke für das Dokument. Ich werde es mir zu Gemüte führen und hoffe, es hilft mir etwas mehr, als die Micrium AppNotes :-)
Adelbert schrieb: > ich möchte das OS auf einem ARM720T laufen lassen. Für ARM gibt es auch Ports bei Micrium. Sie mal nach, vielleicht findest du dort sogar den passenden. Sonst nimm einen, wo der CPU-Kern dem Deines ARM720T entspricht und passe ihn an. 900ss
Ich habe schon einen passenden Code gefunden, es ist sogar ein Beispielprojekt für IAR EWARM dabei. Das einzige, was mir daran nicht gefällt, ist, dass der Code für ein ganz spezifisches Board geschrieben ist. Ich habe natürlich mein eigenes Board. Des weiteren ist er leider völlig überladen mit einem unnötig grossen Beispielprogramm, und das ich uC/OS-II bis jetzt noch nicht so gut kenne, ist es sehr schwierig für mich, herauszufiltern, welche Dateien vom Sourcecode ich wirklich brauche, und welche zum Beispielprojekt gehören.... BTW: weisst du einen guten TCP-Stack, welcher zum uC/OS-II passt? Den von Micrium bekommt man ja nicht, wenn man nicht einen gewissen, sichrlich astronomisch hohen, Betrag bezahlt.
Adelbert schrieb: > TCP-Stack Die folgenden beiden sollten sich ohne große Probleme für uc/OS-II eignen: http://www.sics.se/~adam/uip/index.php/Main_Page http://www.sics.se/~adam/lwip/ Wobei der erst genannte mit weniger Ram auskommt, aber die Performance auch geringer ist. Der ist eher für kleine 8-Bit Systeme gedacht. Ein Vergleich der beiden Stacks findest du im Anhang. Adelbert schrieb: > Das einzige, was mir daran nicht > gefällt, ist, dass der Code für ein ganz spezifisches Board geschrieben > ist. Ich habe natürlich mein eigenes Board. Also ein wenig solltest du schon selber machen. Wo bleibt denn sonst der Spaß? Mit den Appnotes von Micrium sollte eigentlich deutlich werden, welche Sourcen wofür sind und was du brauchst. AN1014, da steht es doch recht gut beschrieben.
Danke 900sss. lwip kenne ich bereits; ich hatte allerdings gehofft, dass es noch andere gibt. Bezüglich uC/OS-II. Ich hab jetzt im IAR Workbench nochmals versucht, ein neues Projekt anzulegen. Dabei jammert er mir allerdings immer, dass OS_TASK_TMR_PRIO nicht definiert ist, sowie diverse Hook-Funktionenn. Was für einen Wert muss OS_TASK_TMR_PRIO haben, und wo muss es definiert werden, und wozu wird es gebraucht? Im Manual taucht die Konstante leider nicht auf. Muss ich die Hook-Funktionen schreiben, oder sollten da nicht standardmässige Hooks im uC/OS drin sien im Stil von xyzHook(void) { #if OS_CPU_HOOK_EN > 0 app_cpu_hook(); #endif } Sodass die Funktion vorhanden ist, aber nichts macht, wenn die Hooks ausgeschaltet sind. So steht es zumindest in einem Beispiel im AN-1014, aber ich finde die Sourcecode-Datei nicht, wo das drin steht. Des weiteren beklagt sich der Linker über eine fehlende Funktion "OS_CPU_ExceptHndlr". Muss ich die schreiben, oder sollte die auch irgendwi in den untiefen de uC/OS sein? Was soll die Funktion tun? Irgendwie vermisse ich eine Step by Step Anleitung, wie man das Teil einrichtet. So im Stil von "Erst muss diese und jene Datei erstellt werden, welche die Funktionen xyz und abc enthalten soll ... " etc. Gibts kein Tutorial? Ich Steh' vor dem Sourcecode wie ein Ochs' vor dem Berg :O
Adelbert schrieb: > OS_TASK_TMR_PRIO Die Konstante sollte wie folgt in app_cfg.h definiert werden: #define OS_TASK_TMR_PRIO (OS_LOWEST_PRIO - 2) Sie sollte auch "OS_LOWEST_PRIO - 2" sein! Sonst kommt die Timertask evtl. nicht dran und deine Timer gehen zum Mond. Wenn du Google mit dem define fütterst, dann werden recht viele Fundstellen ausgespuckt, die dir geholfen hätten. > Muss ich die Hook-Funktionen schreiben, oder sollten da nicht > standardmässige Hooks im uC/OS drin sien im Stil von Weißt du was ein Hook ist, wofür er da ist? Wenn du dir das überlegst, dann wirst du auf die Lösung selber kommen. > Sodass die Funktion vorhanden ist, aber nichts macht, wenn die Hooks > ausgeschaltet sind. So steht es zumindest in einem Beispiel im AN-1014, Da bist du ja schon auf dem richtigen Weg mit deinen Gedanken. :-) > aber ich finde die Sourcecode-Datei nicht, wo das drin steht. Die Hook-Funktionen sind vom Anwender bereitzustellen. Die gehören zu dem jeweiligen Anwenderprojekt. Bei deinen ARM-Port, den du dir bei Micrium geholt hast, sollte alles dabei sein. Ich vermute du hast Dateien aus dem Projekt entfernt, die das benötigte enthalten. Andernfalls wäre das Projekt nicht konsistent. Adelbert schrieb: > Irgendwie vermisse ich eine Step by Step Anleitung Bei den neueren Ports oder den Kernel-Sourcen sind in der Regel zwei PDFs dabei... 1) "uCOS-II-CfgMan.pdf" da steht einiges auch zum portieren drin, ist aber keine Step-by-Step Anleitung. 2) "uCOS-II-RefMan.PDF" da ist das API beschrieben, damit du es später auch nutzen kannst. Versuche den letzen Kernel-Source V2.90 von Micrium zu laden. Dort sind die PDFs auch enthalten. Weiter stehen wichtige Infos in den Releasenotes der jeweiligen Version drin.
Hallo 900sss, ich habe nun im IAR Simulator versucht, das uC/OS zum Laufen zu kriegen. Es geht jetzt, aber er wechselt die Tasks nicht?! Der Interrupt ruft jeweils den ExceptHndlr auf, und dieser ruft OSSchedNew auf. Dort wird anhand der Variable OSRdyGrp bestimmt, welcher Task als nächstes laufen soll. Richtig? Bei mir hat diese Variable jetzt den konstanten Wert 0x81. Kein einziges mal wird irgend ein Task umgeschaltet. Und wenn ich alle Sourcecode-Dateien nach dieser Variable durchsuche, dann finde ich auch keine einzige Funktion, die dieser Variablen einen Wert zuweist! Was muss ich tun, damit das umschalten der Tasks funktioniert?
Adelbert schrieb: > .... > anhand der Variable OSRdyGrp bestimmt, welcher Task als nächstes laufen > soll. Richtig? Nööö. > Was muss ich tun, damit das umschalten der Tasks funktioniert? Das Dokument, was ich dort Beitrag "Re: uC/OS-II portieren" als Anhang bereitgestellt habe, lesen. Dort steht genau drin, was passieren muß. Ab Kapitel 4 bis 4.2.4 lesen, und(!) verstehen. Vorkauen hab ich keine Lust zu. Sorry.
Hallo 900sss, sorry, so war das nicht gemeint, mit dem Vorkauen. Ich will es ja schon gerne selber verstehen lernen und anwenden, doch wenn man bisher noch nichts mit RTOSen zu tun hatte, ist das ganze nicht sehr einfach zu verstehen. Bisher habe ich immer mit dem Mainloop mit Interrupts gearbeitet, aber das ist mühsam und manchmal kann das sehr unübersichtlich werden, deshalb erhoffe ich mir mit einem RTOS ein paar Vereinfachungen. (Ausserdem ist es viel toller, wenn man behaupten kann, dass auf dem selber gebauten Board ein RTOS läuft ;-)). Ich habe nun das Reference Manual zu uC/OS studiert. Oder jedenfalls schon mal die wichtigsten Teile, wo es um den Context Switch und den Scheduler geht. Und dabei ist mir etwas aufgefallen. Kann es sein, dass bei uC/OS IMMER der Task mit der höchsten Priorität die CPU belegt? wenn ich also 3 Tasks habe: Task A mit Priorität 1 (höchste) Task B mit Priorität 2 Task C mit Priorität 3 (niedrigste) dann wird IMMER Task A laufen, ausser, durch einen Interrupt wird Task B oder C aufgerufen, oder Task A gibt die CPU von sich aus frei. Ist das korrekt? Wenn dem so ist, dann wäre das nämlich ganz schlecht für mich. Ich hatte mir eher erhofft, dass jeder Task mal dran kommt, und halt einfach der mit der höchsten Priorität am längsten darf. Eine konkrete Anwendung davon wäre z.B.: Task A muss irgendwas berechnen, Task B soll einen TCP-Stack bedienen, und wenn dann mal ein bisschen CPU-Zeit übrig ist, kommt Task C dran, der das User Interface macht, also irgendwelche Lampen ein- und aus schaltet und Taster abfragt. Aber wenn uC/OS wirklich so funktioniert, wie ich es mittlerweile befürchte, dann wird das so nicht funktionieren, oder? Was eigentlich sehr schade ist, denn das uC/OS sieht in meinem ungeübten Auge sehr schlank und effizient aus, der C-Code ist sehr sauber, die Variablen haben gute Namen, sodass man sie wieder erkennt (und nicht einfach i, wie in vielen anderen Codes) und es hat sogar Kommentare. Gibt es vielleicht noch andere RTOSe, die man als Bastler oder Student kostenlos (oder meinetwegen zu einem vernünftigen Preis) kriegt, und die meinen Ansprüchen gerecht werden? :-)
da wirst du pech haben. die meisten Betriebsysteme arbeiten nach dem schema. Die Task mit der höchsten Priorität bekommt solange rechenzeit, bis sie auf irgend was warten muss. oder sie unterbricht sich selber für eine gewise zeit. das was windows macht ist richtig aufwendig, und für echtzeit anwendungen nicht wirklich brauchbar. Auch dien Prioritätseinteilung ist ggf etwas "daneben". z.B. eine gui sollte zügig reagieren. je nach dem wie komplex sie ausfällt. nicht erst nach sekunden. Taster werden über interrupts behändelt, oder ein task, die über den IRQ aktiviert wird. oder über eine Zeitlich gesteuerte task abgefragt. die z.B. alle 100ms anläuft und alle taster abklappert. entweder duch einen sleep oder eine timer realisier wird. Die task für die Darstellung läuft dann los, wenn es was zu tun gibt. an sonsten wartet sie darauf, das ihr jemand sagt, das sie es tun soll. Semaphoren, Messages, Eventflags, ... oder auch zeitgestuert. (z.B. timer oder sleep) tcp steck. ist eigentlich auch wie oben. ein teil wird im irq laufen, der server soket z.B. in einer eigenen task. Die läuft nur dann, wenn jemand was von deinem device will. amspmstem wartet er darauf das der IRQ zuschlägt. und deine berechnungen wurde ich in der niedrigsten task ebene abgehandelt, nur dann wenn keiner sonst was rechnen braucht und das dann in einer endlos schleife ohne sleep. Input IRQ / IRQ + Task Prio 15 / oder über Timer alle z.B. 100ms Output Task prio 20 synchronisiert über eventgroup oder änlich / oder timer gesteuert ( synchronisation nicht vergessen ) Netzerk IRQ + Task Prio 10 Berechnung Task Prio 30 gruss.
Adelbert schrieb: > Hallo 900sss, > sorry, so war das nicht gemeint, mit dem Vorkauen. Kein Thema, ich hatte ein wenig den Eindruck: statt lesen vorkauen lassen :-) > verstehen. Bisher habe ich immer mit dem Mainloop mit Interrupts > gearbeitet, aber das ist mühsam und manchmal kann das sehr > unübersichtlich werden, Ja das funktioniert auch sehr gut. > deshalb erhoffe ich mir mit einem RTOS ein paar > Vereinfachungen. Du mußt dann aber umdenken und vor allem verstehen, wie ein RTOS funktioniert. > (Ausserdem ist es viel toller, wenn man behaupten kann, > dass auf dem selber gebauten Board ein RTOS läuft ;-)). Stimmt! :-)) Aber wenn es nicht richtig läuft, dann lieber ohne RTOS aber mit Funktion. > Kann es sein, dass bei uC/OS IMMER der Task mit der höchsten Priorität > die CPU belegt? wenn ich also 3 Tasks habe: Ja. Das hast du richtig verstanden und ist bei fast allen Echtzeit-Kernen so. Fast alle benutzen das "pre-emptive" Multitasking, so auch uc/OS-II. Die andere bekanntere Form ist das kooperative Multitasking. Es gibt noch mehr Verfahren, z.B. noch Round-Robin. Manche Kerne können Pre-emptive und Round-Robin gleichzeitig, uc/OS-II aber nicht (ich glaube uc/OS-III ja). Beim Round-Robin bekomt jede Task eine feste Rechenzeit zugewiesen (Z.B. 10ms) und dann wird auf die nächste Task umgeschaltet. Wenn die Kernel beides können, dann wird zwischen Tasks, die die gleiche Priorität haben, mittels Round-Robin umgeschaltet. > > Task A mit Priorität 1 (höchste) > Task B mit Priorität 2 > Task C mit Priorität 3 (niedrigste) > > dann wird IMMER Task A laufen, ausser, durch einen Interrupt wird Task B > oder C aufgerufen, oder Task A gibt die CPU von sich aus frei. Ist das > korrekt? Stimmt fast, je nachdem wie du Task-A programmiert hast. > Wenn dem so ist, dann wäre das nämlich ganz schlecht für mich. Nein, du mußt das entsprechend programmieren. Die Task A z.B. soll rechnen. Sie muß ja die Inputs dafür bekommen. Wenn diese z.B. aus einer Queue kommen, dann wird nach dem Ende einer Berechnung wieder "QU_getValue" (Kernelaufruf) aufgerufen. Wenn jetzt keine Werte in der Queue stehen, dann wird die Task vom Scheduler stillgelegt und die anderen Tasks kommen dran. Sie dir mal die Task-States bei uc/OS-II an. Ich weiß aber nicht, ob das im Ref-Manual steht. > Gibt es vielleicht noch andere RTOSe, die man als Bastler oder Student > kostenlos (oder meinetwegen zu einem vernünftigen Preis) kriegt, und die > meinen Ansprüchen gerecht werden? :-) uc/OS-II sollte absolut deinen Ansprüchen gerecht werden, davon bin ich überzeugt :-) Aber es gibt z.B. noch FreeRTOS oder ChibiOS undnoch viele andere. Aber du hast dich in uc/OS-II schon ein wenig eingearbeitet, warum da jetzt aufhören und beim nächsten Kernel feststellen, dass der genauso arbeiten (pre-emptiv). Ich würde mich informieren, wie man mit so einem RTOS arbeitet. Hab gerade keine passende Stelle aber frage mal Google. 900ss
Hallo 900sss, JUHU, ES LEBT! Ich lasse erfolgreich 2 LEDs auf meinem Board blinken. Ich habe 2 Tasks kreiert, jeder schaltet seine LED ein bzw. aus und wird dann mittels OSTimeDly stillgelegt, sodass auf den anderen Task umgeschaltet werden kann. (Ein verdammter aufwand, nur um 2 lächerliche LEDs blinken zu lassen ;-))) Ansich ist das ganz toll, aber wie gesagt würde mir das, was du unter "Round Robin" beschrieben hast noch ein bisschen besser gefallen. Klar werde ich jetzt nicht unbedingt mit uC/OS aufhören wollen, ich möchte es schon gerne verwenden, aber wenn es den Round Robin auch noch beherrschen würde, wäre es perfekt. Kann das denn z.B. FreeRTOS oder ChibiOS? Oder gibt es das gar nicht fürm solche Embedded-Anwendungen? Jedenfalls, es macht mir in der Zwischenzeit den Eindruck, dass es funktioniert, aber ich bin mir nicht 100%ig sicher, ob ich alles richtig implementiert habe. Kannst du evtl. mal einen Blick in meinen Code werden, wenn ich ihn hochlade? (Darf ich das überhaupt, wenn da uC/OS mit dabei ist? :-/ ) Wie würde ich es jetzt machen, wenn ich z.B. Task A habe, welcher irgendwelches Zeugs berechnet, und Task B, der den TCP-Stack laufen lässt. Wenn beide grade nichts zu tun haben, sollen sie die CPU an den Task C abgeben, der das ganze dann visualisiert. Wie würde man das machen? Einfach überall einen OSTimeDly einzufügen, wird wohl kaum die richtige Lösung sein ;-) Ausserdem ist es schwierig, abzuschätzen, was für ein Delay sinnvoll ist. Müsste man das mittels eines Timers machen, der den Task C alle paar 10ms "aufweckt"? Was müsste ich tun, wenn ich ein paar Aufgaben habe, die eigentlich gleich wichtig sind? Nehmen wir an, ich habe 2 serielle Schnittstellen. Auf der einen kommen Daten rein, die sollen von der CPU gespeichert werden, und ein paar Dinge sollen modifiziert werden, und danach sollen die daten über die 2. Schnittstelle weiter gesendet werden. Dann sollte das senden doch eigentlich dieselbe Priorität haben, wie das empfangen, denn die Daten sollen ja auch innerhalb einer gewissen Zeit raus. In uC/OS darf aber ein Task nicht dieselbe Priorität haben, wie ein anderer! Wie könnte man das Problem lösen? In einem solchen Fall wäre Round Robin genau das richtige. Eigentlich würde das "ideale RTOS" wohl nach dem Schema vorgehen: Tasks mit der höheren Priorität laufen zuerst Tasks mit der selben Priorität bekommen nach dem Round Robin-Verfahren eine feste CPU-Zeit zugeteilt. Dann könne ich Task A und B mit gleicher Prio laufen lassen, die wechseln sich dann schön gleichmässig ab. Und dazwischen kommt dann irgendwann mal noch Task C dran, der eine niedrigere Priorität hat.
moin RS232 In, wird im IRQ behandelt, und in einem grossen loop buffer abgespeichert. Wenn daten anliegen, wird z.B. ein Eventflag gesetzt, um z.B. eine Task aufzuwecken. Die Verarbeitungstask ruft in RS232Read auf. hier wird z.b auf das eventflag von IRQ gewartet. und erst dann die angefordete anzahl von characters auf dem loop buffer copiert. Danach folgt in der task die verarbeitung. die irgend wann mal ein RS232Write aufruft. die funktion configurert z.B. den dma zum versenden, schaltet den DataSendCompleat IRQ scharf, und wartet ggf darauf, das die dtaen komplett versendet worden sind. bzw vor dem senden, damit es zu keinen kollisionen kommt. ggf aufpassen mit den sendpuffern. sollte die "RS232" noch zusätzlich irgendwelche Protokoll aufwand berücksichtigen, würde ich 2 weiter task drüberlegen. eine fürs senden, eine fürs empfangen. Prioritätsmässich würde ich senden höher legen als empfangen. denn wenn ich vor lauter empfangen nicht mehr senden kann ist auch nichts gewonnen. umgekehrt, läuft der empfangspuffer über, was ich erkennen kann. Weiter läuft die Kommunikation meist über queues oder ähnliches. die laufen dann irgend wann mal voll, so das die verarbeitende task entweder fehler beim eintragen bekommt, oder suspendiert wird, bis die queue wiede platz bekommt. Eine Receive Task sollte nicht als loop check for input implementiert werden. hier gibt es schönere lösungsmöglichkeiten über Interrupts gruss
Adelbert schrieb: > Hallo 900sss, > JUHU, ES LEBT! Ich lasse erfolgreich 2 LEDs auf meinem Board blinken. > Ich habe 2 Tasks kreiert, jeder schaltet seine LED ein bzw. aus und wird > dann mittels Na dann.... Applaus, freut mich. :-) > > OSTimeDly > > stillgelegt, sodass auf den anderen Task umgeschaltet werden kann. (Ein > verdammter aufwand, nur um 2 lächerliche LEDs blinken zu lassen ;-))) Ja ja, aber es ist gut so anzufangen. Wenn du noch ein kompliziertes Programm hättest, dann wüßtest du kaum noch, wo du die Fehler suchen solltest. > > Ansich ist das ganz toll, aber wie gesagt würde mir das, was du unter > "Round Robin" beschrieben hast noch ein bisschen besser gefallen. Das liegt nur daran, dass du das Prinzip eines solchen Schedulers (pre-emptiv) noch nicht ganz verstanden hast. Nicht umsonst wird dieses Verfahren so häufig eingesetzt. > Kann das denn z.B. FreeRTOS oder > ChibiOS? Oder gibt es das gar nicht fürm solche Embedded-Anwendungen? Ähhh die haben eine Homepage mit ganz viel Doku :-) Aus dem Kopf weiß ich das auch nicht, müßte erst nachlesen. Aber nochmal ganz kurz zur Taskumschaltung. Der Kernel schaltet die Tasks nicht nur bei "OSTimeDly" um, weil die eine Task dann "schlafen" geht. Der Scheduler prüft auch bei jedem Timertick (IRQ), ob er Tasks umschalten muß. Weiter prüft er bei fast jedem Kernelaufruf (API-Aufruf), ob eine höher priorisierte Task wieder "ready" ist. Ein kleines einfaches Beispiel, es ist nicht sinnvoll so ;-) Es dient nur der Verdeutlichung würde aber trotzdem so funktionieren. Nehmen wir an, du hast eine Task, die wartet auf Zeichen, die über den UART empfangen werden. Die Kette sieht so aus: UART --> UartRxIrq --> | QueueRx | --> UartRxTask --> | QueueData | --> .... weiter geht es unten Der UART empf. ein Zeichen, das lößt einen Empfangsinterrupt aus, diese (UartRxIrq) Routine ließt das Zeichen aus dem UART und packt es mittels OSQPost() in eine Queue (QueueRx). Die Task UartRxTask holt diese mittels OSQPend() wieder ab und verarbeitet sie. Die Task UartRxTask bleibt solange in der Funktion OSQPend(), bis etwas in die Queue geschrieben wird. Und was macht der Kernel solange mit dieser Task? Richtig, er legt sie schlafen und erst wenn ein Zeichen in der Queue ist, wird diese Task wieder geweckt. Also ähnlich, als wenn du die Task mit OSTimeDly schlafen legst. Und wenn diese Task jetzt die höchte Priorität hat, dann macht das nicht, solange keine Zeichen in der Queue sind schläft die sie jetzt und die anderen nieder priorisierten Tasks können arbeiten. Es ist so, dass der Kernel immer bei einem Betriebssystem-API-Aufruf die Task umschalten kann, wenn in dem Moment eine höher priorisierte "ready" ist. Es sei denn du hast es explizit verboten. Und weiter von oben: .... UartTxTask --> | QueueTx | --> UartTxIrq --> UART Die UartTxTask wartet mittels OSQPend() auf die Daten in "QueueData". Solange da nichts drin ist, schläft auch diese Task. Erst wenn Daten da sind, werden sie mittels OSQPost() in die QueueTx geschoben und NUR das erste Zeichen in den Uart geschrieben zum senden. Der UART-TX-Ready Interrupt holt sich jetzt die anderen Daten aus der Queue UartTxIrq bis er keine mehr findet, dann wird nichts mehr in den UART geschrieben und es war der letzte IRQ. Das ist jetzt etwas vereinfacht geschildert. Was passiert z.B. wenn die UartTxTask Daten in QueueTx packt und das Senden über UartTxIrq noch aktiv ist, weil die Queue nicht leer war? Dann darf UartTxTask nicht das erste Zeichen in den UART packen (das macht ja schon der UartTxIrq in dem Moment), dann gäbe es Datensalat. Das wird dann mit einem Flag gesteuert. Solange UartTxIrq aktiv ist, wird ein Flag gesetzt welches UartTxTask darüber informiert, dass der Sender-IRQ noch aktiv ist, er nicht extra angestoßen werden muß mit dem ersten Zeichen, welches UartTxTask senden möchte. Hmmm... Schwierig das in Text zu fassen, hoffe es ist einigermaßen verständlich. Ich denke du solltest einfach damit rumspielen. Geht ja nix kaputt :-) Und immer schön das Ref-Manual zu den verwendeten Funktionen lesen. Da wird es recht gut erklärt, sogar ob ein Context-Switch passiert (hab ich jedenfalls gerade gesehen an einer Stelle). Es gibt zur Erklärung auch noch einen Artikel: http://www.mikrocontroller.net/articles/Multitasking Deinen Source ansehen brauche ich nicht, solange er funktioniert. Außerdem ist in dem ersten von mir geposteten Dokument ausreichend beschrieben, wie man alles testet :-) Das kannst du ja mal mit deinem Kernel machen, dann wirst du schon sehen, ob du alles richtig gemacht hast. Uff ist das lang geworden. Solange es hilft.... 900ss
Hey 900sss, wow, vielen Dank für deine Ausführungen! Super :-) Ich habe mittlerweile noch ein bisschen mehr experimentiert, und nun lasse ich auch mittels zusätzlicher Interrupts LEDs blinken - via Messages, die an andere Tasks versandt werden usw. Wenn man da erst mal so ein bisschen in so ein RTOS rein sieht, dann merkt man erst, wie genial das ist! Total faszinierend, wie der Scheduler immer rausfindet, wo es noch was zu tun gibt und die Tasks das dann erledigen. Genau sowas wollte ich eigentlich! Frage: Wenn ich Task a mit einer hohen Priorität habe, und Task B mit einer niedrigen. B wartet jetzt mittels OSQPend(), bis was im UART rein kommt. Wird dann Task B geweckt, wenn was rein kommt, oder wird so lange am Task A weiter gerechnet, bis dieser sich schlafen legt oder was anderes tun muss? Umgekehrt ist der Fall natürlich klar, denn wenn B die höhere Priorität hätte, dann würde natürlich dieser Task weiterrechnen. Jetzt wo es läuft, ist es echt toll, dieses uC/OS :-) Das erste RTOS, das ich zum Laufen gekriegt habe. Beim TNKernel (welcher präemptiv ist und nach dem Round Robin arbeitet, also genau das was ich wollte) habe ich es auch nach vielen Versuchen nicht geschafft. uC/OS geht aber jetzt super :D danke für deine Hilfe!
Es wird solange task A ausgeführt, bis diese sich durch einen Systemaurfuf selber unterbicht und in den zustand waiting versetzt wird. Warten auf Events, auslesen eines elementes einer Queue, die lehr ist, ..., ... oder eine höher priorisierte Task in den Ready state wechselt. Dann kommt diese task C zum zuge. Task B bleibt im Ready state bis sie die task mit der höchsten priorität ist, die im Ready state ist. die task mit der niedrigsten priorität ist die idel loop, in der nicht benötigte rechenzeit verblasen wird. gruss und noch viel spass mit micrium
Adelbert schrieb: > Hey 900sss, > wow, vielen Dank für deine Ausführungen! Super :-) Freit mich, wenn es hilft. > .... Total faszinierend, > wie der Scheduler immer rausfindet, wo es noch was zu tun gibt und die > Tasks das dann erledigen. Genau sowas wollte ich eigentlich! Sach ich doch, pre-empives Multitasking ist schon gut, birgt aber auch Fallen, da der Programmablauf sehr schwer nachzuvollziehen ist. > kommt. Wird dann Task B geweckt, wenn was rein kommt, oder wird so lange > am Task A weiter gerechnet, bis dieser sich schlafen legt oder was > anderes tun muss? Überlege selber. Wenn ein Zeichen in der Queue landet und Task B in den Ready state wechselt, dann hast du zwei Tasks, die laufen könnten, A und B. Welche nimmt der Scheduler? > Umgekehrt ist der Fall natürlich klar, denn wenn B die höhere Priorität > hätte, dann würde natürlich dieser Task weiterrechnen. Richtig. Wenn du jetzt überlegt hast, dann wirst du wissen, welche Task oben dran kommt (A oder B). Also im Prinzip wird immer die Task mit der höchsten Priorität ausgeführt, wenn mehrere "ready" sind. Es gibt aber Ausnahmen, das Stichwort heißt "Priority inversion". Dieses ist wichtig da es sonst zu dead-locks kommen kann. Und uc/OS beherrscht das auch. Frag mal Google. Suche solange, bis du einen Beitrag findest, der das gut erklärt. Oder im Anhang auch zwei PDFs dazu. > Jetzt wo es läuft, ist es echt toll, dieses uC/OS :-) Sag ich doch :-) > Das erste RTOS, das ich zum Laufen gekriegt habe. Ja, man ist "stolz wie Oskar" ;-) > Beim TNKernel (welcher präemptiv ist > und nach dem Round Robin arbeitet, also genau das was ich wollte) habe > ich es auch nach vielen Versuchen nicht geschafft. TNKernel hab ich vor uc/OS probiert und er funktionierte auch. Aber zu uc/OS-II hatte ich mehr Infos, deshalb hab ich das auch noch probiert und bin dort geblieben. > super :D danke für deine Hilfe! Bitte schön, fröhliches Basteln. 900ss
123, auch dir möchte ich auch mal noch herzlich für deine Ausführungen danken. Ihr beide habt mir echt weiter geholfen :-) 900sss, noch eine allerletzte Frage. Ich habe jetzt folgendes probiert. Ich initialisiere den UART, natürlich mit Interrupts. Nun habe ich eine Funktion print_string gebastelt: void print_string(char *str) { char err; while(*str) { U0THR = *str; OSMboxPend(UART_Mbox, 0, &err); str++; } } die Interrupt-Routine sieht natürlich dann so aus: void uartinterrupt(void) { if(U0IIR & BIT_01) /* tx register empty */ { OSMboxPost(UART_Mbox, (void*)0xdeadbeef); } } Wenn also ein Zeichen in den Sendepuffer geschrieben worden ist, dann wird auf eine Message gewartet (und der Task dadurch natürlich de facto schlafen gelegt). Wenn das Zeichen gesendet worden ist, dann wird die Interruptroutine aufgerufen, welche eine (hier sehr sinnvolle....) Message an den Task sendet - und diesen dadurch natürlich wieder zum Leben erweckt. Darauf hin wird das nächste Zeichen gesendet usw. usf. Es funktioniert auch wunderbar zum Senden, wenn ich allerdings den selben Mechanismus einbaue, um was zu Empfangen - dann stürzt mir der Rechner ab, sobald ich auch nur ein einzelnes Zeichen sende. Er bleibt interessanterweise bei der Funktion OSIntExit hängen. Mach ich was falsch, hab ich zu wenig Stack, oder macht man das mit dem UART gar nicht so, wie ich es anpacke? Im Sample-Project von Micrium wird das zwar auch so gemacht. Ein Keyboard-Task checkt, ob eine Taste gedrückt wurde. Wenn nicht, wird er mittels OSTimeDly unterbrochen. Wurde allerdings eine Taste gedrückt, dann wird mittels OSMboxPost(AppKbdIFMbox, (INT32U)key); der Keycode an den User Interface Task gesandt, der das dann auf einem Display anzeigt (oder irgendwie so - ich kenne die Hardware, für die dieses Example geschrieben wurde, leider nicht).
Adelbert schrieb: > Mach ich was falsch, Wahrscheinlich ;-) > hab ich zu wenig Stack, Das kannst du besser probieren, als ich. Oder soll ich deinen Stack zum probieren vegrößern? ;-) > oder macht man das mit dem > UART gar nicht so, wie ich es anpacke? Es ist eine Möglichkeit, sehe da erstmal kein Problem. Nur das die Tasks, die print... aufrufen, solange hängenbleiben, bis der String gesendet ist. Das ist ein Nachteil. Aber wenn man damit leben kann... Du solltest deinen Sourcecode posten, so wie du ihn verwendest, keine Auschnitte oder abgetippt. Poste alle deine C Files ALS ANHANG. Das uc/OS selber kannst du weglassen. Was heißt denn, er bleibt hängen? Stürzt ab, aber woher hast du die Info, dass das in der IntExit Routine passiert? So kann man da nichts zu sagen. Du must etwas mehr Infos geben. 900ss
Moin das mit dem uart kann man so machen. nur warscheinlich etwas langsam. Aber zur übung und zum lernen sicher gut. Ich würde das senden des Strings imr IRQ machen. Mittels einer Semaphore würde ich verhindern, das 2 sende aufträge gleichzeitig auf die Uart zugreifen. zu deinem Problem. verwendest du 2 unterschiedliche MessageQueus? jeweils eine für jdede Task? 1. für die Send task, die 2. für die Receive Task? wenn nicht, weist du nicht, welche task aktiviert wird. Man könnte für das einlesen eine Semaphore verwenden. Beim Recieive würde ich dann einen Loopp buffer anlegen, in dem der IRQ daten ablegt. beim überlauf, ein Fehlerflag / eventflag setzen. Wenn daten in den puffer kopiert worden sind, wird für jedes Character ein OSSemPost auslösen Die ReadString liest aus dem Ringpuffer jeweils nur ein Character. Vor dem auslesen wird ein OSSemPend aufgerufen. Zur implementierung bitte einen Read pointer verwenden, der nur von der ReadString funktion verwendet wird, und einen Write pointer, der nur vom irq verwendet wird. ansonsten sind "Critical Sections" notwendig da die gleiche variable vom IRQ und einer Task verändert werden kann. Sicher nicht die schnellste möglichkeit.
900sss, den Sourcecode habe ich mittlerweile wieder gelöscht, weil ich mich so geärgert hab, weil er nicht funktioniert :-/ ich muss versuchen, ihn wiederherzustellen. Eigentlich wollte ich die Verwendung von einem speziellen write buffer vermeiden. Ich möchte gerne eine print-Funktion, welche ohne unnötige Buffers auskommt, sodass man einen String senden kann, ohne Daten herum zu kopieren. Geht das?
Sicher. Wenn du sicherstellen kanst / sicher stellt, dass der send puffer auf den du beim senden verwendest nicht wieder überschrieben wird. z.B. so. die Print Funktion bekommt einen Pointer auf die zu sendenden Strin übergeben. Diese startet das senden. und wartet auf das ende mit hilfe einer Semaphore. Der IRQ send buffer empty kopiert dann immer das nächste character in den das send register, bis der komplette string verschickt wurde. Wenn ales verschickt wurde setzt der IRQ die Semaphore und die blockierte task wieder zu aktivieren. ob du vor dem starten des Senden oder nach dem starten des senden wartetst, oder auf beides oder garnicht, hängt vom Anwendungsfall / Anforderungen ab. willst du Synchron oder asynchron arbeiten. Vor dem Start des Sendens verhindert, das während ein String verschikt wird ein zweite senden gestartet wird. z.B. wenn eine 2 tasks was senden will. oder wenn eine task so schnell hintereinander etwas senden will, das die RS232 nicht mehr hinterher kommt. der 2. Send wird bereits gestartet bevor der erste beendet wurde. Nach dem Senden, ist vom prinzip her das gleiche, wie wenn die task das senden im polling mode selber übernehmen würde. nur würde sie die ganze zeit den zustand des UARTS überprüfen und keiner anderen task die möglichkeit geben die rechenzeit sinvoller zu nutzen. Die task weis nach dem verlassen der Funktion, das der String über die Leitung geganben ist. und komplett verschickt wurde. nicht nur das das senden begonnen hat und demnächst irgend wann mal beendet wird. beim empfangen das gleiche. willst du synchron oder asynchron lesen. den von mir angesprochenen ringpuffer brauchst du nur, wenn du daten, die vor dem Receive aufruf eingelaufen sind empfangen willst. wenn nicht, kannst du die daten auch in den puffer speichern, dem du der funktion übergeben hast. Daten die vor dem aufruf eingetroffen sind liegen dann ggf noch im Register oder im fifo. gruss
Adelbert schrieb: > 900sss, > den Sourcecode habe ich mittlerweile wieder gelöscht, weil ich mich so > geärgert hab, weil er nicht funktioniert :-/ Da wär ich arbeitslos, wenn ich löschen würde, was nicht gleich funktioniert. An manchen Fehlern sucht man Wochen, wenn sie schwer bis garnicht reproduzierbar sind, also einfach zufällig mal auftreten. Ist zum Glück recht selten. Da schmeißt er die Flinte gleich in's Korn ;-) > Eigentlich wollte ich die Verwendung von einem speziellen write buffer > vermeiden. Ich möchte gerne eine print-Funktion, welche ohne unnötige > Buffers auskommt, sodass man einen String senden kann, ohne Daten herum > zu kopieren. Geht das? Klar, das hattest du ja schon und sah auch gut aus. Zumindest die Sendefunktion. Du hast doch sicher einen JLink oder ähnliches dann kannst du doch auch sehr schön debuggen und den Fehler finden. Man muß sich nur Gedanken machen, wie man an das Problem rangeht und vor allem wie muß der Code funktionieren, nicht nur "so ungefähr". 900ss
Hihi, normalerweise lösche ich ja auch nicht alles, was nicht funktioniert ;-) Nur diesmal hab ich mich ein bisschen geärgert. Ich muss es nochmal versuchen, das mache ich dann heute Abend und gebe wieder hier Bescheid - jetzt sitze ich grade in der FH und kann nicht so gut basteln. Gruss
900sss, so ich habe es erfolgreich zum Laufen gekriegt. Wie ich das Problem nun gelöst hab:
1 | char old, new; |
2 | OS_EVENT *mbox; |
3 | OS_EVENT *uartmbox; |
4 | char* uartstr; |
5 | |
6 | void timer_tick(void) |
7 | {
|
8 | T1IR = 0xFF; |
9 | old = new; |
10 | new = (FIO0PIN & BIT_05); |
11 | if(new && !old) |
12 | OSMboxPost(mbox, (void*)1); |
13 | }
|
14 | |
15 | void test_task(void* arg) |
16 | {
|
17 | char err; |
18 | inituart(); |
19 | while(1) |
20 | {
|
21 | OSMboxPend(mbox, 0, &err); |
22 | print("key pressed\n\r"); |
23 | }
|
24 | }
|
25 | |
26 | void inituart(void) |
27 | {
|
28 | PCLKSEL0 |= BIT_06; /* uart0 clock = 72 MHz */ |
29 | PINSEL0 |= (BIT_04 |BIT_06); /* enable RXD0 and TXD0 */ |
30 | U0LCR = BIT_07; /* divisor latch enable */ |
31 | U0DLM = 0; |
32 | U0DLL = 23; /* 115200 */ |
33 | U0FDR = 0xA7; |
34 | U0LCR = (BIT_01 | BIT_00); /* 8 bits, no parity */ |
35 | U0FCR = (BIT_02 | BIT_01 | BIT_00); /* fifo enable & reset */ |
36 | U0IER = (BIT_00 | BIT_01); /* enable interrupts */ |
37 | VICVectPriority6 = 0; |
38 | VICVectAddr6 = (long int)uartint; |
39 | VICIntEnable = BIT_06; |
40 | uartmbox = OSMboxCreate((void*)0x12345678); |
41 | }
|
42 | |
43 | void uartint(void) |
44 | {
|
45 | long int intstatus = U0IIR; /* get the interrupt source */ |
46 | if(intstatus & BIT_02) /* receive data available */ |
47 | {
|
48 | }
|
49 | else if(intstatus & BIT_01) /* transmit holding register empty */ |
50 | {
|
51 | uartstr++; |
52 | if(*uartstr != 0) |
53 | {
|
54 | U0THR = *uartstr; |
55 | }
|
56 | else
|
57 | {
|
58 | OSMboxPost(uartmbox, (void*)0x12345678); |
59 | }
|
60 | }
|
61 | }
|
62 | |
63 | void print(char* str) |
64 | {
|
65 | char err; |
66 | OSMboxPend(uartmbox, 0, &err); |
67 | uartstr = str; |
68 | U0THR = *str; |
69 | }
|
So, was macht der Code? der ausgeführte Task ist natürlich der test_task. Periodisch wird die Funktion timer_tick aufgerufen, welche einen Taster abfragt. Wurde dieser gedrückt (Erkennung der steigenden Flanke), dann wird eine Message an den test_task gesendet, welcher nur auf diese eine Message gewartet hat. Darauf hin führt der test_task nämlich die Funktion print auf. Diese prüft, ob momentan evtl. noch ein String gesendet wird. Wenn nicht, dann wird der Stringpointer uartstr entsprechend gesetzt, und das erste Byte ins Senderegister geschrieben. Die Funktion print ist dann fertig, der test_task kann sofort weiter rechnen. Ist das erste Byte des Strings dann gesendet, dann wird der uartint aufgerufen. Dieser prüft, ob das Senderegister leer ist. Ist dies der Fall, und das nächste Byte im String ist nicht eine Null, dann wird weiter gesendet. Ist es jedoch eine Null, der String ist also zu Ende, dann wird eine Message platziert. Und genau die Anwesendheit dieser Message prüft die print-Funktion zu beginn. Ist der String also noch nicht zuende gesendet worden, und man ruft trotzdem print auf, dann versetzt diese Funktion den aktuellen Task so langen in den "Schlafzustand", bis der String fertig gesendet wurde. Na, klappt das so? Bei mir kommt jedenfalls der Text richtig im Terminal an :-) Schöne Grüsse
Kennt von Euch jemand Firmen die Auftargsarbeiten µC/OSII durchführen bzw. bereits Produkte realisiert haben? Benötige da einige Kontakte Liebe Grüße
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.