Was ist der Grund, warum mein PIC16-Multitasking-RTOS-Kernel nicht funktioniert?

11

Ich versuche, ein semi-präventives (kooperatives) RTOS für PIC x16-Mikrocontroller zu erstellen. In meiner vorherigen Frage habe ich erfahren, dass der Zugriff auf den Hardware-Stack-Zeiger in diesen Kernen nicht möglich ist. Ich habe mir diese Seite in PIClist angesehen und versuche dies mit C zu implementieren.

Mein Compiler ist Microchip XC8 und derzeit arbeite ich an einem PIC16F616 mit 4 MHz internem RC-Oszillator, der in den Konfigurationsbits ausgewählt ist.

Ich habe gelernt, dass ich mit C auf PCLATH- und PCL-Register zugreifen kann, indem ich mir die Header-Datei meines Compilers ansehe. Also habe ich versucht, einen einfachen Task-Switcher zu implementieren.

Es funktioniert wie gewünscht im Debugger, wenn ich den Debugger nach dem Neustart anhalte, zurücksetze und den PC auf den Cursor setze, wenn sich der Cursor nicht in der ersten Zeile ( TRISA=0;) befindet, sondern in einer anderen Zeile (zum Beispiel ANSEL=0;). Beim ersten Start des Debuggers erhalte ich folgende Meldungen Debugger Console:

Launching
Programming target
User program running
No source code lines were found at current PC 0x204

Bearbeiten: Ich weiß nicht, warum es funktioniert hat, aber der Debugger funktioniert jetzt perfekt. Lassen Sie also die obige Ausgabe und den obigen Absatz weg.

Bearbeiten: Wenn Sie die Hauptdefinition so ändern, funktioniert der folgende Code. Dies startet die Hauptfunktion an der Programmadresse 0x0099. Ich weiß nicht, was das verursacht. Dies ist keine echte Lösung. Ich vermute jetzt, dass es einen compilerspezifischen Fehler gibt.

void main(void) @ 0x0099
{

Hier ist mein C-Code:

/* 
 * File:   main.c
 * Author: abdullah
 *
 * Created on 10 Haziran 2012 Pazar, 14:43
 */
#include <xc.h> // Include the header file needed by the compiler
__CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_ON & MCLRE_OFF & CP_OFF & IOSCFS_4MHZ & BOREN_ON);
/*
 * INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN
 * WDT disabled and can be enabled by SWDTEN bit of the WDTCON register
 * PWRT enabled
 * MCLR pin function is digital input, MCLR internally tied to VDD
 * Program memory code protection is disabled
 * Internal Oscillator Frequency Select bit : 4MHz
 * Brown-out Reset Selection bits : BOR enabled
 */

/*
 * OS_initializeTask(); definition will copy the PCLATH register to the task's PCLATH holder, which is held in taskx.pch
 * This will help us hold the PCLATH at the point we yield.
 * After that, it will copy the (PCL register + 8) to current task's PCL holder which is held in taskx.pcl.
 * 8 is added to PCL because this line plus the "return" takes 8 instructions.
 * We will set the PCL after these instructions, because
 * we want to be in the point after OS_initializeTask when we come back to this task.
 * After all, the function returns without doing anything more. This will initialize the task's PCLATH and PCL.
 */
#define OS_initializeTask(); currentTask->pch = PCLATH;\
                             currentTask->pcl = PCL + 8;\
                             asm("return");

/*
 * OS_yield(); definition will do the same stuff that OS_initializeTask(); definition do, however
 * it will return to "taskswitcher" label, which is the start of OS_runTasks(); definition.
 */

#define OS_yield();          currentTask->pch = PCLATH;\
                             currentTask->pcl = PCL + 8;\
                             asm("goto _taskswitcher");

/*
 * OS_runTasks(); definition will set the "taskswitcher" label. After that it will change the
 * current task to the next task, by pointing the next item in the linked list of "TCB"s.
 * After that, it will change the PCLATH and PCL registers with the current task's. That will
 * make the program continue the next task from the place it left last time.
 */

#define OS_runTasks();       asm("_taskswitcher");\
                             currentTask = currentTask -> next;\
                             PCLATH = currentTask->pch;\
                             PCL = currentTask->pcl;

typedef struct _TCB // Create task control block and type define it as "TCB"
{
    unsigned char pch; // pch register will hold the PCLATH value of the task after the last yield.
    unsigned char pcl; // pcl register will hold the PCL value of the task after the last yield.
    struct _TCB* next; // This pointer points to the next task. We are creating a linked list.
} TCB;

