Z jakiego powodu moje wielozadaniowe jądro RTIC PIC16 nie działa?

11

Próbuję stworzyć pół-wyprzedzający (współpracujący) RTOS dla mikrokontrolerów PIC x16. W poprzednim pytaniu dowiedziałem się, że dostęp do wskaźnika stosu sprzętu nie jest możliwy w tych rdzeniach. Przejrzałem stronę na liście PIClist i właśnie to próbuję wdrożyć za pomocą C.

Mój kompilator to Microchip XC8 i obecnie pracuję na PIC16F616 z wewnętrznym oscylatorem RC 4MHz wybranym w bitach konfiguracyjnych.

Nauczyłem się, że mogę uzyskać dostęp do rejestrów PCLATH i PCL za pomocą C, patrząc na plik nagłówka mojego kompilatora. Próbowałem więc zaimplementować prosty przełącznik zadań.

Działa tak, jak chciałem w debuggerze, jeśli zatrzymam debuger po ponownym uruchomieniu, zresetowaniu i ustawieniu komputera na kursor, gdy kursor nie znajduje się w pierwszej linii ( TRISA=0;), ale w innej linii (na przykład ANSEL=0;). Przy pierwszym uruchomieniu debugera otrzymuję te wiadomości w Debugger Console:

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

Edycja: Nie wiem, co sprawiło, że to działało, ale debugger działa teraz idealnie. Pomiń powyższe wyjście i akapit.

Edycja: Zmiana głównej definicji w ten sposób powoduje, że poniższy kod działa. Uruchamia to główną funkcję pod adresem programu 0x0099. Nie wiem co to powoduje. To nie jest prawdziwe rozwiązanie. Zgaduję, że wystąpił błąd specyficzny dla kompilatora.

