2017-01-13 3 views
0

Je travaille sur un Motorola HCS08 μCU dans CodeWarrior V10.6, j'essaye de créer un bitfield extern qui a des bits à partir de registres existants. La façon dont les bitfields sont créés dans l'en-tête μCU est commeComment créer un champ de bit à partir de variables existantes dans C

typedef unsigned char byte; 
typedef union { 
    byte Byte; 
    struct { 
    byte PTAD0  :1; 
    byte PTAD1  :1;          
    byte PTAD2  :1;          
    byte PTAD3  :1;          
    byte PTAD4  :1;          
    byte PTAD5  :1;          
    byte PTAD6  :1;          
    byte PTAD7  :1;          
    } Bits; 
} PTADSTR; 
extern volatile PTADSTR _PTAD @0x00000000; 
#define PTAD       _PTAD.Byte 
#define PTAD_PTAD0      _PTAD.Bits.PTAD0 
#define PTAD_PTAD1      _PTAD.Bits.PTAD1 
#define PTAD_PTAD2      _PTAD.Bits.PTAD2 
#define PTAD_PTAD3      _PTAD.Bits.PTAD3 
#define PTAD_PTAD4      _PTAD.Bits.PTAD4 
#define PTAD_PTAD5      _PTAD.Bits.PTAD5 
#define PTAD_PTAD6      _PTAD.Bits.PTAD6 
#define PTAD_PTAD7      _PTAD.Bits.PTAD7 

qui laissera la valeur de registre soit modifié soit par PTAD = 0x01 ou PTAD_PTAD0 = 1, par exemple. Cette définition est fondamentalement la même pour PTAD, PTBD, PTCD, ... PTGD, la seule chose qui change est l'adresse.

Mon attemp pour créer une coutume BitField sur les précédentes variables existantes est

typedef union { 
    byte Byte; 
    struct { 
    byte *DB0; 
    byte *DB1; 
    byte *DB2; 
    byte *DB3; 
    byte *DB4; 
    byte *DB5; 
    byte *DB6; 
    byte *DB7; 
    } Bits; 
} LCDDSTR; 

Je voudrais créer et initialiser le bitfield comme LCDDSTR lcd = {{&PTGD_PTGD6, &PTBD_PTBD5, ...}}, parce que par une raison quelconque, l'initialisation comme LCDSTR lcd = {*.Bits.DB0 = &PTGD_PTGD6, *.Bits.DB1 = &PTBD_PTBD5, ...} (le traitant comme un struct , s'il vous plaît corrigez-moi à nouveau) des conseils dans How to initialize a struct in accordance with C programming language standards ne fonctionne pas avec ce compilateur (il fonctionne sur un compilateur en ligne). Cependant, comme vous pouvez le voir, je suis en quelque sorte en train de grouper les bits, et (si cela fonctionne) je pourrais changer les valeurs du registre réel en faisant *lcd.Bits.DB0 = 1, ou quelque chose comme ça, mais si je le fais lcd.Byte = 0x00, je voudrais changer le dernier (je pense) octet de l'adresse de la mémoire contenue dans lcd.Bits.DB0, vous savez, parce que la structure ne contient pas réellement les données, mais les pointeurs à la place.

Comment continuer à obtenir une structure capable de contenir et de modifier des bits à partir de plusieurs registres? (Je suppose que le problème ici est que dans la mémoire les bits ne sont pas l'un à côté de l'autre, ce qui, je suppose, le rendrait plus facile). Est-ce même possible? Je l'espère.

+0

Y at-il des adresses de mémoire qui sont mis en correspondance avec des registres particuliers? Par exemple l'adresse de mémoire 0x00000000 est mappée au registre 0 et l'adresse de mémoire 0x00000001 est mappée au registre 1 (en supposant des registres de 1 octet ou de 8 bits)? Je m'attendrais à utiliser la même structure 'PTADSTR' pour la définition, puis à créer un pointeur au début de la zone mémoire où se trouvent les registres et à les référencer comme un tableau de la structure' PTADSTR'. –

+0

Vous ne pouvez pas avoir de pointeur sur un champ de bits. Regardez http://stackoverflow.com/questions/13547352/c-cannot-take-address-of-bit-field. –

+0