TCB* currentTask; // This TCB pointer will point to the current task's TCB.

TCB task1; // Define the TCB for task1.
TCB task2; // Define the TCB for task2.

void fTask1(void); // Prototype the function for task1.
void fTask2(void); // Prototype the function for task2.

void main(void)
{
    TRISA = 0; // Set all of the PORTA pins as outputs.
    ANSEL = 0; // Set all of the analog input pins as digital i/o.
    PORTA = 0; // Clear PORTA bits.

    currentTask = &task1; // We will point the currentTask pointer to point the first task.

    task1.next = &task2; // We will create a ringed linked list as follows:
    task2.next = &task1; // task1 -> task2 -> task1 -> task2 ....

    /*
     * Before running the tasks, we should initialize the PCL and PCLATH registers for the tasks.
     * In order to do this, we could have looked up the absolute address with a function pointer.
     * However, it seems like this is not possible with this compiler (or all the x16 PICs?)
     * What this compiler creates is a table of the addresses of the functions and a bunch of GOTOs.
     * This will not let us get the absolute address of the function by doing something like:
     * "currentTask->pcl=low(functionpointer);"
     */
    fTask1(); // Run task1 so that we get the address of it and initialize pch and pcl registers.
    currentTask = currentTask -> next; // Point the currentTask pointer to the next pointer which
    fTask2(); // is task2. And run task2 so that we get the correct pch and pcl.

    OS_runTasks(); // Task switcher. See the comments in the definitions above.
}

void fTask1(void)
{
    OS_initializeTask(); // Initialize the task
    while (1)
    {
        RA0 = ~RA0; // Toggle PORTA.0
        OS_yield(); // Yield
        RA0 = ~RA0; // Toggle PORTA.0
    }
}

void fTask2(void)
{
    OS_initializeTask(); // Initialize the task
    while (1)
    {
        RA1 = ~RA1; // Toggle PORTA.1
        OS_yield(); // Yield
        RA1 = ~RA1; // Toggle PORTA.1
    }
}

Und hier ist die Auflistungsdatei für die Demontage, die mein Compiler erstellt hat. Beginnt bei line 74.

Ich habe den eigentlichen Chip programmiert und überhaupt keine Änderung an PORTA vorgenommen. es funktioniert nicht

Was ist der Grund, warum mein Programm nicht funktioniert?

abdullah kahraman
quelle

Antworten:

10

Was Sie versuchen zu tun, ist schwierig, aber sehr lehrreich (wenn Sie bereit sind, viel Mühe zu investieren).

Zunächst müssen Sie sich darüber im Klaren sein, dass diese Art der Nur-PC-Taskumschaltung (im Gegensatz zu PC + SP) (die das einzige ist, was Sie auf einem einfachen 12- oder 14-Bit-PIC-Kern ausführen können) nur funktioniert, wenn der gesamte Ertrag ( ) Anweisungen in einer Aufgabe haben dieselbe Funktion: Sie können sich nicht in einer aufgerufenen Funktion befinden, und der Compiler darf nicht mit der Funktionsstruktur in Konflikt geraten sein (wie dies bei der Optimierung der Fall sein könnte).

Nächster:

currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
  • Sie scheinen anzunehmen, dass PCLATH die oberen Bits des Programmzählers sind, da PCL die unteren Bits sind. Das ist nicht der Fall. Wenn Sie in PCL schreiben, werden die PCLATH-Bits in den PC geschrieben, aber die oberen PC-Bits werden niemals (implizit) in PCLATH geschrieben. Lesen Sie den entsprechenden Abschnitt des Datenblattes erneut.
  • Selbst wenn PCLATH die oberen Bits des PCs wären, würde dies Sie in Schwierigkeiten bringen, wenn die Anweisung nach dem Goto nicht auf derselben 256-Anweisungsseite wie die erste Anweisung angezeigt wird.
  • Das einfache Goto funktioniert nicht, wenn sich _taskswitcher nicht auf der aktuellen PCLATH-Seite befindet. Sie benötigen ein LGOTO oder ein gleichwertiges Gerät.

Eine Lösung für Ihr PCLATH-Problem besteht darin, nach dem goto ein Label zu deklarieren und die unteren und oberen Bits dieses Labels in Ihre pch- und pcl-Positionen zu schreiben. Ich bin mir jedoch nicht sicher, ob Sie in der Inline-Assembly ein "lokales" Etikett deklarieren können. Sie können sicher in einfachem MPASM (Olin wird lächeln).

