Wie implementiere ich eine einfache Stapelumschaltung in PIC12 / 16-Kernen?

7

Ich versuche zu verstehen, wie Echtzeitbetriebssysteme funktionieren. Ich habe mir die Quellcodes einiger RTOS angesehen. Ich möchte lernen, indem ich mein einfaches RTOS erstelle, so etwas wie FLIRT .

Ich verwende die PIC16-Serie und den XC8 C-Compiler mit MPLABX. Ich möchte auch diese sehr einfache RTOS-PIC12-Serie implementieren.

Also entschied ich mich, zunächst zu lernen, wie man einen Stapel manipuliert (wie es Supercat in dieser Antwort getan hat ), und begann mit der Suche und stieß auf AN818 mit dem Titel "Manipulieren des Stapels des PIC18-Mikrocontrollers". Zitiert aus dem Anwendungshinweis:

Traditionell wurde der Mikrocontroller-Stack nur als Speicherplatz für Rücksprungadressen von Subroutinen oder Interrupt-Routinen verwendet, bei denen alle Push- und Pop-Operationen ausgeblendet waren.

Zum größten Teil hatten Benutzer keinen direkten Zugriff auf die Informationen auf dem Stapel. Der PIC18-Mikrocontroller weicht geringfügig von dieser Tradition ab. Mit dem neuen PIC18-Kern haben Benutzer jetzt Zugriff auf den Stapel und können den Stapelzeiger und die Stapeldaten direkt ändern.

Ich bin verwirrt. Wie kommt es, dass RTOSs für PIC-Mikrocontroller entwickelt wurden, die mit PIC16-Kernen arbeiten? Zum Beispiel ist OSA RTOS für PIC12 / 16 mit mikroC-Compiler verfügbar.

Können Sie mich auf einige Ressourcen verweisen oder wenn möglich Beispiele nennen, damit ich mehr über das Stack-Switching erfahren kann?

abdullah kahraman
quelle

Antworten:

4

Jedes RTOS für einen PIC, der keinen durch Software adressierbaren Stapel hat, erfordert im Allgemeinen, dass alle Aufgaben bis auf eine der Aufgaben in unterbrechungsfreie Teile unterteilt sind, die auf der obersten Stapelebene beginnen und enden. Die Operation "Task-Yield" verwendet keinen Funktionsaufruf, sondern eine Sequenz wie

// Dieser Code ist Teil von Aufgabe C (für dieses Beispiel wird angenommen, dass es Aufgaben gibt
// genannt A, B, C.
  movlw JumpC4 & 255
  gehe zu TASK_SWITCH_FROM_C
TargetC4:

An anderer Stelle im Code wäre ein Code wie:

TASK_SWITCH_FROM_A:
  movwf nextJumpA // Status der Aufgabe C speichern
  // Versende nun die nächste Anweisung für Aufgabe A.
  movlw TaskB_Table >> 8
  movwf PCLATH
  movf nextJumpB, w
  movwf PCL
TASK_SWITCH_FROM_B:
  movwf nextJumpB // Status der Aufgabe C speichern
  // Versende nun die nächste Anweisung für Aufgabe A.
  movlw TaskC_Table >> 8
  movwf PCLATH
  movf nextJumpC, w
  movwf PCL
TASK_SWITCH_FROM_C:
  movwf nextJumpC // Status der Aufgabe C speichern
  // Versende nun die nächste Anweisung für Aufgabe A.
  movlw TaskA_Table >> 8
  movwf PCLATH
  movf nextJumpA, w
  movwf PCL

Am Ende des Codes befindet sich für jede Aufgabe eine Sprungtabelle. Jede Tabelle müsste in eine 256-Wörter-Seite passen (und könnte somit maximal 256 Sprünge haben).

TaskC_Table:
JumpC0: gehe zu TargetC0
JumpC1: gehe zu TargetC1
JumpC2: gehe zu TargetC2
JumpC3: gehe zu TargetC3
JumpC4: gehe zu TargetC4
...usw.

Tatsächlich movlwlädt die zu Beginn der Task-Switch-Sequenz das W-Register mit dem LSB der Adresse des Befehls bei JumpC4. Der Code at TASK_SWITCH_FROM_Cwürde diesen Wert irgendwo speichern und dann den Code für Aufgabe A versenden. Später, nachdem er TASK_SWITCH_FROM_Bausgeführt wurde, würde die gespeicherte JumpC4Adresse erneut in W geladen und das System würde zu der Anweisung springen, auf die dadurch verwiesen wird. Diese Anweisung wäre eine goto TargetC4, die wiederum die Ausführung bei der Anweisung fortsetzen würde , die der Taskwechselsequenz folgt. Beachten Sie, dass beim Taskwechsel der Stapel überhaupt nicht verwendet wird.