void main(void) @ 0x0099
{

Oto mój kod C:

/* 
 * 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
    }
}

A oto plik z listą dezasemblacji, który utworzył mój kompilator. Zaczyna się o line 74.

Zaprogramowałem rzeczywisty układ i wcale nie zmieniłem PORTA; to nie działa.

Z jakiego powodu mój program nie działa?

abdullah kahraman
źródło

Odpowiedzi:

10

To, co próbujesz zrobić, jest podchwytliwe, ale bardzo pouczające (jeśli jesteś gotów poświęcić dużo wysiłku).

Po pierwsze, musisz zdać sobie sprawę, że tego rodzaju przełączanie zadań tylko na PC (w przeciwieństwie do PC + SP) (co jest jedyną rzeczą, którą możesz zrobić na zwykłym 12 lub 14-bitowym rdzeniu PIC) będzie działać tylko wtedy, gdy cała wydajność ( ) instrukcje w zadaniu mają tę samą funkcję: nie mogą znajdować się w wywoływanej funkcji, a kompilator nie może mieć pomieszanych struktur funkcji (jak może to zrobić optymalizacja).

Kolejny:

currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
  • Wydaje się, że zakładasz, że PCLATH to górne bity licznika programu, ponieważ PCL to dolne bity. Nie o to chodzi. Kiedy piszesz do PCL, bity PCLATH są zapisywane na PC, ale górne bity PC nigdy nie są (domyślnie) zapisywane w PCLATH. Ponownie przeczytaj odpowiednią sekcję arkusza danych.
  • Nawet jeśli PCLATH był górnymi bitami komputera, spowodowałoby to kłopoty, gdy instrukcja po goto nie jest na tej samej „stronie” 256 instrukcji jak pierwsza instrukcja.
  • zwykłe goto nie będzie działać, gdy _taskswitcher nie znajduje się na bieżącej stronie PCLATH, będziesz potrzebować LGOTO lub odpowiednika.

Rozwiązaniem problemu PCLATH jest zadeklarowanie etykiety po goto i zapisanie dolnych i górnych bitów tej etykiety w lokalizacjach pch i pcl. Ale nie jestem pewien, czy można zadeklarować etykietę „lokalną” w zestawie wbudowanym. Na pewno możesz w zwykły sposób MPASM (Olin się uśmiechnie).

Wreszcie, do tego rodzaju przełączania kontekstu należy zapisać i przywrócić WSZYSTKIE konteksty, od których może zależeć kompilator, co może obejmować

  • rejestry pośrednie
  • flagi stanu
  • lokalizacje pamięci scratch
  • zmienne lokalne, które mogą się nakładać w pamięci, ponieważ kompilator nie zdaje sobie sprawy, że zadania muszą być niezależne
  • inne rzeczy, których nie mogę sobie teraz wyobrazić, ale autor kompilatora mógłby użyć w następnej wersji kompilatora (zwykle są bardzo pomysłowe)

Architektura PIC jest pod tym względem bardziej problematyczna, ponieważ wiele zasobów jest zapętlonych w całej mapie pamięci, gdzie bardziej tradycyjne architektury mają je w rejestrach lub na stosie. W rezultacie kompilatory PIC często nie generują kodu ponownego wysyłania, co jest zdecydowanie tym, czego potrzebujesz, aby robić to, co chcesz (ponownie, Olin prawdopodobnie uśmiechnie się i zgromadzi).

Jeśli interesuje Cię pisanie przełącznika zadań, sugeruję przejście do procesora o bardziej tradycyjnej organizacji, takiego jak ARM lub Cortex. Jeśli utkniesz stopami w betonowej płycie PIC, sprawdź istniejące przełączniki PIC (na przykład salwa / dynia?).

Wouter van Ooijen
źródło
Dzięki za wspaniałe informacje! Jestem zdeterminowany, aby stworzyć spółdzielczy przełącznik zadań. XC8 i PIC nie są po mojej stronie, jestem tego świadomy :) Tak, jak widzicie, możliwe jest tworzenie etykiet, tak jak to zrobiłem w jednej z moich odpowiedzi na to pytanie.
abdullah kahraman
Ponadto, na szczęście, nie ma stronicowania pamięci programu dla PIC16F616, nad którym pracuję, co jest wielką zaletą w tym momencie, prawda?
abdullah kahraman
Czy możesz wyjaśnić więcej, w jaki sposób zmienne lokalne zachodzą na siebie w pamięci, a także „porysować lokalizacje pamięci”?
abdullah kahraman
Jeśli ograniczysz się do chipów z kodem 2K lub mniejszym, rzeczywiście możesz zapomnieć o lgoto, ale nie o „stronach” instrukcji 256. Scratch: kompilator może założyć, że wszystko, co robi w pamięci, pozostaje na swoim miejscu, chyba że jest „niestabilne”. Może więc umieścić częściowe obliczenia w niektórych lokalizacjach, które mogą być współdzielone przez różne funkcje . Ovelap: jeśli main () wywołuje zarówno f ​​(), jak i g () (i nie ma innych wywołań), lokalne zmienne f () ig () można odwzorować na te same lokalizacje pamięci.
Wouter van Ooijen
Cóż, wygląda na to, że prawie niemożliwe jest dotarcie do tych zmiennych i zapisanie ich z powodu ich przypadkowego miejsca w pamięci, prawda?
abdullah kahraman
7

Przejrzałem listę zgromadzeń, którą dostarczyłeś, i nic nie wyskakuje jak najwyraźniej zepsute.

Gdybym był tobą, moje następne kroki to:

(1) Wybrałbym inną metodę mrugania diod LED. Notoryczny „problem odczytu / modyfikacji / zapisu” może (ale nie musi) być wywołany przez „XORWF PORTA, F” na liście zespołów.

Być może coś takiego:

// 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
    }
}

(Jeśli naprawdę chcesz zobaczyć szczegółowe wyjaśnienia, dlaczego „XORWF PORTA, F” często powoduje problemy, zobacz „ Co powoduje włączenie pojedynczego styku wyjściowego w Microchip PIC16F690, aby spontanicznie wyłączyć kolejny styk na tym samym porcie? ”; „ Co się stanie kiedy dane są zapisywane w LATCH? ”;„ Problem odczytu-modyfikacji / zapisu ”;„ Przeczytaj przed zapisem ”)

(2) Przejrzałbym kod krok po kroku, upewniając się, że zmienne są ustawione na oczekiwane wartości oraz w oczekiwanej kolejności. Nie jestem pewien, czy istnieje jednoetapowy debugger sprzętowy dla PIC16F616, ale istnieje wiele doskonałych symulatorów mikrokontrolerów PIC, takich jak PICsim, które mogą symulować układy serii PIC16 .

Kod jednostopniowy (w symulatorze lub z jednoetapowym debugerem sprzętowym) to dobry sposób na zrozumienie szczegółów tego, co się naprawdę dzieje, potwierdzenie, że wszystko dzieje się zgodnie z zamierzeniem, i pozwala zobaczyć rzeczy, które są praktycznie niemożliwe do zobaczenia podczas uruchamiania programu z pełną prędkością.

(3) Jeśli nadal mam problemy, spróbuję przetłumaczyć kod, aby używał tablic zamiast wskaźników. Niektórzy uważają, że używanie wskaźników jest nieco trudne i trudne do debugowania. I często, że w procesie tłumaczenia trudne kod wskaźnika do tablicy zorientowanych kodu, dowiem się, na czym polega błąd. Nawet jeśli w końcu powrócę do pierwotnego kodu wskaźnika i wyrzucę wersję tablicy, ćwiczenie jest przydatne, ponieważ pomogło mi znaleźć i naprawić błąd. (Czasami kompilator może generować krótszy, szybszy kod z kodu zorientowanego na tablicę, więc czasami wyrzucam oryginalny kod wskaźnika i zachowuję wersję tablicy).

Może coś takiego

// 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
źródło
Wdrażam teraz tablice. Dziękuję za rekomendację.
abdullah kahraman
3

Zasadniczo zgodziłbym się z Davidcary. Wygląda na to, że może działać.

Nie wiem, co sprawiło, że działało, ale debugger działa teraz doskonale.

Zgaduję, że masz na myśli, że działa idealnie w symulatorze .

1) Sprawdź, czy Twoje zadania działają samodzielnie, w środowisku innym niż RTOS w prawdziwym układzie scalonym.

2) Wykonaj debugowanie w obwodzie. Przejdź przez program na prawdziwym chipie i obserwuj wszystkie odpowiednie zmienne, aby mieć pewność, że wszystko idzie zgodnie z planem.

Rocketmagnet
źródło
Tak, miałem na myśli debugger, czyli symulator MPLABX. Zadania działają samodzielnie w środowisku innym niż RTOS. Nie mam ICD. Mam tylko mikroElektronika easyPIC5 z ICD, jednak działa tylko z kompilatorem mikroC. Teraz zmiana kompilatorów nie pozwoli mi znaleźć problemu, czy może?
abdullah kahraman
1

Patrzyłem tylko na twój kod krótko, ale to nie ma sensu. W kilku miejscach piszesz do PCL, a następnie spodziewasz się, że wyda inne instrukcje.

Jak już powiedziałem wcześniej, C jest nieodpowiedni do tego rodzaju dostępu do podstawowych rejestrów sprzętowych na niskim poziomie. Naprawdę musisz do tego użyć zestawu. Próbowanie wyjaśnienia, dlaczego kod C nie działa, jest po prostu bezcelową stratą czasu.

Olin Lathrop
źródło
Nie mogłem połączyć montażu i C. Musiałem wykonać dużo pracy. Zarówno dezasemblacja, jak i kod C wydają mi się logiczne. Gdzie masz na myśli, że oczekuję wykonania instrukcji następujących po zapisie do PCL? Obejrzałem debugger zarówno do montażu, jak i C, i działa zgodnie z życzeniem.
abdullah kahraman
Przepraszam za -1. Powinienem był naciskać przypadkowo i teraz to zauważyłem.
abdullah kahraman
@abdullah: Na komputerze, na którym teraz jestem, nie widzę kodu źródłowego. Jest trwale zwinięty w przeglądarce. Pamiętam, że przypisałeś rzeczy do PCLATH, potem PCL, a potem myślę, że w jednym przypadku próbowałeś wykonać ZWROT. Jak tylko napiszesz do PCL, wykonanie przeskoczy na adres, który umieściłeś w PCLATH: PCL, więc wszelkie poniższe instrukcje są nieistotne. Naprawdę nie jest dobrze robić to w C, ponieważ masz problemy z zasobami zarządzanymi przez kompilator, a tym samym prawdopodobnie unieważniasz założenia kompilatora. Użyj już prawdziwego zestawu. Mam już dość powtarzania tego.
Olin Lathrop,
1
Patrząc na kod, nigdzie nie zmienia się PCL tuż przed kolejną instrukcją. Jedyne miejsce, które wydaje się być modyfikowane, znajduje się na samym końcu main (). Ale dobrze, że musisz być bardzo pewny, że nie walczysz z kompilatorem o jego zasoby. Oboje stracicie.
Rocketmagnet
3
C jest całkowicie akceptowalny dla tego rodzaju pracy, a właściwie lepiej jest pisać w języku średniego poziomu, takim jak C, niż w języku asemblera, ponieważ jest łatwiejszy do odczytania i utrzymania. Przyzwoity kompilator wygeneruje kod niezbyt daleko od tego, co i tak napisze przeciętna osoba. Zazwyczaj piszę asembler tylko dla bardzo podstawowego kodu startowego, w określonych obszarach, w których mogę zoptymalizować lepiej niż kompilator lub dla szybkich przerwań, lub jeśli wymagają tego ograniczenia rozmiaru kodu. W dzisiejszych czasach nie ma potrzeby czystego montażu.
akohlsmith,
1

Poniżej znajduje się sposób, aby to zrobić z wbudowanym zestawem za pomocą kompilatora XC8 i działa teraz! Muszę jednak dodać więcej kodu do zapisywania i przywracania STATUSrejestru, co wydaje się nieco trudniejsze niż w przypadku zwykłego rejestru.

Edycja: Kod został zmieniony. Poprzedni kodzawiera starsze wersje tego postu.

/*
 * 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.
    }
}

A oto plik nagłówka 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
źródło
Wygląda na to, że wygrasz swoją nagrodę. Gratulacje! :-)
stevenvh
@stevenvh Ah, czy to się dzieje, nie wiedziałem? Dzięki :)
abdullah kahraman
Gratulacje za uruchomienie go!
davidcary,
Dzięki @davidcary! Naprawdę doceniam wasze gratulacje chłopaki.
abdullah kahraman
1
Czy naprawdę potrzebujesz przywrócić STATUS? Jeśli tak, musisz użyć instrukcji „swapf”, z powodów udokumentowanych gdzie indziej: „ P. Anderson ”, „ Podręcznik rodziny średniego zasięgu Microchip: sekcja 8.5 Zapisywanie kontekstu ”, „ Zapisywanie PIC W i STATUS
Davidcary
0

Poniżej znajduje się sposób zaimplementowania tego za pomocą zestawu. Uzyskaj dostęp do tego samego kodu za pomocą formatowania (linki do Pastebin) . Jak można to poprawić? To mój pierwszy program w montażu PIC, każdy komentarz jest mile widziany.

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
źródło
Twój pierwszy program w asemblerze to wielozadaniowy RTOS? Łał. Większość ludzi ma się naprawdę dobrze, jeśli może migać dioda LED. :-).
davidcary,
To właściwie mój pierwszy program do montażu w architekturze PIC . Wcześniej na uniwersytecie wziąłem 8086 zajęć, ale nie były one praktyczne i musiałem się uczyć sam, ponieważ wykładowca był zastępcą i nic nie wiedział, a jednak zadawał trudne pytania na egzaminach.
abdullah kahraman