Schließlich müssen Sie für diese Art der Kontextumschaltung ALLEN Kontext speichern und wiederherstellen, von dem der Compiler abhängig sein kann, einschließlich

  • Indirektionsregister
  • Statusflags
  • Speicherplätze kratzen
  • lokale Variablen, die sich im Speicher möglicherweise überschneiden, weil der Compiler nicht erkennt, dass Ihre Aufgaben unabhängig sein müssen
  • andere Dinge, die ich mir momentan nicht vorstellen kann, aber der Compiler-Autor könnte sie in der nächsten Version des Compilers verwenden (sie sind in der Regel sehr einfallsreich)

Die PIC-Architektur ist in dieser Hinsicht problematischer, da viele Ressourcen auf der gesamten Speicherzuordnung verteilt sind, wo traditionellere Architekturen sie in Registern oder auf dem Stapel haben. Infolgedessen generieren PIC-Compiler häufig keinen wiedereintrittsfähigen Code. Dies ist definitiv das, was Sie benötigen, um die gewünschten Dinge zu tun (auch hier wird Olin wahrscheinlich lächeln und sich zusammensetzen.)

Wenn Sie sich für die Freude am Schreiben eines Task-Switcher interessieren, empfehle ich Ihnen, zu einer CPU zu wechseln, die eine traditionellere Organisation hat, wie z. B. ein ARM oder ein Cortex. Wenn Sie mit den Füßen in einer Betonplatte mit PICs stecken, untersuchen Sie vorhandene PIC-Umschalter (z. B. Salve / Kürbis?).

Wouter van Ooijen
quelle
Danke für die tollen Infos! Ich bin fest entschlossen, einen kooperativen Task-Switcher zu erstellen. XC8 und PIC sind mir diesbezüglich nicht auf meiner Seite :) Ja, wie Sie sehen, ist es möglich, Labels zu erstellen, wie ich es in einer meiner Antworten auf diese Frage getan habe .
Abdullah Kahraman
Zu meinem Glück gibt es auch kein Paging des Programmspeichers für den PIC16F616, an dem ich arbeite. Das ist an dieser Stelle ein großer Vorteil, oder?
Abdullah Kahraman
Könnten Sie näher erläutern, wie sich lokale Variablen im Speicher überlappen und auch "Speicherorte kratzen"?
Abdullah Kahraman
Wenn Sie sich auf Chips mit 2K-Code oder weniger beschränken, können Sie zwar das lgoto vergessen, aber nicht die Seiten mit 256 Anweisungen. Scratch: Ein Compiler kann davon ausgehen, dass alles, was er im Speicher tut, an Ort und Stelle bleibt, es sei denn, es ist "flüchtig". Daher kann es an einigen Stellen zu Teilberechnungen kommen, die von verschiedenen Funktionen gemeinsam genutzt werden können . Ovelap: Wenn main () sowohl f () als auch g () aufruft (und es keine anderen Aufrufe gibt), können die lokalen Variablen von f () und g () denselben Speicherorten zugeordnet werden.
Wouter van Ooijen
Nun, es scheint fast unmöglich zu sein, diese Variablen zu erreichen und aufgrund ihrer zufälligen Position im Speicher zu speichern, oder?
Abdullah Kahraman
7

Ich habe die von Ihnen bereitgestellte Baugruppenliste durchgesehen, und nichts ist so offensichtlich kaputt.

Wenn ich du wäre, wären meine nächsten Schritte:

(1) Ich würde eine andere Methode zum Blinken der LEDs wählen. Das berüchtigte "Lese-, Änderungs- und Schreibproblem" kann (oder auch nicht) durch "XORWF PORTA, F" in der Baugruppenliste ausgelöst werden.

Vielleicht so etwas wie:

// Partial translation of code from abdullah kahraman
// untested code
// feel free to use however you see fit
void fTask2(void)
{
    OS_initializeTask(2); // Initialize task 2
    while (1)
    {
        PORTC = 0xAA;
        OS_yield(2); // Yield from task 2
        PORTC = 0x55;
        OS_yield(2); // Yield from task 2
    }
}

(Wenn Sie wirklich detaillierte Erklärungen darüber erhalten möchten, warum "XORWF PORTA, F" häufig Probleme verursacht, lesen Sie " Was bewirkt das Einschalten eines einzelnen Ausgangspins am Microchip PIC16F690, um spontan einen anderen Pin am selben Port auszuschalten? "; " Was passiert ? wenn Daten in LATCH geschrieben werden? ";" Das Read-Modify-Write-Problem ";" Read before Write ")