Wenn man einen Taskwechsel innerhalb einer aufgerufenen Funktion durchführen möchte, ist dies möglicherweise möglich, wenn der Aufruf und die Rückgabe dieser Funktion auf ähnliche Weise wie oben behandelt werden (man müsste den Funktionsaufruf wahrscheinlich in ein spezielles Makro umschließen, um erzwingen, dass der richtige Code generiert wird). Beachten Sie, dass der Compiler selbst keinen Code wie oben generieren kann. Stattdessen würden Makros im Quellcode Anweisungen in der Assembler-Datei generieren. Ein vom RTOS-Anbieter bereitgestelltes Programm würde die Assembler-Datei lesen, nach diesen Anweisungen suchen und den entsprechenden Vektorcode generieren.

Superkatze
quelle
Ich konnte den Code nur aus dem PCLATH-Einstellungsteil verstehen. Was sind JUMPCx und TargetCx? Was macht ein Umzug JumpC4 & 255nach W?
Abdullah Kahraman
Danke für die Bearbeitung! Wäre besser, wenn Sie mich darüber bemerkt hätten :) Soweit ich weiß, befindet sich bei jedem Ertrag einer Aufgabe ein Rückkehrziel am Tisch der Aufgabe. Dies ist , was ich in C jetzt tun, etwas sehr ähnlich wie diese . Ich verstehe nicht, warum die Tische gebraucht werden? Diese Seite zeigt, dass nur eine Variable pro Aufgabe oder zwei bei Paging verwendet werden können.
Abdullah Kahraman
1
Es gibt eine Reihe leicht unterschiedlicher Ansätze, mit denen Multitasking auf dem PIC erreicht werden kann. Der Vorteil des Jump-Table-Ansatzes besteht darin, dass Einzelbyte-Variablen verwendet werden, um den nächsten Schritt jeder Aufgabe zu verfolgen, die Aufgaben selbst jedoch weiterhin Code im Wert von 2 KB verwenden können. Wenn der Code für jede Aufgabe in eine 256-Byte-Seite passt, kann auf die Sprungtabelle verzichtet werden. Wenn Sie die Sprungtabelle nicht verwenden möchten, können Sie alternativ Zwei-Byte-Variablen für den 'nächsten Schritt' jeder Aufgabe verwenden, aber der Code für den Aufgabenwechsel wird langsamer und umfangreicher.
Supercat
Bei der Verwendung von movlw/gotoversus retlwspart letzteres ein Wort Code-Speicherplatz pro Task-Switch, ist jedoch aufgrund der Notwendigkeit calleines "Sprungbretts" ein Zyklus langsamer . Noch wichtiger ist, dass bei Verwendung movlw/gotoeiner Aufgabe Aufgabenwechsel innerhalb verschachtelter Routinen durchgeführt werden können. Wenn man die Verwendung retlwauf Aufgaben beschränken würde, die nicht aus verschachtelten Routinen heraus wechseln mussten, wäre das in Ordnung, aber das Mischen von Paradigmen zum Wechseln von Aufgaben könnte Verwirrung stiften.
Supercat
4

Die herkömmlichen 14-Bit-Core-PICs (meistens PIC 16 und einige PIC 12) bieten dem Programm keine Möglichkeit, auf den Aufrufstapel zuzugreifen. Dies macht daher ein echtes Multitasking-System unmöglich.

Sie können jedoch immer noch so genannte Pseudo-Aufgaben haben. Dies ist eine Routine, die regelmäßig von der Hauptereignisschleife aufgerufen wird. Es verwaltet intern eine Neustartadresse. Wenn die Haupt-Even-Schleife es aufruft, springt sie zur Neustartadresse der Pseudo-Task, die für dieses Modul privat ist. Nach einiger Verarbeitung ruft der Taskcode ein YIELD-Makro auf, das die Neustartadresse unmittelbar nach dem Makro festlegt und dann vom ursprünglichen Aufruf zurückkehrt. Da der Aufrufstapel nicht abgewickelt werden kann, kann YIELD nur von der obersten Aufgabenebene aus aufgerufen werden. Trotzdem ist dies eine nützliche Abstraktion.

Ein Beispiel für eine solche Pseudo-Task, die zum Verarbeiten eines Befehlsstroms von einem Host-Computer verwendet wird, finden Sie in meinem QQQ_CMD.ASPIC-Vorlagenmodul. In diesem Fall ist die Rückkehr zum Aufrufer im GETBYTE-Makro verborgen, das aus Sicht der Aufgabe das nächste Eingabebyte abzurufen scheint. Installieren Sie meine PIC Development Tools-Version von der Seite zum Herunterladen von Software . QQQ_CMD.ASPIC befindet sich im Verzeichnis SOURCE> PIC im Softwareinstallationsverzeichnis.

Um zu sehen, wie echtes Multitasking ausgeführt werden kann, wenn Sie Zugriff auf den Aufrufstapel haben, werfen Sie einen Blick auf TASK.INS.ASPIC im selben Verzeichnis. Das ist mein generisches Multitasking-System für den PIC 18. Sie können dasselbe für die dsPIC-Architektur sehen, indem Sie sich TASK.INS.DSPIC im Verzeichnis SOURCE> DSPIC ansehen.

