Hallo liebe AVR Gemeinde. Da die aktuell verfügbaren Real Time Operating Systems (RTOS) für AVR's nicht ganz meinen Erwartungen entsprach, habe ich mich daran gemacht selbst ein RTOS zu schreiben. Das Ergebnis ist AvRtos. Es wurde in C geschrieben um die Lesbarkeit zu verbessern und so evtl. Fehler schneller finden zu können. Das gesamte RTOS besteht lediglich aus 2 Dateien, was das Einbinden in ein bestehendes Projekt vereinfachen soll. Entwickelt wurde AvRtos auf einem ATmega32 und benötigt dabei folgende System Ressourcen: - 1 kByte Flash für Code - 11 Byte RAM pro Task Control Block - 5 Byte RAM pro Semaphor Control Block - 20 Byte RAM pro Queue Control Block Zu beachten ist jedoch, dass pro Task jeweils ca. 100-150 Byte für den Stack zur Verfügung gestellt werden muss. Dies hängt jedoch vom jeweiligen System ab. Für die Task Synchronisierung stehen Semaphoren und Queues zur Verfügung, was in den meisten Fällen ausreicht. Ich habe bei Sourceforge ein Projekt angelegt. Dort kann AvRtos auch heruntergeladen werden. https://sourceforge.net/projects/avrtos/ Unter folgendem Link ist die aktuelle Doxygen Dokumentation zu finden. http://avrtos.sourceforge.net Über Kritik oder weitere Anregungen würde ich mich natürlich sehr freuen.
Vielen Dank. Sieht gut aus, aufgeräumter und übersichtlicher Code, gute Beschreibung! Ich werde es gerne bei Gelegenheit ausprobieren. Eine Kleinigkeit die mir aufgefallen ist:
1 | /* Type definition for a queue */
|
2 | typedef struct _Queue |
3 | {
|
4 | uint8_t* queueBuffer; /*< Pointer to the queue buffer */ |
5 | uint16_t msgSize; /*< Size of one message */ |
6 | uint16_t msgCount; /*< Number of messages currently put to the queue */ |
7 | uint16_t msgMax; /*< Number of maximal messages that can be put to the queue */ |
8 | uint16_t insertPointer; /*< Pointer to handle message insertion */ |
9 | uint16_t removePointer; /*< Pointer to handle removing messages */ |
10 | tElement getElement; /*< Element to handle the queue task list */ |
11 | tElement putElement; /*< Element to handle the queue task list */ |
12 | } tQueue; |
Ist es wirklich notwendig, dass die Variabeln in diesem strukt uint16_t sind, oder würde uint8_t nicht auch reichen? Ist effizienter auf einem 8 Bitter, zudem hat man sicher Atomic-Zugriffe. MfG peter
:
Bearbeitet durch User
Hallo Peter, es ist tatsächlich nicht notwendig, dass bei der Queue als Datentyp uint16_t verwendet wird. Danke für die Anmerkung. Ich werde den Datentyp auf uint8_t ändern und eine neue Version hochladen. Die Anzahl an Nachrichten, die an die Queue gesendet werden können, reduziert sich somit auf maximal 255 und die Nachrichtengröße wird ebenfalls auf 255 Byte reduziert. Dies ist jedoch auf einem 8-Bitter mehr als genug. Es hat ausserdem den Vorteil, dass der Bedarf an RAM für einen Queue Kontrollblock auf 13 Byte reduziert wird und ein optimierter Zugriff auf die Daten der Queue stattfindet. MfG Harald
Wie viele Takte werden für einen Taskwechsel maximal benötigt? In der Dokumentation steht es nicht drin oder ich habe es überlesen.
Harald B. schrieb: > Für die Task Synchronisierung stehen Semaphoren und Queues zur > Verfügung, was in den meisten Fällen ausreicht. Ich vermisse die Möglichkeit, Semaphoren und Queues in Interrupts zu verwenden. Nicht wartend, natürlich, aber zur Signalisierung. IMHO ist das in Programmen mit einem RTOS eigentlich immer erforderlich. Im existierenden Code ist das schon aufgrund der verwendeten Methode der Interrupt-Sperre nicht möglich. Siehe <util/atomic.h> ATOMIC_BLOCK(ATOMIC_RESTORESTATE) statt ENTER/EXIT_CRITICAL.
Es schiene mir sinnvoll, den direkt auf die Timer-Hardware bezogenenen Teil auszulagern. Nicht alle AVRs sind gleich gestrickt und vielleicht will jemand das ja an einen anderen Timer hängen. Also den Timer-Hook _kernelScheduleTick als Teil des API exportieren und den Teil mit TIM0 in ein separates Files legen. Die Bits vom Timer-Register kann man übrigens auch symbolisch angeben. Das ist deutlich sicherer als numerisch (TCCR0 = 0x08 | 0x03).
:
Bearbeitet durch User
Wieviele Takte für einen Taskwechsel benötigt werden kann ich aktuell nicht sagen, da ich es bis jetzt noch nicht ermittelt habe. Ich werde dies noch nachholen und in die Doku aufnehmen. Die Möglichkeit Semaphoren aus einem Interrupt heraus zu setzen ist gegeben. Allerdings muss hier darauf geachtet werden, dass dies die letzte Aktion innerhalb der ISR ist. Der entsprechende Code für den Usart könnte z.B. wie folgt aussehen:
1 | ISR(USART_RXC_vect) |
2 | {
|
3 | uint8_t taskWoken = FALSE; |
4 | |
5 | ...
|
6 | |
7 | /* Check if the frame end was reached */
|
8 | if( lastCharacter == '\r' ) |
9 | {
|
10 | ...
|
11 | |
12 | /* All data received, wake up and inform calling task by setting the semaphore */
|
13 | taskWoken = TRUE; |
14 | }
|
15 | |
16 | if( taskWoken ) |
17 | {
|
18 | semaphoreSet( RxReadySemaphore ); |
19 | }
|
20 | }
|
Allerding ist das Senden von Nachrichten an eine Queue tatsächlich nicht möglich. Der interne Aufbau einer Queue lässt dies schon nicht zu. Ist die Queue bereits voll während der entsprechende Interrupt ausgeführt wird, würde die Nachricht verloren gehen. Queues sollten lediglich zur Synchronisierung und zum Datenaustausch zwischen Tasks eingesetzt werden. Zur Synchronisierung von Interrupts und Tasks können wie oben beschrieben Semaphoren eingesetzt werden. Die Initialisierung des Timer Interrupts sowie die Macros für den Context-Switch in eine separate Datei auszulagern habe ich auch schon überlegt. Allerdings war mein Bestreben, das gesamte RTOS in einer Datei zu belassen :-). Um einen anderen Timer (z.B. TIMER1) als System-Tick zu verwenden, müssen die entsprechenden Anpassungen aktuell in der Datei AvRtos.c vorgenommen werden. Dies habe ich in der Doxygen Doku beschrieben. Vielen Dank für die konstruktive Kritik. Ich werde die Anmerkungen beachten und in die neue Version mit aufnehmen. MfG Harald
Harald B. schrieb: > Um einen anderen Timer (z.B. TIMER1) als System-Tick zu verwenden, > müssen die entsprechenden Anpassungen aktuell in der Datei AvRtos.c > vorgenommen werden. Dies habe ich in der Doxygen Doku beschrieben. Wobei man dann aber diese Anpassung mit einem Update des Kerncodes nochmal machen muss.
Harald B. schrieb: > Zur Synchronisierung von Interrupts und Tasks können wie oben > beschrieben Semaphoren eingesetzt werden. Wobei hier aber Task-Switches ausschliesslich als Teil des Timer-Ticks ausgeführt werden. Als Reaktionszeit eines preemptives RTOS ist das doch arg zäh. Bei einem Zeitverhalten in dieser Grössenordnung kann man auch kooperativ arbeiten und dem Programmierer damit einige Arbeit ersparen. Um das mit kurzen Reaktionszeiten nutzen zu können könnte der Timer-Tick in seine 2 Teile getrennt werden. Der zweite Teil liesse sich dann in anderen ISRs verwenden.
A. K. schrieb: > Wobei man dann aber diese Anpassung mit einem Update des Kerncodes > nochmal machen muss. PS: Weshab man üblicherweise jene Files, die zur Anpassung vorgesehen sind, von der Deklaration des APIs trennt. Das betrifft also auch das .h File.
Ja, das ist allerdings richtig. Hmmm... Es ist unter Umständen tatsächlich sinnvoller von der 1-File Strategie weg zu gehen und die Hardware abhängigen Teile in ein separates File auszulagern. Ich sehe schon... Die nächste Version kommt bald :-).
Ich habe mir eure Kritik und Anregungen zu Herzen genommen und eine neue verbesserte Version erstellt. Diese kann unter https://sourceforge.net/projects/avrtos/ heruntergeldaen werden. Die Hardware abhängigen Teile des Codes habe ich nun in ein 2. File ausgelagert. Bitte darauf achten, dass die Initialisierung des Timer Interrupt nun in der Datei AvRtosHw.c zu finden ist. Ausserdem können nun alle OS-Funktionen auch innerhalb einer Interrupt-Routine aufgefufen werden. Am Ende der Interrupt-Routine wird dann ein Context-Switch ausgeführt, falls eine Task lauffähig geworden ist. Um dies zu erreichen musste das Macro ISR() neu definiert werden. Also bitte darauf achten, dass alle Interrupt-Funktionen mit Hilfe dieses Macros definiert werden. Falls Ihr weitere Anregungen habt, stehe ich euch gerne zur Verfügung. MfG Harald
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.