(2) Ich würde den Code in einem Schritt durchgehen und sicherstellen, dass die Variablen auf die erwarteten Werte und in der erwarteten Reihenfolge eingestellt werden. Ich bin nicht sicher, ob es einen einstufigen Hardware-Debugger für den PIC16F616 gibt, aber es gibt viele ausgezeichnete PIC-Mikrocontroller-Simulatoren wie PICsim , die Chips der PIC16-Serie simulieren können.

Einzelschrittcode (in einem Simulator oder mit einem Einzelschritt-Hardware-Debugger) ist eine gute Möglichkeit, die Details der tatsächlichen Vorgänge zu verstehen, zu bestätigen, dass die Dinge so ablaufen, wie Sie es beabsichtigt haben, und Sie können die Dinge sehen, die sind Praktisch unmöglich zu sehen, wenn das Programm mit voller Geschwindigkeit ausgeführt wird.

(3) Wenn ich immer noch ratlos bin, würde ich versuchen, den Code so zu übersetzen, dass Arrays anstelle von Zeigern verwendet werden. Einige Leute finden die Verwendung von Zeigern etwas schwierig und schwierig zu debuggen. Oft finde ich , dass in dem Prozess des Übersetzens tricky Zeiger Code in Array-orientierten Code, finde ich heraus , was der Fehler ist. Selbst wenn ich am Ende zum ursprünglichen Zeigercode zurückkehre und die Array-Version wegwerfe, ist die Übung nützlich, da sie mir geholfen hat, den Fehler zu finden und zu beheben. (Manchmal kann der Compiler kürzeren, schnelleren Code aus Array-orientiertem Code generieren, daher kann es vorkommen, dass ich den ursprünglichen Zeigercode wegwerfe und die Array-Version behalte.)

Vielleicht so etwas

// Partial translation of code from abdullah kahraman
// untested code
// feel free to use however you see fit
struct TCB_t // Create task control block and type define it as "TCB_t"
{
    unsigned char pch; // PCLATH value
    unsigned char pcl; // PCL value
    int next; // This array index points to the next task. We are creating a linked list.
};

int currentTask = 1; // This TCB index will point to the current task's TCB.

struct TCB_t tasks[3]; // Define the TCB for task1 and task2.

#define OS_initializeTask(x); tasks[x].pch = PCLATH;\
                             tasks[x].pcl = PCL + 8;\
                             asm("return");

#define OS_runTasks();       asm("_taskswitcher");\
                             currentTask = tasks[currentTask].next;\
                             PCLATH = tasks[currentTask].pch;\
                             PCL = tasks[currentTask].pcl;

#define OS_yield(x);         tasks[x].pch = PCLATH;\
                             tasks[x].pcl = PCL + 8;\
                             asm("goto _taskswitcher");
Davidcary
quelle
Ich implementiere jetzt Arrays. Danke für die Empfehlung.
Abdullah Kahraman
3

Ich würde Davidcary grundsätzlich zustimmen. Es sieht so aus, als könnte es funktionieren.

Ich weiß nicht, warum es funktioniert hat, aber der Debugger funktioniert jetzt perfekt.

Ich vermute damit meinst du, dass es im Simulator perfekt funktioniert .

1) Überprüfen Sie, ob Ihre Aufgaben in einer Nicht-RTOS-Umgebung auf dem realen Chip eigenständig funktionieren.

2) Führen Sie das In-Circuit-Debugging durch. Gehen Sie das Programm auf dem realen Chip durch und beobachten Sie alle relevanten Variablen, um sicherzustellen, dass alles wie geplant verläuft.

Raketenmagnet
quelle
Ja, ich habe den Debugger gemeint, das ist der Simulator von MPLABX. Aufgaben arbeiten alleine in einer Nicht-RTOS-Umgebung. Ich habe keinen ICD. Ich habe nur mikroElektronika easyPIC5 mit ICD, aber es funktioniert nur mit mikroC Compiler. Wenn ich jetzt die Compiler wechsle, kann ich das Problem nicht finden, oder?
Abdullah Kahraman
1

Ich habe Ihren Code nur kurz angeschaut, aber es macht keinen Sinn. An mehreren Stellen schreiben Sie an PCL und erwarten dann, dass andere Anweisungen ausgeführt werden.

Wie ich bereits sagte, ist C für diese Art des Zugriffs auf grundlegende Hardwareregister auf niedriger Ebene ungeeignet. Sie müssen dafür wirklich Assembly verwenden. Der Versuch herauszufinden, warum C-Code nicht funktioniert, ist nur sinnlose Zeitverschwendung.

