Quelle est la raison pour laquelle mon noyau RTOS multitâche PIC16 ne fonctionne pas?


11

J'essaie de créer un RTOS semi-préemptif (coopératif) pour les microcontrôleurs PIC x16. Dans ma question précédente , j'ai appris que l'accès au pointeur de pile matérielle n'est pas possible dans ces cœurs. J'ai regardé cette page dans PIClist, et c'est ce que j'essaie de mettre en œuvre en utilisant C.

Mon compilateur est Microchip XC8 et actuellement je travaille sur un PIC16F616 avec un oscillateur RC interne à 4 MHz sélectionné dans les bits de configuration.

J'ai appris que je peux accéder aux registres PCLATH et PCL avec C, en regardant le fichier d'en-tête de mon compilateur. J'ai donc essayé d'implémenter un simple sélecteur de tâches.

Cela fonctionne comme souhaité dans le débogueur si je mets le débogueur en pause après le redémarrage, la réinitialisation et que je place PC sur le curseur lorsque le curseur n'est pas sur la première ligne ( TRISA=0;) mais sur une autre ligne (par exemple ANSEL=0;). Au premier démarrage du débogueur, je reçois ces messages dans Debugger Console:

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

Edit: je ne sais pas ce qui l'a fait fonctionner, mais le débogueur fonctionne maintenant parfaitement. Donc, omettez la sortie et le paragraphe ci-dessus.

Modifier: la modification de la définition principale comme celle-ci fait fonctionner le code ci-dessous. Cela démarre la fonction principale à l'adresse du programme 0x0099. Je ne sais pas ce qui cause ça. Ce n'est pas une vraie solution. Je suppose maintenant qu'il y a une erreur spécifique au compilateur.

void main(void) @ 0x0099
{

Voici mon code 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
    }
}

Et voici le fichier de liste de désassemblage créé par mon compilateur. Commence à line 74.

J'ai programmé la puce réelle et aucun changement sur PORTA; ça ne marche pas.

Quelle est la raison pour laquelle mon programme ne fonctionne pas?

Réponses:


10

Ce que vous essayez de faire est délicat, mais très éducatif (si vous êtes prêt à dépenser beaucoup d'efforts).

Tout d'abord, vous devez comprendre que ce type de commutation de tâche uniquement sur PC (par opposition à PC + SP) (qui est la seule chose que vous pouvez faire sur un cœur PIC 12 ou 14 bits) ne fonctionnera que lorsque tout le rendement ( ) les instructions d'une tâche sont dans la même fonction: elles ne peuvent pas être dans une fonction appelée, et le compilateur ne doit pas avoir gâché la structure de la fonction (comme pourrait le faire l'optimisation).

Prochain:

currentTask->pch = PCLATH;\
currentTask->pcl = PCL + 8;\
asm("goto _taskswitcher");
  • Vous semblez supposer que PCLATH est les bits supérieurs du compteur de programme, comme PCL est les bits inférieurs. Ce n'est pas le cas. Lorsque vous écrivez sur PCL, les bits PCLATH sont écrits sur le PC, mais les bits supérieurs du PC ne sont jamais (implicitement) écrits sur PCLATH. Relisez la section pertinente de la fiche technique.
  • Même si PCLATH était le bit supérieur du PC, cela vous causerait des problèmes lorsque l'instruction après le goto n'est pas sur la même page de 256 instructions que la première instruction.
  • le goto simple ne fonctionnera pas lorsque _taskswitcher n'est pas dans la page PCLATH actuelle, vous aurez besoin d'un LGOTO ou équivalent.

Une solution à votre problème PCLATH consiste à déclarer une étiquette après le goto et à écrire les bits inférieur et supérieur de cette étiquette dans vos emplacements pch et pcl. Mais je ne suis pas sûr que vous puissiez déclarer une étiquette «locale» dans un assemblage en ligne. Vous pouvez certainement en simple MPASM (Olin sourira).

Enfin, pour ce type de changement de contexte, vous devez enregistrer et restaurer TOUS les contextes dont le compilateur peut dépendre, qui peuvent inclure

  • registre (s) d'indirection
  • drapeaux d'état
  • emplacements de mémoire de travail
  • variables locales qui peuvent se chevaucher en mémoire car le compilateur ne se rend pas compte que vos tâches doivent être indépendantes
  • d'autres choses que je ne peux pas imaginer pour le moment mais l'auteur du compilateur pourrait utiliser dans la prochaine version du compilateur (ils ont tendance à être très imaginatifs)

L'architecture PIC est plus problématique à cet égard car de nombreuses ressources sont localisées sur toute la carte mémoire, où les architectures plus traditionnelles les ont dans les registres ou sur la pile. En conséquence, les compilateurs PIC ne génèrent souvent pas de code réentrant, ce dont vous avez absolument besoin pour faire les choses que vous voulez (encore une fois, Olin sourira et s'assemblera probablement.)

Si vous êtes dans ce domaine pour la joie d'écrire un sélecteur de tâches, je vous suggère de passer à un processeur qui a une organisation plus traditionnelle, comme un ARM ou un Cortex. Si vous êtes coincé avec vos pieds dans une plaque en béton de PIC, étudiez les commutateurs PIC existants (par exemple salvo / pumkin?).


Merci pour la grande info! Je suis déterminé à créer un sélecteur de tâches coopératif. Le XC8 et le PIC ne sont pas de mon côté là-dessus, j'en suis conscient :) Oui, comme vous pouvez le voir, il est possible de créer des labels comme je l'ai fait dans une de mes réponses à cette question.
abdullah kahraman

De plus, à ma chance, il n'y a pas de pagination de la mémoire du programme pour le PIC16F616 sur lequel je travaille, c'est un grand avantage à ce stade, non?
abdullah kahraman

Pourriez-vous expliquer plus en détail comment les variables locales se chevauchent en mémoire, ainsi que les "emplacements de mémoire de travail"?
abdullah kahraman

Si vous vous limitez aux puces avec du code 2K ou moins, vous pouvez en effet oublier le lgoto, mais pas les 256 pages d'instructions. Scratch: un compilateur peut supposer que tout ce qu'il fait en mémoire reste en place à moins qu'il ne soit «volatil». Il peut donc placer des calculs partiels dans un emplacement qui peut être partagé par différentes fonctions . Ovelap: si main () appelle à la fois f () et g () (et qu'il n'y a pas d'autres appels), les variables locales de f () et g () peuvent être mappées aux mêmes emplacements de mémoire.
Wouter van Ooijen