@RichardChambers Je peux dire que plus loin dans la définition une structure PTADDSTR est définie à 0x00000001, puis PTBDSTR à 0x00000002, puis PTBDDSTR à 0x00000003 et ainsi de suite, et oui registres 8 bits, je suppose que cela répond à vos premières questions, mais je ne pas votre idée, créer un tableau avec des pointeurs sur les bits de chaque structure, dites-vous? – Hans

Répondre

1

Comment continuer à obtenir une structure capable de contenir et de modifier des bits à partir de plusieurs registres? (Je suppose que le problème ici est que en mémoire les bits ne sont pas un à côté de l'autre ..

C'est que je ne pense pas que vous pouvez le faire avec un struct. Parce bitfields par définition doivent occuper la adresses identiques ou contiguës.

Cependant macros peuvent être utiles ici

#define DB0 PTGD_PTGD6 
#define DB1 PTBD_PTBD5 
.... 

et pour effacer les bits à tous les 0 ou un ensemble à tous des 1 vous pouvez utiliser une macro multiligne

#define SET_DB(x) do { \ 
    PTGD_PTGD6 = x; \ 
    PTBD_PTBD5 = x; \ 
    ......    \ 
} while(0) 
+0

Merci beaucoup pour votre réponse. J'aime cette approche, je m'attendais à quelque chose comme 'DB = 0xAB' comme indiqué sur ma question, mais je suppose que je pourrais écrire une macro' SET_DB (x) 'de sorte que je puisse accéder à chaque bit dans' x' et l'assigner à chaque bit BD, par exemple, laissez-moi l'essayer pour voir si cela fonctionne comme prévu. Je dois aussi voir si cela suffit (j'ai en tête que pour une raison quelconque j'ai besoin des bits contenus dans une variable). – Hans

+0

Fait beaucoup de tests et a travaillé comme un charme, merci beaucoup. – Hans

+0

Vous êtes les bienvenus. –

1

Comment continuer à obtenir une structure capable de contenir et de modifier des bits à partir de plusieurs registres?

Vous ne pouvez pas. Une structure doit représenter un bloc de mémoire unique et continu. Dans le cas contraire, des opérations comme prendre la structure sizeof ou effectuer des opérations sur un pointeur ne sont pas logiques.

Si vous voulez permuter les bits d'une valeur, vous devrez trouver un moyen de le faire explicitement. Si l'ordre de vos bits est relativement simple, cela peut être possible avec quelques opérations au niveau du bit; Si c'est plus bizarre, vous devrez peut-être utiliser une table de recherche.Au-delà: les champs de bits en C sont assez limités. Le langage ne donne pas beaucoup de garanties sur la façon dont une structure contenant des champs de bits finira par être mise en mémoire; ils sont généralement mieux évités pour le code portable. (Ce qui ne s'applique pas ici, lorsque vous écrivez du code pour une combinaison compilateur/microcontrôleur spécifique, mais cela vaut la peine de garder à l'esprit en général.)

+0

L'homme, merci beaucoup, pour votre réponse. Est vraiment utile. – Hans

1

Votre syndicat n'a malheureusement aucun sens, car il forme un syndicat de l'un byte et 8 byte*. Comme un pointeur est à 16 bits sur HCS08, cela se traduit par 8 * 2 = 16 octets de données, qui ne peuvent être utilisés de manière significative.

  • S'il vous plaît noter que la structure C appelée champs de bits est très mal spécifié par la norme et doit donc être évitée dans tout programme. See this.
  • Veuillez noter que les cartes de registre Codewarrior ne sont pas proches du standard C (ni MISRA-C).
  • Veuillez noter que les structures sont en général problématiques pour le mappage du registre matériel, car les structures peuvent contenir un remplissage. Vous n'avez pas ce problème sur HCS08 spécifiquement, car il ne nécessite pas d'alignement des données. Mais la plupart des MCU l'exigent.

Il est donc préférable de déployer votre propre carte de registre en C standard si vous avez cette option. Le port d'un registre de données pourrait simplement être définie comme ceci:

#define PTAD (*(volatile uint8_t*)0x0000U) 
#define PTAD7 (1U << 7) 
#define PTAD6 (1U << 6) 
#define PTAD5 (1U << 5) 
#define PTAD4 (1U << 4) 
#define PTAD3 (1U << 3) 
#define PTAD2 (1U << 2) 
#define PTAD1 (1U << 1) 
#define PTAD0 (1U << 0) 