Olin Lathrop
quelle
Ich konnte Montage und C nicht kombinieren. Ich musste viel arbeiten. Sowohl die Demontage als auch der C-Code erscheinen mir logisch. Wo beziehen Sie sich darauf, dass ich Anweisungen ausführen möchte, die auf ein Schreiben in PCL folgen? Ich habe den Debugger sowohl für Assembly als auch für C beobachtet und er funktioniert wie gewünscht.
Abdullah Kahraman
Entschuldigung für die -1. Ich hätte versehentlich drücken sollen und das habe ich jetzt bemerkt.
Abdullah Kahraman
@abdullah: Auf dem Computer, auf dem ich mich gerade befinde, kann ich den Quellcode nicht sehen. Es ist im Browser dauerhaft reduziert. Ich erinnere mich, dass Sie PCLATH etwas zugewiesen haben, dann PCL, und ich denke, in einem Fall haben Sie versucht, eine RÜCKKEHR durchzuführen. Sobald Sie in PCL schreiben, springt die Ausführung zu der Adresse, die Sie in PCLATH: PCL eingegeben haben. Daher sind die folgenden Anweisungen irrelevant. Es ist wirklich nicht gut, dies in C zu tun, da Sie mit vom Compiler verwalteten Ressourcen herumspielen und dadurch möglicherweise die Compiler-Annahmen ungültig machen. Verwenden Sie bereits eine echte Baugruppe. Ich werde es leid, das wiederholen zu müssen.
Olin Lathrop
1
Wenn man sich den Code ansieht, gibt es keinen Ort, an dem PCL unmittelbar vor einer anderen Anweisung geändert wird. Der einzige Ort, an dem es geändert zu werden scheint, ist ganz am Ende von main (). Aber es ist ein guter Punkt, dass Sie sehr sicher sein müssen, dass Sie den Compiler nicht um seine Ressourcen kämpfen. Sie beide werden verlieren.
Raketenmagnet
3
C ist für diese Art von Arbeit durchaus akzeptabel, und tatsächlich ist es vorzuziehen, in einer Mittelsprache wie C zu schreiben, im Gegensatz zur Assemblersprache, da es einfacher zu lesen und zu warten ist. Ein anständiger Compiler generiert Code, der nicht zu weit von dem entfernt ist, was die durchschnittliche Person sowieso schreibt. Normalerweise schreibe ich Assembler nur für den sehr einfachen Startcode, für bestimmte Bereiche, in denen ich besser als der Compiler optimieren kann, oder für schnelle Interrupts oder wenn dies durch Einschränkungen der Codegröße vorgeschrieben ist. Heutzutage besteht keine große Notwendigkeit für eine reine Montage.
Akohlsmith
1

Im Folgenden finden Sie die Vorgehensweise bei der Inline-Montage mit dem XC8-Compiler, die jetzt funktioniert . Ich muss jedoch hinzufügen, dass mehr Code zum Speichern und Wiederherstellen des STATUSRegisters entwickelt wird, was etwas schwieriger erscheint als bei einem normalen Register.

Bearbeiten: Code wird geändert. Den vorherigen Code findenSie in älteren Versionen dieses Beitrags.

/*
 * File:   main.c
 * Author: abdullah
 *
 * Created on 10 Haziran 2012 Pazar, 14:43
 */
#include <xc.h> // Include the header file needed by the compiler
#include "RTOS.h" // Include the header for co-operative RTOS.
__CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_ON & MCLRE_OFF & CP_OFF & IOSCFS_4MHZ & BOREN_ON);

unsigned char OS_currentTask; // This register holds the current task's place in the array OS_tasks
unsigned char OS_tasks[4]; // This array holds PCL and PCLATH for tasks. This array will have..
//                            .. (number of tasks)*2 elements, since every task occupies 2 places.

void fTask1(void); // Prototype the function for task1.
void fTask2(void); // Prototype the function for task2.

void main(void)
{
    TRISA = 0; // Set all of the PORTA pins as outputs.
    TRISC = 0; // Set all of the PORTC pins as outputs.
    ANSEL = 0; // Set all of the analog input pins as digital i/o.
    PORTA = 0; // Clear PORTA bits.
    PORTC = 0; // Clear PORTC bits.

    OS_currentTask = 0; // Current task is first task.
    fTask1(); // Call task to initialize it.
    OS_currentTask += 2; // Increment task pointer by two since every task occupies 2 places in the array.
    fTask2(); // Call task to initialize it.
    OS_runTasks(4); // Run the tasks in order. The argument of this macro takes is: (Number of tasks) * 2
}