Eh bien, il semble qu'il soit presque impossible d'atteindre ces variables et de les stocker, en raison de leur place aléatoire dans la mémoire, non?
abdullah kahraman

7

J'ai parcouru la liste d'assemblage que vous avez fournie, et rien ne saute comme manifestement cassé.

Si j'étais vous, mes prochaines étapes seraient:

(1) Je choisirais une autre méthode pour faire clignoter les LED. Le fameux «problème de lecture-modification-écriture» peut (ou peut ne pas) être déclenché par le «XORWF PORTA, F» dans la liste d'assembly.

Peut-être quelque chose comme:

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

(Si vous voulez vraiment voir des explications détaillées sur les raisons pour lesquelles "XORWF PORTA, F" cause souvent des problèmes, voir " Qu'est-ce qui fait que l'activation d'une seule broche de sortie sur Microchip PIC16F690 désactive spontanément une autre broche sur le même port? "; " Que se passe-t-il lorsque les données sont écrites dans LATCH? ";" Le problème de lecture-modification-écriture ";" Lire avant écriture ")

(2) Je voudrais parcourir le code en une seule étape, en m'assurant que les variables sont définies sur les valeurs attendues et dans la séquence attendue. Je ne sais pas s'il existe un débogueur matériel en une seule étape pour le PIC16F616, mais il existe de nombreux excellents simulateurs de microcontrôleurs PIC tels que PICsim qui peuvent simuler des puces de la série PIC16.

Le code pas à pas (dans un simulateur ou avec un débogueur matériel en une seule étape) est un bon moyen de comprendre les détails de ce qui se passe réellement, de confirmer que les choses se passent comme vous le souhaitiez et il vous permet de voir les choses qui sont pratiquement impossible à voir lors de l'exécution du programme à pleine vitesse.

(3) Si je suis toujours perplexe, j'essaierais de traduire le code pour utiliser des tableaux plutôt que des pointeurs. Certaines personnes trouvent l'utilisation des pointeurs un peu délicate et difficile à déboguer. Je trouve souvent que, dans le processus de traduction de code de pointeur délicat en code orienté tableau, je découvre quel est le bogue. Même si je reviens au code du pointeur d'origine et jette la version du tableau, l'exercice est utile car il m'a aidé à trouver et à corriger le bogue. (Parfois, le compilateur peut générer du code plus court et plus rapide à partir du code orienté tableau, il y a donc des fois où je jette le code du pointeur d'origine et conserve la version du tableau).

Peut-être quelque chose comme

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

J'implémente des tableaux maintenant. Merci pour la recommandation.
abdullah kahraman

3

Je suis fondamentalement d'accord avec davidcary. Il semble que cela pourrait fonctionner.

Je ne sais pas ce qui l'a fait fonctionner, mais le débogueur fonctionne maintenant parfaitement.

Je suppose que vous voulez dire que cela fonctionne parfaitement dans le simulateur .

1) Vérifiez que vos tâches fonctionnent seules, dans un environnement non RTOS dans la vraie puce.

2) Effectuez le débogage en circuit. Parcourez le programme sur la vraie puce et regardez toutes les variables pertinentes pour vous assurer que tout se passe comme prévu.


