Qual è la ragione per cui il mio kernel RTOS multitasking PIC16 non funziona?


11

Sto cercando di creare un RTOS semi-preventivo (cooperativo) per i microcontrollori PIC x16. Nella mia domanda precedente , ho imparato che l'accesso a puntatore stack hardware non è possibile in questi core. Ho guardato questa pagina in PIClist, ed è quello che sto cercando di implementare usando C.

Il mio compilatore è Microchip XC8 e attualmente sto lavorando su un PIC16F616 con un oscillatore RC interno a 4 MHz selezionato nei bit di configurazione.

Ho imparato che posso accedere ai registri PCLATH e PCL con C, guardando il file di intestazione del mio compilatore. Quindi, ho provato a implementare un semplice selettore di attività.

Funziona come desiderato nel debugger se metto in pausa il debugger dopo il riavvio, ripristino e imposto il PC sul cursore quando il cursore non si trova sulla prima riga ( TRISA=0;) ma su un'altra riga (ad esempio ANSEL=0;). Nel primo avvio del debugger ricevo questi messaggi nel Debugger Console:

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

Modifica: non so che cosa l'ha fatto funzionare, ma il debugger ora funziona perfettamente. Quindi, omettere l'output e il paragrafo sopra.

Modifica: la modifica della definizione principale in questo modo fa funzionare il codice seguente. Questo avvia la funzione principale all'indirizzo del programma 0x0099. Non so che cosa causi questo. Questa non è una vera soluzione. Ora sto indovinando che c'è un errore specifico del compilatore.

void main(void) @ 0x0099
{

Ecco il mio codice 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
    }
}

Ed ecco il file dell'elenco di smontaggio creato dal mio compilatore. Inizia a line 74.

Ho programmato il chip reale e nessuna modifica su PORTA; non funziona.

Qual è la ragione per cui il mio programma non funziona?

Risposte:


10

Quello che stai cercando di fare è complicato, ma molto istruttivo (se sei pronto a spendere molto).