void fTask1(void)
{
    OS_initializeTask(); // Initialize the task so that task runner can get its ingredients.
    while (1)
    {
        PORTC = 0xAA;
        OS_yield(); // Yield CPU to other tasks.
        PORTC = 0x55;
        OS_yield(); // Yield CPU to other tasks.
    }
}

void fTask2(void)
{
    OS_initializeTask(); // Initialize the task so that task runner can get its ingredients.
    while (1)
    {
        PORTC = 0xFF;
        OS_yield(); // Yield CPU to other tasks.
        PORTC = 0x00;
        OS_yield(); // Yield CPU to other tasks.
    }
}

Und hier ist die Header-Datei RTOS.h:

/* 
 * File:   RTOS.h
 * Author: abdullah
 *
 * Created on 21 Haziran 2012 Perşembe, 10:51
 */

#ifndef RTOS_H
#define RTOS_H

asm("OS_yield MACRO");
asm("local OS_tmp");
asm("movlw   _OS_tasks            ; Store the address of tasks, which is the start address of our task 'array'."); 
asm("addwf   _OS_currentTask, w   ; Add current task's index to the start address."); 
asm("movwf   fsr                  ; We have the index of current task in W. Copy it to FSR"); 
asm("movlw   high(OS_tmp)         ; Copy PCLATH register's contents for the label, to W register.");
asm("movwf   indf                 ; Copy W to current task's first item. We now store PCLATH of the current state of the task."); 
asm("incf    fsr, f               ; Increment index, so that we will point to the next item of current task."); 
asm("movlw   low(OS_tmp)          ; Copy PCL of the label to W register. This will let us save the PCL of the current state of the task.");
asm("movwf   indf                 ; Copy W to task's next item. With that, we will initialize the current task.");
asm("goto    OS_taskswitcher");
asm("OS_tmp:                      ; We will use this label to gather the PC of the return point.");
asm("ENDM"); 

#define OS_yield(); asm("OS_yield");

asm("OS_initializeTask MACRO");
asm("local   OS_tmp");
asm("movlw   _OS_tasks            ; Store the address of tasks, which is the start address of our task 'array'."); 
asm("addwf   _OS_currentTask, w   ; Add current task's index to the start address."); 
asm("movwf   fsr                  ; We have the index of current task in W. Copy it to FSR"); 
asm("movlw   high(OS_tmp)        ; Copy PCLATH register's contents for the label, to W register."); 
asm("movwf   indf                 ; Copy W to current task's first item. We now store PCLATH."); 
asm("incf    fsr,f                ; Increment index, so that we will point to the next item of current task."); 
asm("movlw   low(OS_tmp)         ; Copy PCL of the label to W register. This will let us save the PCL of the current state of the task."); 
asm("movwf   indf                 ; Copy W to task's next item. With that, we will initialize the current task."); 
asm("return                       ; We have gathered our initialazation information. Return back to main."); 
asm("OS_tmp                      ; We will use this label to gather the PC of the return point.");
asm("ENDM"); 

#define OS_initializeTask(); asm("OS_initializeTask");

asm("OS_runTasks MACRO numberOfTasks");
asm("global OS_taskswitcher");
asm("OS_taskswitcher:");
asm("CLRWDT"); 
asm("movlw   0x02                 ; W = 2"); 
asm("addwf   _OS_currentTask, f   ; Add 2 to currentTask, store it in currentTask."); 
asm("movlw   numberOfTasks        ; W = numOfTasks");
asm("subwf   _OS_currentTask, w   ; w= f - w"); 
asm("btfsc   status, 0            ; If currentTask >= numOfTasks"); 
asm("clrf    _OS_currentTask      ; Clear currentTask"); 
asm("movlw   _OS_tasks            ; Store the address of tasks, which is the start address of our task 'array'."); 
asm("addwf   _OS_currentTask, w   ; Add current task's index to the start address."); 
asm("movwf   fsr                  ; We have the index of current task in W. Copy it to FSR"); 
asm("movf    indf, w              ; Copy the contents of current task's first item to W"); 
asm("movwf   pclath               ; Copy W to PCLATH. As a result, current task's PCLATH will be in PCLATH register."); 
asm("incf    fsr, f               ; Increment index, so that we will point to the next item of current task."); 
asm("movf    indf, w              ; Copy the contents of current task's second item to W."); 
asm("movwf   pcl                  ; Copy W to PCL. Finally, current task's PCL will be in PCL register.");
asm("ENDM");

#define OS_runTasks(numberOfTasks); asm("OS_runTasks "#numberOfTasks);