Comme on peut le dire, la définition des masques de bits est la plupart du temps superflu de toute façon, comme PTAD |= 1 << 7; est également lisible PTAD |= PTAD7;. C'est parce que c'était un port d'E/S pur. La définition de masques binaires textuels pour les registres d'état et de contrôle améliore la lisibilité du code.


Si vous souhaitez modifier les bits de plusieurs registres, vous feriez quelque chose comme ce qui suit:

Supposons que nous avons un RVB (rouge-vert-bleu) LED, cathode commune, avec 3 couleurs connecté à 3 broches différentes sur 3 ports différents. Au lieu de battre le concepteur de PCB, vous pouvez le faire:

#define RGB_RED_PTD  PTAD 
#define RGB_RED_PTDD PTADD 
... 
#define RGB_BLUE_PTD PTBD 
#define RGB_BLUE_PTDD PTBDD 
... 
#define RGB_GREEN_PTD PTDD 
#define RGB_GREEN PTDD PTDDD 

#define RGB_RED_PIN 1 
#define RGB_BLUE_PIN 5 
#define RGB_GREEN_PIN 3 

Vous pouvez maintenant définir ces indépendamment de l'endroit où ils se trouvent être situé sur le matériel:

void rgb_init (void) 
{ 
    RGB_RED_PTDD |= (1 << RGB_RED_PIN); 
    RGB_BLUE_PTDD |= (1 << RGB_BLUE_PIN); 
    RGB_GREEN_PTDD |= (1 << RGB_GREEN_PIN); 
} 

void rgb_yellow (void) 
{ 
    RGB_RED_PTD |= (1 << RGB_RED_PIN); 
    RGB_BLUE_PTD &= ~(1 << RGB_BLUE_PIN); 
    RGB_GREEN_PTD |= (1 << RGB_GREEN_PIN); 
} 

Et ainsi de suite. Des exemples étaient pour HCS08 mais la même chose peut bien sûr être utilisée universellement sur n'importe quelle MCU avec E/S de port direct.

0

Cela ressemble à une approche comme celle-ci est dans le sens où vous souhaitez aller avec une solution.

Je n'ai pas testé cela car je n'ai pas le matériel mais cela devrait fournir une alternative à regarder. Ceci suppose que vous voulez activer des broches particulières ou désactiver des broches particulières, mais il n'y aura pas de cas où vous voudrez activer certaines broches et désactiver les autres broches pour un périphérique particulier en une seule opération. Si cela devait être le cas, je considérerais que le type RegPinNo soit un raccourci non signé pour inclure un code d'opération pour chaque combinaison de registre/numéro de broche.Cela suppose également que la synchronisation des opérations n'est pas une contrainte critique et que le matériel a une puissance suffisante pour que les petites boucles ne représentent pas une charge importante pour le débit et le temps CPU nécessaire pour d'autres tâches. Ce code peut donc nécessiter des modifications pour améliorer l'optimisation si cela est une considération. Je suppose que vous voulez une sorte de moyen facilement lisible d'exprimer une commande qui allume et éteint une série de bits dispersés à travers plusieurs zones de la mémoire. La première chose est d'arriver à une représentation de ce à quoi ressemblerait une telle commande et il me semble qu'emprunter un tableau char pour représenter une chaîne suffirait.

typedef byte RegPinNo; // upper nibble indicates register number 0 - 7, lower nibble indicates pin number 0 - 7 

const byte REGPINNOEOS = 0xff; // the end of string for a RegPinNo array. 

Et ceux-ci seraient utilisés pour définir un tableau de numéros de registre/broches comme suit.

RegPinNo myLed[] = { 0x01, 0x12, REGPINNOEOS }; // LED is addressed through Register 0, Pin 0 and Register 1, Pin 1 (zero based) 

Donc, à ce stade, nous avons un moyen de décrire un dispositif particulier, une LED dans ce cas, est abordée à travers une série d'éléments de numéro de registre/broches.

Ensuite, nous allons créer une petite bibliothèque de fonctions qui utilisera cette représentation pour modifier les broches spécifiques dans des registres spécifiques en parcourant cette matrice de numéros de registre/broche et en effectuant une opération sur le registre ou effacer le bit dans le registre.

