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_Pagehttp://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
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