Innanzitutto, è necessario rendersi conto che questo tipo di task switching solo PC (al contrario di PC + SP) (che è l'unica cosa che è possibile fare su un semplice core PIC a 12 o 14 bit) funzionerà solo quando tutto il rendimento ( ) le istruzioni in un'attività sono nella stessa funzione: non possono essere in una funzione chiamata e il compilatore non deve aver incasinato la struttura della funzione (come potrebbe fare l'ottimizzazione).

Il prossimo:

currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
  • Sembra che PCLATH sia il bit superiore del contatore del programma, poiché PCL è il bit inferiore. Questo non è il caso. Quando si scrive su PCL i bit PCLATH vengono scritti sul PC, ma i bit PC superiori non vengono mai (implicitamente) scritti su PCLATH. Rileggi la sezione pertinente del foglio dati.
  • Anche se PCLATH era il bit superiore del PC, questo potrebbe creare problemi quando l'istruzione dopo il goto non è sulla stessa pagina di 256 istruzioni della prima istruzione.
  • il semplice goto non funzionerà quando _taskswitcher non si trova nella pagina PCLATH corrente, avrai bisogno di un LGOTO o equivalente.

Una soluzione al tuo problema PCLATH è dichiarare un'etichetta dopo il goto e scrivere i bit inferiore e superiore dell'etichetta nelle posizioni pch e pcl. Ma non sono sicuro che puoi dichiarare un'etichetta "locale" nell'assemblaggio in linea. Sicuramente puoi farlo in MPASM (Olin sorriderà).

Infine, per questo tipo di cambio di contesto è necessario salvare e ripristinare TUTTO il contesto da cui il compilatore potrebbe dipendere, che potrebbe includere

  • registro / i indiretto / i
  • flag di stato
  • posizioni di memoria scratch
  • variabili locali che potrebbero sovrapporsi in memoria perché il compilatore non si rende conto che le attività devono essere indipendenti
  • altre cose che non posso immaginare in questo momento, ma l'autore del compilatore potrebbe usare nella prossima versione del compilatore (tendono ad essere molto fantasiosi)

L'architettura PIC è più problematica da questo punto di vista poiché molte risorse sono contenute in tutta la mappa di memoria, dove architetture più tradizionali le hanno nei registri o nello stack. Di conseguenza, i compilatori PIC spesso non generano un codice rientrante, che è ciò di cui hai sicuramente bisogno per fare le cose che vuoi (di nuovo, Olin sorriderà e assemblerà di nuovo).

Se ti piace questo per la gioia di scrivere uno switcher di attività ti suggerisco di passare a una CPU che ha un'organizzazione più tradizionale, come un ARM o Cortex. Se sei bloccato con i piedi in una lastra di cemento armato di PIC, studia i commutatori PIC esistenti (ad esempio salvo / pumkin?).


Grazie per le informazioni fantastiche! Sono determinato a creare un commutatore di attività cooperativo. XC8 e PIC non sono dalla mia parte, ne sono consapevole :) Sì, come puoi vedere, è possibile creare etichette come ho fatto in una delle mie risposte a questa domanda.
Abdullah Kahraman,

Inoltre, per mia fortuna, non c'è il paging della memoria del programma per il PIC16F616 su cui sto lavorando, questo è un grande vantaggio a questo punto, giusto?
Abdullah Kahraman,

Potresti spiegare di più in che modo le variabili locali si sovrapporranno nella memoria e anche "le posizioni della memoria di memoria virtuale"?
Abdullah Kahraman,

Se ti limiti ai chip con codice 2K o inferiore, puoi davvero dimenticare lgoto, ma non le "pagine" di 256 istruzioni. Scratch: un compilatore può presumere che qualsiasi cosa faccia in memoria rimanga in posizione a meno che non sia "volatile". Quindi potrebbe mettere calcoli parziali in qualche posizione che possono essere condivisi da diverse funzioni . Ovelap: se main () chiama sia f () che g () (e non ci sono altre chiamate), le variabili locali di f () e g () possono essere mappate nelle stesse posizioni di memoria.
Wouter van Ooijen,

Bene, sembra quasi impossibile raggiungere quelle variabili e archiviarle, a causa della loro posizione casuale nella memoria, giusto?
Abdullah Kahraman,

7

Ho sfogliato l'elenco dell'assemblaggio che hai fornito e nulla salta come ovviamente rotto.

Se fossi in te, i miei prossimi passi sarebbero:

(1) Sceglierei un altro metodo per far lampeggiare i LED. Il famigerato "problema di lettura-modifica-scrittura" può (o non può) essere attivato da "XORWF PORTA, F" nell'elenco dell'assemblaggio.

Forse qualcosa del tipo:

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

(Se vuoi davvero vedere spiegazioni dettagliate sul perché "XORWF PORTA, F" spesso causa problemi, vedi " Cosa causa l'attivazione di un singolo pin di uscita su Microchip PIC16F690 per la disattivazione spontanea di un altro pin sulla stessa porta? "; " Cosa succede? quando i dati vengono scritti su LATCH? ";" Il problema di lettura-modifica-scrittura ";" Leggi prima di scrivere ")

(2) Passo attraverso il codice, assicurandomi che le variabili siano impostate sui valori previsti e nella sequenza prevista. Non sono sicuro che esista un debugger hardware a singolo passaggio per PIC16F616, ma ci sono molti eccellenti simulatori di microcontrollori PIC come PICsim in grado di simulare i chip della serie PIC16.

Il codice a passo singolo (in un simulatore o con un debugger hardware a passaggio singolo) è un buon modo per comprendere i dettagli di ciò che sta realmente accadendo, confermare che le cose stiano accadendo nel modo desiderato e che ti consente di vedere le cose che stanno praticamente impossibile da vedere quando si esegue il programma a tutta velocità.

(3) Se sono ancora sconcertato, proverei a tradurre il codice per utilizzare le matrici anziché i puntatori. Alcune persone trovano che usare i puntatori sia un po 'complicato e difficile da eseguire il debug. Trovo spesso che, nel processo di traduzione del codice puntatore complicato in codice orientato all'array, scopro qual è il bug. Anche se finisco per ripristinare il codice del puntatore originale e buttare via la versione dell'array, l'esercizio è utile perché mi ha aiutato a trovare e correggere il bug. (A volte il compilatore può generare codice più breve e più veloce dal codice orientato all'array, quindi ci sono volte in cui lancio il codice del puntatore originale e mantengo la versione dell'array).

Forse qualcosa del genere

// 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");

Sto implementando array ora. Grazie per la raccomandazione
Abdullah Kahraman,

3

Sono sostanzialmente d'accordo con David. Sembra che potrebbe funzionare.

Non so cosa l'abbia fatto funzionare, ma il debugger ora funziona perfettamente.

Suppongo che tu intenda che funzioni perfettamente nel simulatore .

1) Verifica che le tue attività lavorino da sole, in un ambiente non RTOS nel chip reale.

2) Esegui il debug in-circuit. Passa attraverso il programma sul chip reale e osserva tutte le variabili rilevanti per essere sicuro che tutto vada come previsto.