typedef unsigned char byte; 
typedef union { 
    byte Byte; 
    struct { 
     byte PTAD0 : 1; 
     byte PTAD1 : 1; 
     byte PTAD2 : 1; 
     byte PTAD3 : 1; 
     byte PTAD4 : 1; 
     byte PTAD5 : 1; 
     byte PTAD6 : 1; 
     byte PTAD7 : 1; 
    } Bits; 
} PTADSTR; 

// Define a pointer to the beginning of the register area. This area is composed of 
// 8 different registers each of which is one byte in size. 
// We will address these registers as Register 0, Register 1, ... Register 7 which just happens 
// to be how C does its zero based indexing. 
// The bits representing pins on the PCB we will address as Pin 0, Pin 1, ... Pin 7. 
extern volatile PTADSTR (* const _PTAD) = 0x00000000; 

void SetRegPins(RegPinNo *x) 
{ 
    byte pins[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; 
    int i; 
    for (i = 0; x[i] != REGPINNOEOS; i++) { 
     byte bRegNo = (x[i] >> 4) & 0x07; // get the register number, 0 - 7 
     byte bPinNo = x[i] & 0x07;   // get the pin number, 0 - 7 
     _PTAD[bRegNo].Byte |= pins[bPinNo]; 
    } 
} 

void ClearRegPins(RegPinNo *x) 
{ 
    byte pins[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; 
    int i; 
    for (i = 0; x[i] != REGPINNOEOS; i++) { 
     byte bRegNo = (x[i] >> 4) & 0x07; // get the register number, 0 - 7 
     byte bPinNo = x[i] & 0x07;   // get the pin number, 0 - 7 
     _PTAD[bRegNo].Byte &= ~pins[bPinNo]; 
    } 
} 

void ToggleRegPins(RegPinNo *x) 
{ 
    byte pins[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; 
    int i; 
    for (i = 0; x[i] != REGPINNOEOS; i++) { 
     byte bRegNo = (x[i] >> 4) & 0x07; // get the register number, 0 - 7 
     byte bPinNo = x[i] & 0x07;   // get the pin number, 0 - 7 
     _PTAD[bRegNo].Byte ^= pins[bPinNo]; 
    } 
} 

Vous utiliseriez ce qui précède comme suit. Je ne sais pas à quoi ressemblerait une fonction de temporisation dans votre environnement, donc j'utilise une fonction Sleep() qui prend un argument quant au nombre de millisecondes à retarder ou à mettre en veille.

void LightLed (int nMilliSeconds) 
{ 
    RegPinNo myLed[] = { 0x01, 0x12, REGPINNOEOS }; // LED is addressed through Register 0, Pin 0 and Register 1, Pin 1 (zero based) 

    SetRegPins(myLed); // turn on the LED 
    Sleep(nMilliSeconds); // delay for a time with the LED lit 
    ClearRegPins(myLed); // turn the LED back off 
} 

Edition - Un Raffinement

Une mise en œuvre plus efficace qui permettent à plusieurs broches à fixer dans un registre particulier en même temps serait de définir l'utilisation de RegPinNo comme étant un court non signé `avec l'octet supérieur étant le numéro de registre et l'octet inférieur étant les broches à manipuler comme un masque de bits pour l'octet.

Avec cette approche, vous auriez une fonction SetRegPins() qui ressemblerait à ceci. Un changement similaire serait nécessaire pour les autres fonctions.

void SetRegPins(RegPinNo *x) 
{ 
    int i; 
    for (i = 0; x[i] != REGPINNOEOS; i++) { 
     byte bRegNo = (x[i] >> 8) & 0x07; // get the register number, 0 - 7 
     byte bPinNo = x[i] & 0xFF;   // get the pin mask 
     _PTAD[bRegNo].Byte |= bPinNo; 
    } 
} 

Et les typedefs ressembleraient:

typedef unsigned short RegPinNo; // upper byte indicates register number 0 - 7, lower byte provides pin mask 

const byte REGPINNOEOS = 0xffff; // the end of string for a RegPinNo array. 

Et ces éléments seraient utilisés comme:

void LightLed (int nMilliSeconds) 
{ 
    RegPinNo myLed[] = { 0x0002, 0x0103, REGPINNOEOS }; // LED is addressed through Register 0, Pin 1 and Register 1, Pin 0 and Pin 1 (zero based) 

    SetRegPins(myLed); // turn on the LED 
    Sleep(nMilliSeconds); // delay for a time with the LED lit 
    ClearRegPins(myLed); // turn the LED back off 
}