#endif  /* RTOS_H */
abdullah kahraman
quelle
Sieht so aus, als würden Sie Ihr eigenes Kopfgeld gewinnen. Herzliche Glückwünsche! :-)
Stevenvh
@stevenvh Ah, passiert das, ich wusste es nicht? Danke :)
Abdullah Kahraman
Herzlichen Glückwunsch zum Laufen!
Davidcary
Danke @davidcary! Ich schätze deine Glückwünsche sehr.
Abdullah Kahraman
1
Müssen Sie STATUS wirklich wiederherstellen? In diesem Fall müssen Sie die Anweisung "swapf" aus Gründen verwenden, die an anderer Stelle dokumentiert sind: " P. Anderson ", " Microchip Mid-Range-Familienhandbuch: Abschnitt 8.5 Speichern des Kontexts ", " Speichern des PIC W und STATUS "
davidcary
0

Im Folgenden wird beschrieben, wie Sie dies mithilfe der Assembly implementieren. Greifen Sie mit der Formatierung auf denselben Code zu (Links zu Pastebin) . Wie kann es verbessert werden? Dies ist mein erstes Programm in der PIC-Assembly. Jeder Kommentar wird geschätzt.

list p=16f616
#include p16f616.inc

;*** Configuration Bits ***
__CONFIG _FOSC_INTOSCIO & _WDTE_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _IOSCFS_8MHZ & _BOREN_ON
;**************************

;*** Variable Definitions ***
VARS        UDATA                   ; Define undefined data(s).
numOfTasks  res     1               ; This variable holds the number of tasks multiplied by 2.
currentTask res     1               ; Index variable that points to the current task's index in "tasks"
tasks       res     4               ; This is task "array". Every task occupies 2 bytes.
;****************************

;*** Reset Vector ***
RESET   CODE    0x0000              ; Define a code block starting at 0x0000, which is reset vector, labeled "RESET"
        goto    start               ; Start the program.
;********************

;*** Main Code ***
MAIN    CODE
start                               ; Label the start of the program as "start".
        banksel TRISA               ; Select appropriate bank for TRISA.
        clrf    TRISA               ; Clear TRISA register. Configure all of the PORTA pins as digital outputs.
        clrf    TRISC               ; Clear TRISC register. TRISC and TRISA are at the same bank, no need for "banksel".
        clrf    ANSEL               ; Clear ANSEL register and configure all the analog pins as digital i/o.
        banksel PORTA               ; Select appropriate bank for PORTA.
        clrf    PORTA               ; Clear PORTA register.
        clrf    PORTC               ; Clear PORTC register. PORTC and PORTA are at the same bank, no need for "banksel".


        movlw   0x04                ; W = Number of tasks * 2.
        movwf   numOfTasks          ; Since every task has two datas in it, we will multiply by 2.
        clrf    currentTask         ; Set the task#0 as current task.

        CALL    task0               ; Call task#0 since we need to initialize it. We are going to get..
                                    ; ..its PCL and PCLATH values at the start address.
        movlw   0x02                ; W = 2
        addwf   currentTask, f      ; Increment currentTask by 2, since every task occupies 2 places.

        CALL    task1               ; Call task#1, for initialazation.

taskswitcher
        movlw   0x02                ; W = 2
        addwf   currentTask, f      ; Add 2 to currentTask, store it in currentTask.
        movf    numOfTasks, w       ; W = numOfTasks
        subwf   currentTask, w      ; w= f - w
        btfsc   STATUS, 0           ; If currentTask >= numOfTasks
        clrf    currentTask         ; Clear currentTask

        movlw   tasks               ; Store the address of tasks, which is the start address of our task "array".
        addwf   currentTask, w      ; Add current task's index to the start address.
                                    ; For example; task1's index is 2:  [task0_1][task0_2][task1_1][task1_2]....
                                    ;                                       0        1        2        3
        movwf   FSR                 ; We have the index of current task in W. Copy it to FSR
        movf    INDF, w             ; Copy the contents of current task's first item to W
        movwf   PCLATH              ; Copy W to PCLATH. As a result, current task's PCLATH will be in PCLATH register.

        incf    FSR, f              ; Increment index, so that we will point to the next item of current task.
        movf    INDF, w             ; Copy the contents of current task's second item to W.
        movwf   PCL                 ; Copy W to PCL. Finally, current task's PCL will be in PCL register.

        goto    $                   ; This instruction is not effective. But, enter the endless loop.