Sì, intendevo il debugger, ovvero il simulatore di MPLABX. Le attività funzionano da sole, in un ambiente non RTOS. Non ho un ICD. Ho solo mikroElektronika easyPIC5 con ICD, tuttavia, funziona solo con il compilatore mikroC. Ora, la modifica dei compilatori non mi permetterà di trovare il problema, o lo sarà?
Abdullah Kahraman,

1

Ho esaminato il tuo codice solo brevemente, ma non ha senso. In diversi punti stai scrivendo su PCL, quindi ti aspetti che segua altre istruzioni.

Come ho già detto prima, C non è appropriato per questo tipo di accesso a basso livello dei registri hardware fondamentali. È davvero necessario utilizzare assembly per questo. Cercare di capire perché il codice C non funziona è solo una inutile perdita di tempo.


Non riuscivo a combinare assemblaggio e C. dovevo fare molto lavoro. Sia il disassemblaggio che il codice C mi sembrano logici. Dove ti riferisci che mi aspetto di eseguire le istruzioni che seguono una scrittura su PCL? Ho visto il debugger sia per assembly che per C, e funziona come desiderato.
Abdullah Kahraman,

Ci scusiamo per il -1. Avrei dovuto premere per caso e l'ho notato ora.
Abdullah Kahraman,

@abdullah: Sulla macchina che sono ora, non riesco a vedere il codice sorgente. È permanentemente compresso nel browser. Ricordo che hai assegnato cose a PCLATH, quindi a PCL, quindi penso che in un caso abbia tentato di fare un RITORNO. Non appena scrivi su PCL, l'esecuzione salterà all'indirizzo che hai inserito in PCLATH: PCL, quindi le seguenti istruzioni sono irrilevanti. Non è davvero utile farlo in C perché si scherza con le risorse gestite dal compilatore e quindi si possono invalidare le ipotesi del compilatore. Usa già un vero assemblaggio. Mi sto stancando di doverlo ripetere.
Olin Lathrop l'

1
Guardando il codice, non c'è nessun posto in cui PCL viene modificato appena prima di un'altra istruzione. L'unico posto in cui sembra essere modificato è alla fine di main (). Ma è un buon punto che devi essere molto sicuro di non combattere il compilatore per le sue risorse. Entrambi perderete.
Rocketmagnet,

3
C è perfettamente accettabile per questo tipo di lavoro, ed in effetti è preferibile scrivere in un linguaggio di medio livello come C rispetto al linguaggio assembly perché è più facile da leggere e mantenere. Un compilatore decente genererà codice non troppo lontano da ciò che la persona media scriverà comunque. In genere scrivo solo assemblatore per il codice di avvio molto semplice, aree specifiche in cui posso ottimizzare meglio del compilatore o per interruzioni veloci, o se i vincoli di dimensione del codice lo dettano. Oggigiorno non è molto necessario un assemblaggio puro.
akohlsmith,

1

Di seguito è riportato il modo di farlo con l'assemblaggio in linea utilizzando il compilatore XC8 e ora funziona ! Tuttavia, devo aggiungere sviluppare più codice per salvare e ripristinare il STATUSregistro, che sembra un po 'più complicato di quanto non sia per un registro normale.

Modifica: il codice è cambiato. Si prega di fare riferimento alle versioni precedenti di questo post per il codice precedente.

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

Ed ecco il file di intestazione 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 */

Sembra che vincerai la tua taglia. Congratulazioni! :-)
stevenvh,

@stevenvh Ah, succede, non lo sapevo? Grazie :)
Abdullah Kahraman,

Congratulazioni per averlo fatto funzionare!
davidcary,

Grazie @davidcary! Apprezzo davvero i tuoi complimenti ragazzi.
Abdullah Kahraman,

1
Hai davvero bisogno di ripristinare STATUS? In tal caso, dovrai utilizzare l'istruzione "swapf", per motivi documentati altrove: " P. Anderson ", " Manuale della famiglia di fascia media Microchip: sezione 8.5 Salvataggio del contesto ", " Risparmio PIC W e STATUS "
davidcary

0

Di seguito è riportato come implementarlo utilizzando assembly. Accedi allo stesso codice con la formattazione (collegamenti a Pastebin) . Come può essere migliorato? Questo è il mio primo programma nell'assemblaggio PIC, ogni commento è apprezzato.

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.

Il tuo primo programma in assembly è un RTOS multi-tasking? Wow. La maggior parte delle persone sta andando davvero bene se riesce a far lampeggiare un LED. :-).
davidcary,

Bene, in realtà questo è il mio primo programma di assemblaggio nell'architettura PIC . Prima di allora, all'università, ho seguito 8086 lezioni, ma non erano pratiche e ho dovuto imparare da solo perché il docente era un sostituto e non sapeva nulla, eppure
poneva
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.