initialise le pilote en C avec le compilateur xc8 avec les broches gpio

Disons que j’ai un pilote “foo.h” et “foo.c“. Le pilote interagit avec les broches gpio d’un microcontrôleur.

Dans Arduino, chaque broche GPIO a son propre numéro de broche (juste un entier). Ensuite, vous pouvez initialiser foo avec la fonction initFoo (1) ou initFoo (2), pour choisir la broche que vous souhaitez utiliser.

Dans le compilateur Mbed Online c ++, chaque GPIO peut être contrôlé avec un object DigitalIn ou DigitalOut.

Dans JAL (Just Another Language) que je connais aussi bien, ils utilisent le mot clé alias.

alias x is pin_D3 -- the gpio pin called x used by the library pin_D3_direction = output include foo -- jal doesn't use header files. foo_init() 

Comment obtenir des fonctionnalités similaires avec le compilateur Microchip xc8 c? J’ai essayé l’approche JAL avec le mot-clé define, mais le compilateur ne sait pas ce que ‘x’ est. Il dit identifiant non défini ‘x’ …?

 #define x PortDbits.RD0 #include "foo.h" #foo_init(); 

Il y a deux solutions que j’ai utilisées. Rappelez-vous que xc8 dans la version lite est un compilateur horrible et produit un étrange, long, non optimisé, rempli de code assembleur d’horreur, alors passez immédiatement à gcc + stm32 et jetez xc8 à la corbeille.

Vous pouvez créer un conteneur / une classe personnalisé pour interagir avec l’épingle, comme le fait Arduino avec les classes DigitalIn. Nous pouvons stocker une poignée sur une broche en utilisant un pointeur sur le registre PORTA ou PORTB ou PORTX et une position de bit à l’intérieur de ce registre. Ensuite, nous pouvons calculer le masque de bits nécessaire pour définir la broche en utilisant des opérations de bits simples. Au début de ma programmation, je l’ai fait et vous pouvez parcourir le code source pinpointer.h . Fondamentalement, cela fonctionne comme ceci:

 pinpointer_t pp = PP_RB7; // this is PORTBbits.RB7 PP_SET_PIN_AS_OUTPUT(pp); // set RB7 as output, equal to TRISBbits.RB7 = 0 PP_WRITE_PIN(pp, 1); // output 1 on this pin, equal to PORTBbits.RB7 = 1 

Un pinpointer_t a 2 octets. Le premier octet est la position de {PORT,LAT,TRIS}{A,B,C,...} . Sachant que la micropuce produit des microcontrôleurs pour que les registres de ports soient les uns après les autres. PORTB - PORTA = 1 le premier octet stocke le numéro à append à PORTA pour obtenir l’adresse du port. Nous pourrions stocker un pointeur ici, mais cela donnerait à pinpointer_t 4 octets ou plus. Le deuxième octet stocke la position de la broche à l’aide d’un masque de bits. Maintenant, PP_RB7 est égal à 0x180 , c’est-à-dire. le deuxième port PORTA + 1 = PORTB avec un masque de PORTA + 1 = PORTB 0x80, c’est le septième bit. PP_SET_PIN_AS_OUTPUT(PP_RB7) traduit approximativement par smth comme ((TRISA) + (PP_RB7>>8)) &= ~PP_RB7 ) .
Sachant que le numéro de broche n’est jamais supérieur à 7 et qu’il n’ya jamais plus de 7 ports, nous pouvons stocker la même information en utilisant un seul octet (4 premiers bits pour le port, les 4 derniers bits pour la broche), mais j’ai découvert que le XC8 libre Le compilateur de version génère du code **** pour les opérations de décalage telles que PP_RB7>>4 et l’utilisation de 2 octets entraîne généralement un code plus petit.
Donc, votre bibliothèque foo peut ressembler à ceci:

 // foo.c void foo_init(void) { PP_SET_PIN_AS_OUTPUT(foo_pin); } // foo.h extern pinpointer_t foo_pin; void foo_init(void); // main.c pinpointer_t foo_pin = PP_RB7; void main() { foo_init(void); } 

Mais c’est mieux sans variables globales:

 // foo.c void foo_init(struct foo_s *foo, pinpointer_t pin) { foo->pin = pin; PP_SET_PIN_AS_OUTPUT(foo->pin); } // foo.h struct foo_s { pinpointer_t pin; }; void foo_init(struct foo_s *foo, pinpointer_t pin); // main.c void main() { struct foo_s foo; foo_init(&foo, PP_RB7); } 

L’autre solution consiste à utiliser des macros. Les macros produisent un code nettement plus petit et plus rapide, mais leur maintenance est horrible. Je *-config fichier *-config commun, car les macros ne sont pas déterminées par le compilateur, mais par le processus. Les erreurs sont communes. Je le ferais comme ça:

 // foo.c #include  #ifndef FOO_CALLBACK_SET_PIN_AS_OUTPUT #error You need to define FOO_CALLBACK_SET_PIN_AS_OUTPUT #endif void foo_init(void) { FOO_CALLBACK_SET_PIN_AS_OUTPUT(); } // foo.h #include  void foo_init(void); // foo-config.h #define FOO_CALLBACK_SET_PIN_AS_OUTPUT do{ TRISBbits.RB7 = 1; }while(0) // main.c #include  int main() { foo_init(); } 

Le fichier foo-config.h est un fichier créé avec votre projet qui contient les définitions des macros utilisées par la bibliothèque foo. Une fois, j’ai écrit une bibliothèque pour hd44780 qui, pour toutes les opérations gpio / pin, utilisait des rappels de macros. L’application doit append un chemin d’inclusion au processus de compilation avec la configuration .

C’est parce que le ‘x’ n’est vraiment pas défini dans la scope de la bibliothèque foo.

Cela ne fonctionne que lorsque vous avez directement inclus le fichier source (.c) qui n’a pas besoin de fichier d’en-tête.

 #define x PortDbits.RD0 #include "foo.c" #foo_init(); 

Il est préférable que vous ayez un autre en-tête pour définir les broches GPIO. Par exemple, GPIO_x.h

Puis à l’intérieur.

 #ifndef GPIO_X_H #define GPIO_X_H #include "MCUXXXXX.h" #define x PortDbits.RD0 #endif 

après cela, il suffit d’inclure ce fichier dans foo.c

 //this is foo.c #include "GPIO_x.h #include "foo.h" /* your codes */