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?
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:
(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
la source
Je suis fondamentalement d'accord avec davidcary. Il semble que cela pourrait fonctionner.
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.
la source
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.
la source
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
STATUS
registre, 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.
Et voici le fichier d'en-tête
RTOS.h
:la source
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é.
la source