Oui, je voulais dire le débogueur, c'est le simulateur de MPLABX. Les tâches fonctionnent seules, dans un environnement non RTOS. Je n'ai pas de CIM. Je n'ai que mikroElektronika easyPIC5 avec ICD, cependant, cela ne fonctionne qu'avec le compilateur mikroC. Maintenant, changer de compilateur ne me permettra pas de trouver le problème, n'est-ce pas?
abdullah kahraman

1

Je n'ai regardé que brièvement votre code, mais cela n'a aucun sens. À plusieurs endroits, vous écrivez à PCL, puis vous attendez à ce qu'il excute d'autres instructions après cela.

Comme je l'ai également dit précédemment, C est inapproprié pour ce type d'accès de bas niveau aux registres matériels fondamentaux. Vous devez vraiment utiliser l'assemblage pour cela. Essayer de comprendre pourquoi le code C ne fonctionne pas n'est qu'une perte de temps inutile.


Je ne pouvais pas combiner assemblage et C. J'ai dû faire beaucoup de travail. Le démontage et le code C me semblent logiques. Où voulez-vous dire que je m'attends à exécuter des instructions qui suivent une écriture sur PCL? J'ai regardé le débogueur pour l'assemblage et le C, et cela fonctionne comme souhaité.
abdullah kahraman

Désolé pour le -1. J'aurais dû appuyer accidentellement et je l'ai remarqué maintenant.
abdullah kahraman

@abdullah: Sur la machine où je suis actuellement, je ne vois pas le code source. Il est définitivement effondré dans le navigateur. Je me souviens que vous avez assigné des trucs à PCLATH, puis PCL, puis je pense que dans un cas, vous avez tenté de faire un RETOUR. Dès que vous écrivez sur PCL, l'exécution va passer à l'adresse que vous avez insérée dans PCLATH: PCL, donc les instructions suivantes ne sont pas pertinentes. Ce n'est vraiment pas bon de faire cela en C car vous jouez avec les ressources gérées par le compilateur et ainsi invalidez éventuellement les hypothèses du compilateur. Utilisez déjà un véritable assemblage. J'en ai assez de devoir répéter ça.
Olin Lathrop

1
En regardant le code, il n'y a nulle part où PCL est modifié juste avant une autre instruction. Le seul endroit où il semble être modifié est à la fin de main (). Mais c'est un bon point que vous devez être très sûr que vous ne combattez pas le compilateur pour ses ressources. Vous perdrez tous les deux.
Rocketmagnet

3
C est parfaitement acceptable pour ce type de travail, et en fait, il est préférable d'écrire dans un langage de niveau intermédiaire comme C par opposition au langage d'assemblage car il est plus facile à lire et à maintenir. Un compilateur décent générera du code pas trop loin de ce que la personne moyenne écrira de toute façon. J'écris généralement l'assembleur uniquement pour le code de démarrage très basique, des domaines spécifiques où je peux optimiser mieux que le compilateur ou pour des interruptions rapides, ou si les contraintes de taille de code le dictent. De nos jours, il n'y a pas vraiment besoin d'assemblage pur.
akohlsmith

1

Vous trouverez ci-dessous la façon de le faire avec l'assemblage en ligne à l'aide du compilateur XC8, et cela fonctionne maintenant! Cependant, je dois ajouter développer plus de code pour sauvegarder et restaurer le STATUSregistre, ce qui semble un peu plus compliqué que pour un registre normal.

Modifier: le code est modifié. Veuillez vous référer aux anciennes versions de cet article pour le code précédent.

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

Et voici le fichier d'en-tête 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 */

On dirait que vous allez gagner votre propre prime. Toutes nos félicitations! :-)
stevenvh

@stevenvh Ah, est-ce que ça arrive, je ne savais pas? Merci :)
abdullah kahraman

Félicitations pour l'avoir fait fonctionner!
davidcary

Merci @davidcary! J'apprécie vraiment vos félicitations les gars.
abdullah kahraman

1
Avez-vous vraiment besoin de restaurer le STATUT? Si tel est le cas, vous devrez utiliser l'instruction "swapf", pour des raisons documentées ailleurs: " P. Anderson ", " Microchip Mid-range family manual: section 8.5 Context Saving ", " PIC save W and STATUS "
davidcary

0

Vous trouverez ci-dessous comment implémenter cela à l'aide de l'assembly. Accéder au même code avec mise en forme (liens vers Pastebin) . Comment peut-il être amélioré? Ceci est mon premier programme d'assemblage PIC, tout commentaire est apprécié.

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.

Votre premier programme d'assemblage est un RTOS multitâche? Sensationnel. La plupart des gens se portent très bien s'ils peuvent faire clignoter une LED. :-).
davidcary

Eh bien, en fait, c'est mon premier programme d'assemblage dans l'architecture PIC . Avant cela, à l'université, j'ai suivi 8086 cours mais ils n'étaient pas pratiques et j'ai dû apprendre par moi-même car le professeur était un substitut et ne savait rien, mais posait des questions difficiles aux examens ..
abdullah kahraman
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.