;*** TASK 0 ***
TASK0   CODE
;**************
task0
        movlw   tasks               ; Store the address of tasks, which is the start address of our task "array".
        addwf   currentTask, w      ; Add current task's index to the start address.

        movwf   FSR                 ; We have the index of current task in W. Copy it to FSR
        movf    PCLATH, w           ; Copy PCLATH register's contents to W register.
        movwf   INDF                ; Copy W to current task's first item. We now store PCLATH.

        incf    FSR,f               ; Increment index, so that we will point to the next item of current task.
        movlw   low($+3)            ; Copy PCL+3 to W register. This will let us save the PCL of the start of the task.
        movwf   INDF                ; Copy W to task's next item. With that, we will initialize the current task.
        return                      ; We have gathered our initialazation information. Return back to main.

task0main
        banksel PORTA               ; Select the appropriate bank for PORTA
        movlw   0xAA                ; Move literal to W so that W = 0xAA
        movwf   PORTA               ; PORTA = 0xAA. Use a LATA register to create more robust code.

        movlw   tasks               ; Store the address of tasks, which is the start address of our task "array".
        addwf   currentTask, w      ; Add current task's index to the start address.

        movwf   FSR                 ; We have the index of current task in W. Copy it to FSR
        movf    PCLATH, w           ; Copy PCLATH register's contents to W register.
        movwf   INDF                ; Copy W to current task's first item. We now store PCLATH of the current state of the task.

        incf    FSR,f               ; Increment index, so that we will point to the next item of current task.
        movlw   low($+3)            ; Copy PCL+3 to W register. This will let us save the PCL of the current state of the task.
        movwf   INDF                ; Copy W to task's next item. With that, we will initialize the current task.

        goto    taskswitcher        ; Yield the CPU to the awaiting task by going to task switcher.

        banksel PORTA               ; Select the appropriate bank for PORTA
        movlw   0x55                ; Move literal to W so that W = 0x55
        movwf   PORTA               ; PORTA = 0xAA. Use a LATA register to create more robust code.

        goto    task0main           ; Loop by going back to "task0main". We will continuously toggle PORTA.

;*** TASK 1 ***
TASK1   CODE
;**************
task1
        movlw   tasks               ; Store the address of tasks, which is the start address of our task "array".
        addwf   currentTask, w      ; Add current task's index to the start address.

        movwf   FSR                 ; We have the index of current task in W. Copy it to FSR
        movf    PCLATH, w           ; Copy PCLATH register's contents to W register.
        movwf   INDF                ; Copy W to current task's first item. We now store PCLATH.

        incf    FSR,f               ; Increment index, so that we will point to the next item of current task.
        movlw   low($+3)            ; Copy PCL+3 to W register. This will let us save the PCL of the start of the task.
        movwf   INDF                ; Copy W to task's next item. With that, we will initialize the current task.
        return                      ; We have gathered our initialazation information. Return back to main.

task1main
        banksel PORTA               ; Select the appropriate bank for PORTA
        movlw   0xAA                ; Move literal to W so that W = 0xAA
        movwf   PORTA               ; PORTA = 0xAA. Use a LATA register to create more robust code.

        movlw   tasks               ; Store the address of tasks, which is the start address of our task "array".
        addwf   currentTask, w      ; Add current task's index to the start address.

        movwf   FSR                 ; We have the index of current task in W. Copy it to FSR
        movf    PCLATH, w           ; Copy PCLATH register's contents to W register.
        movwf   INDF                ; Copy W to current task's first item. We now store PCLATH of the current state of the task.

        incf    FSR,f               ; Increment index, so that we will point to the next item of current task.
        movlw   low($+3)            ; Copy PCL+3 to W register. This will let us save the PCL of the current state of the task.
        movwf   INDF                ; Copy W to task's next item. With that, we will initialize the current task.

        goto    taskswitcher        ; Yield the CPU to the awaiting task by going to task switcher.

        banksel PORTA               ; Select the appropriate bank for PORTA
        movlw   0x55                ; Move literal to W so that W = 0x55
        movwf   PORTA               ; PORTA = 0xAA. Use a LATA register to create more robust code.

        goto    task1main           ; Loop by going back to "task1main". We will continuously toggle PORTA.

        END                         ; END of the program.
abdullah kahraman
quelle
Ihr erstes Programm in der Montage ist ein Multitasking-RTOS? Beeindruckend. Den meisten Menschen geht es wirklich gut, wenn sie eine LED zum Blinken bringen können. :-).
Davidcary
Nun, eigentlich ist dies mein erstes Assembly-Programm in der PIC-Architektur . Davor habe ich an der Universität 8086 Kurse besucht, aber sie waren nicht praktisch und ich musste selbst lernen, weil der Dozent ein Ersatz war und nichts wusste, aber in den Prüfungen schwierige Fragen stellte.
abdullah kahraman