2012-03-17 5 views
0

Je travaille actuellement sur la mise en place d'un framework en C pour l'utilisation entre plusieurs microcontrôleurs. Le cadre doit porter tout le code spécifique à l'appareil, donc l'application ne contient que l'utilisation abstraite des périphériques (comme SerialPort_Read, write, SetBaudRate, etc etc)LUT dans une macro C

Une des choses avec lesquelles je suis aux prises pour trouver une solution pour en C sont la carte des broches d'E/S. J'ai vu des projets (comme l'Arduino très très populaire) où la carte de broche est mise dans une LUT (table de consultation) qui est utilisée pendant l'exécution. Cependant, cette LUT ne sera jamais modifiée pendant l'exécution, il n'est donc pas utile de l'avoir dans la mémoire. Par exemple, cette fonction résout certains indices de bits et registres de quelques tables « const uint », et soit fixe ou efface un peu:

void pinMode(uint8_t pin, uint8_t mode) 
{ 
     uint8_t bit = digitalPinToBitMask(pin); 
     uint8_t port = digitalPinToPort(pin); 
     volatile uint8_t *reg; 

     if (port == NOT_A_PIN) return; 

     // JWS: can I let the optimizer do this? 
     reg = portModeRegister(port); 

     if (mode == INPUT) { 
       uint8_t oldSREG = SREG; 
       cli(); 
       *reg &= ~bit; 
       SREG = oldSREG; 
     } else { 
       uint8_t oldSREG = SREG; 
       cli(); 
       *reg |= bit; 
       SREG = oldSREG; 
     } 
} 

Parce que ce code est réel C en cours d'exécution sur le contrôleur, il est drainant effiency et la vitesse . Je préfère définir une sorte de macro qui fait la même chose, mais est déjà résolu lors de la compilation d'un « one-liner » qui peut être compilé beaucoup plus efficace:

GPIO_Write(PORTA, 5, 1); // Write '1' to pin 5 on PORTA 
> LATA |= 1<<5; // Sets bit 5 high 
GPIO_Tris(PORTA, 4, OUTPUT); // Set pin 4 on PORTA to output 
> PORTA &= ~(1<<4); // sets pin 4 as output I/O type 

Est-ce que quelqu'un sait s'il est possible (et comment) définir et utiliser une table de correspondance avec une macro en C? En ce moment j'utilise le compilateur MicroChip C30, qui, je crois, est basé dans GCC. Il est supposé être portable entre différents compilateurs, y compris MicroChip C18, C32 et en plus également ARM et AVR.

+0

S'il vous plaît ne pas lier au code sur des sites externes. –

+0

Très bien, j'ai enlevé les liens et je l'ai expliqué ici. – Hans

+0

Dans votre exemple de code, que voulez-vous que l'optimiseur fasse? Si le port peut avoir des valeurs différentes au moment de l'exécution, selon la fonction appelée. – blueshift

Répondre

2

Pour votre exemple précis, quelque chose le long de ces lignes fonctionnera:

#define WRITE_PORTA LATA 
#define GPIO_Write(port, pin, value)   \ 
    (value ? WRITE_##port |= (1U << (pin)) \   
      : WRITE_##port &= ~(1U << (pin))) 

#define INPUT 0 
#define OUTPUT 1 
#define GPIO_Tris(port, pin, direction)      \ 
    ((direction) == INPUT ? port |= (1U << (pin)) \ 
          : port &= ~(1U << (pin))) 

Vous devrez vous assurer de définir LATA et PORTA en quelque sorte le système comprendra - notamment en essayant de surcharger son sens la façon dont cela semble être dans votre exemple pourrait être difficile à résoudre.

+0

Cela semble à peu près juste. Ne pensez pas que vous avez besoin de la macro supplémentaire TRIS_, mais que le port PORT ## devrait faire? Peut-être qu'un exemple d'utilisation aiderait le demandeur à l'obtenir. – blueshift

+0

Bon appel - cela va nettoyer les choses. En ce qui concerne un exemple, je les ai fait fonctionner exactement comme le PO a dans sa question. –

+0

Oui, mais vous ne les avez pas * montré * en train de travailler. Si vous n'êtes pas familier aveC##, le demandeur ne peut pas brancher qu'il appelle simplement avec, par exemple, (A, 1, 1). – blueshift

1

Quel processeur ou microcontrôleur ciblez-vous? Vous pourriez sous-estimer l'utilité de la LUT.

Pour de nombreux processeurs, le LUT fait plus que mapper un numéro de broche «logique» à une seule valeur, le numéro de broche «physique». La LUT associe le numéro de broche «logique» à plusieurs informations.

En général, la broche «logique» est mappée sur l'adresse de port du registre de lecture/écriture ou d'écriture/sortie approprié, et le décalage de bit dans le registre de lecture ou d'écriture. Ainsi, la valeur de broche, sur de nombreux MCU, est réellement mappée à une structure. Il peut également inclure un mappage vers le registre de direction de données et les champs qu'il contient, ainsi que des registres qui définissent l'état des résistances de pull-up ou de pull-down.

Par exemple, j'ai le code pour multiplexer un affichage 8x8. Au moment de l'exécution, j'ai besoin d'utiliser pinMode pour transformer une broche d'une sortie en une entrée à haute impédance, et donc cette information doit être encodée d'une manière ou d'une autre.

Il est possible de faire ce genre de chose, avec une certaine ingéniosité, sur certains MCU. ARM MCU (et je crois que 8051, bien que je ne l'ai jamais utilisé un) en utilisant « bande de bit d'adressage » http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0179b/CHDJHIDF.html

Cette attribue une adresse mémoire unique pour chaque broche du port et des décalages fixes peut dériver l'adresse de la broche pour l'autre registre de données, et d'autres fonctions. Ce n'est pas de la magie, le code code les informations qui sont souvent stockées dans le LUT.

Pour les autres MCU, ils ont vraiment besoin d'une position de port et de bit, donc il s'agit de deux valeurs pour chaque numéro de broche. Si vous êtes prêt à abandonner l'idée d'utiliser des entiers pour les pins, et à utiliser à la place des noms, comme P0, P1, alors vous pouvez initialiser beaucoup de const struct, un par nom de broche, et vos fonctions prendraient le const struct valeurs. La structure contiendrait les valeurs initialisées de port et de décalage de bit ou de masque de bits. Le compilateur peut être en mesure d'optimiser la vitesse. Cela éviterait d'avoir une table de conversion, mais utiliserait des espaces similaires pour les broches utilisées. Vous pourriez être en mesure de l'organiser de sorte que les broches inutilisées n'aient pas besoin d'être incluses dans le code, et donc d'économiser de l'espace. Editer: Si vous souhaitez utiliser C++, je suggérerais des templates C++ qui peuvent donner une bien meilleure solution que les macros. Ils peuvent être de type sécurité, et sont souvent plus facile à déboguer (si vous avez le débogage matériel, par exemple JTAG et gdb)

+0

Je suis actuellement en train de cibler les PIC Microchip 16 bits et 32 ​​bits, je ne pense pas qu'ils aient des bandes de bits. Je devrais regarder dans le support de C++, je ne sais pas quelles sont les possibilités exactes de mon environnement sur C++, je le rechercherai. L'index du port est le plus gros problème, le résoudre en un 'unidimensionnel' semble beaucoup plus simple, mais malheureusement beaucoup de structures const sont très similaires à ce que fait Arduino. – Hans

+0

J'ai peur, je connais très peu de PIC 16 bits. Le PIC32 n'a pas de bande de bits, bien qu'il ait des paramètres sournois + rapide, clair et bascule. Je ne sais pas si PIC32 a 16 ou 32 broches d'E/S par adresse de port. L'ARM n'a que 16 broches d'E/S/adresse de port, il est donc possible d'empaqueter deux morceaux de données dans un mot de 32 bits. Alors cela pourrait être manipulé avec des macros pour obtenir le port et le bit, avec l'espoir que le compilateur pourrait calculer des valeurs au moment de la compilation. – gbulmer

1

Considérez la macro suivante:

#define write(port, pin, value) do { \ 
    if (value) \ 
    LAT##port |= 1 << pin; \ 
    else \ 
    LAT##port &= ~(1 << pin); \ 
} while (0) 

Utilisation:

write(A, 3, 1); // compiles to LATA |= 1 << 3; 
write(B, 2, 0); // compiles to LATB &= ~(1 << 2); 

Est que le genre de chose que vous étiez après?

+0

Oui très similaire à ce que Carl posté. J'ai aussi utilisé le style C si j'étais moi-même parce que le compilateur C de la micropuce ne l'obtenait pas. Toujours pas la structure de table la plus flexible et lisible que je cherchais, mais je suppose que la seule solution serait les structures. – Hans

0

Je l'ai vu faire (https://github.com/triffid/Teacup_Firmware/blob/Gen7/arduino.h) avec macros couple:

/// Read a pin 
#define  _READ(IO)     (IO ## _RPORT & MASK(IO ## _PIN)) 
/// write to a pin 
#define  _WRITE(IO, v)   do { if (v) { IO ## _WPORT |= MASK(IO ## _PIN); } else { IO ## _WPORT &= ~MASK(IO ## _PIN); }; } while (0) 

/// set pin as input 
#define  _SET_INPUT(IO)  do { IO ## _DDR &= ~MASK(IO ## _PIN); } while (0) 
/// set pin as output 
#define  _SET_OUTPUT(IO)  do { IO ## _DDR |= MASK(IO ## _PIN); } while (0) 



// why double up on these macros? see http://gcc.gnu.org/onlinedocs/cpp/Stringification.html 

/// Read a pin wrapper 
#define  READ(IO)     _READ(IO) 
/// Write to a pin wrapper 
#define  WRITE(IO, v)   _WRITE(IO, v) 
/// set pin as input wrapper 
#define  SET_INPUT(IO)   _SET_INPUT(IO) 
/// set pin as output wrapper 
#define  SET_OUTPUT(IO)  _SET_OUTPUT(IO) 

avec:

#define DIO0_PIN  PIND0 
#define DIO0_RPORT  PIND 
#define DIO0_WPORT  PORTD 
#define DIO0_PWM  &OCR0B 
#define DIO0_DDR  DDRD 

#define DIO1_PIN  PIND1 
#define DIO1_RPORT  PIND 
#define DIO1_WPORT  PORTD 
#define DIO1_PWM  &OCR2B 
#define DIO1_DDR  DDRD 
... 

Vous pouvez modifier les macros pour prendre des entiers droites plutôt que DION