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?
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:
(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
quelle
Ich würde Davidcary grundsätzlich zustimmen. Es sieht so aus, als könnte es funktionieren.
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.
quelle
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.
quelle
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
STATUS
Registers 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.
Und hier ist die Header-Datei
RTOS.h
:quelle
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.
quelle