Olin Lathrop
quelle
Es scheint, als müsste ich anfangen, Montage zu lernen.
Abdullah Kahraman
@abdullah: Ja, Sie benötigen Assemblersprache, um unnatürliche Aktionen auf dem Stapel auszuführen, was in den meisten Taskwechselschemata erforderlich ist. Sie müssen zumindest die Assemblierung verstehen, um zu wissen, was in den niedrigen Ebenen vor sich geht, selbst wenn Sie in einer höheren Sprache programmieren. Auf Mikrocontrollern können Sie Hardwaredetails nicht ignorieren. Es ist nicht wie bei einem großen Allzwecksystem, bei dem zwischen Ihnen und der Hardware viele Abstraktionsebenen bestehen. Alle EEs, die Mikrocontroller verwenden, müssen die Montage kennen.
Olin Lathrop
Ich denke, ich muss mir nicht alle Anweisungen merken und so, sie sind alle im Datenblatt. Was ich verstehen sollte, sind die Hardware-Details. Ich habe mir das GETBYTEMakro angesehen, konnte aber überhaupt nichts verstehen. Ich werde später darauf zurückkommen. Jetzt schaue ich auf Pic'Xe und kann die Baugruppe verstehen, wenn ich auf das Datenblatt schaue . Ich verstehe jedoch nicht, was FSR und INDF usw. bedeuten, was Hardware ist. Also, ich bin jetzt ein Follower von PIC x14 Architektur Tutorials hier .
Abdullah Kahraman
@abdullah: Welchen Teil von GETBYTE verstehst du nicht? Ich kann es durchgehen, aber spezifische Fragen würden helfen. Beachten Sie, dass mein Code die in STD.INS.ASPIC definierten Makros annimmt und auch über meinen Präprozessor (PREPIC, beschrieben im DOC-Verzeichnis) ausgeführt wird, bevor MPASM etwas davon sieht. Ich habe mir gerade GETBYTE angesehen und es werden einige meiner Makros (DBANKIF, JUMP usw.) verwendet, aber nicht der Präprozessor.
Olin Lathrop
Ah, es ist wirklich zu schwer für mich, GETBYTE zu verstehen. Was ich jedoch vom YIELD-Makro verstehe, ist, dass es die Neustartadresse der Funktion, die sich unmittelbar nach dem Makro befindet, im Stapel der Funktion speichert. Wenn ein Taskwechsel-Trigger auftritt, wird der PC mit der Neustartadresse der nächsten Task geladen. Was ich verstehe ist, dass, wenn ich keine Aufgabenumschaltung in den Unterroutinen benötige, was ich noch nie getan habe, dies praktisch dieselbe Technik wie bei der Stapelumschaltung ist.
Abdullah Kahraman
2

Die "14-Bit-Core" -Chips der PIC16-Serie verfügen über einen "Hardware-Stack-RAM", der vollständig getrennt und unabhängig vom "Daten-RAM" ist. (Es hat sogar eine andere Größe und Breite als der Daten-RAM). Das "Kernblockdiagramm" für einen dieser Chips zeigt deutlich, dass der "Hardware-Stapel" nur mit dem Programmzähler verbunden ist - der einzige Befehl, der in diesen Hardware-Stapel schreibt, ist (die verschiedenen Arten von) CALL und der einzige Befehl Das, was von diesem Hardware-Stack liest, ist (die verschiedenen Arten von) RETURN.

"Stack Switching" ist für den "Hardware Stack" in diesen "14 Bit Core" Chips nicht möglich.

Dies macht die meisten herkömmlichen Methoden zum Wechseln von Betriebssystemaufgaben auf dieser Chipserie unmöglich.

Es gibt einige RTOSs, die auf dieser Reihe von Chips ausgeführt werden. Daher müssen auf diesem Chip einige Task-Switching-Methoden möglich sein ( "PIC Microcontroller-spezifische Multitasking-Methoden" ).

In der Praxis haben alle RTOSs, die ich gesehen habe, eine Art kooperativen Task-Switcher, der nur im Code der obersten Ebene jeder Task von einer Task zur nächsten "nachgibt". Da die Ausbeute niemals innerhalb eines Unterprogramms liegt, gibt es keine Rücksprungadressen auf dem Hardware-Stack.

Im Prinzip ist es möglich, diese Chips so zu programmieren, dass ein Software-Stack im RAM erstellt und damit ein Return-Stack simuliert wird. (Verwenden eines "benutzerdefinierten Stapels" im Daten-RAM anstelle des Hardware-Stapels). Dann könnten Sie alle herkömmlichen Aufgaben zum Wechseln von Betriebssystemaufgaben verwenden, z. B. jeder Aufgabe einen separaten RAM-Block für ihren lokalen Stapel zuweisen usw. Ich weiß nicht, ob sich die zusätzliche Komplexität und die nicht standardmäßigen Aufrufsequenzen lohnen würden.

Davidcary
quelle
Danke für die allgemeinen Infos und den Link. Der Link hat sehr geholfen.
Abdullah Kahraman