web-dev-qa-db-fra.com

pack de bits dans une structure c ++ / arduino

J'ai une structure:

typedef struct {
  uint8_t month;  // 1..12 [4 bits]
  uint8_t date;   // 1..31 [5 bits]
  uint8_t hour;   // 00..23 [5 bits]
  uint8_t minute; // 00..59 [6 bits]
  uint8_t second; // 00..59 [6 bits]
} TimeStamp;

mais je voudrais l'emballer pour qu'il ne consomme que 4 octets au lieu de 5.

Existe-t-il un moyen de déplacer les bits pour créer une structure plus serrée?

Cela peut ne pas sembler grand-chose, mais il entre dans l'EEPROM, donc 1 octet enregistré est 512 octets supplémentaires dans une page de 4 Ko (et je peux utiliser ces 6 bits supplémentaires restants pour autre chose aussi).

23
Kent

Ce que vous recherchez, ce sont des champs de bits.

Ils ressemblent à ceci:

typedef struct {
  uint32_t month  : 4;   // 1..12 [4 bits]
  uint32_t date   : 5;   // 1..31 [5 bits]
  uint32_t hour   : 5;   // 00..23 [5 bits]
  uint32_t minute : 6;   // 00..59 [6 bits]
  uint32_t second : 6;   // 00..59 [6 bits]
} TimeStamp;

En fonction de votre compilateur, afin de tenir dans 4 octets sans remplissage, la taille des membres doit être de 4 octets (c'est-à-dire uint32_t) dans ce cas. Sinon, les membres de la structure seront remplis pour ne pas déborder sur chaque limite d'octets, ce qui entraînera une structure de 5 octets, si vous utilisez uint8_t. L'utilisation de ceci comme règle générale devrait aider à éviter les écarts du compilateur.

Voici un lien MSDN qui va un peu en profondeur dans les champs binaires:

Champs de bits C++

29
Ian A.B.K.

Les champs de bits sont une "bonne" façon de procéder en général, mais pourquoi ne pas simplement stocker des secondes depuis le début de l'année? 4 octets suffisent pour les stocker confortablement; en fait, 4 octets suffisent pour stocker les secondes entre 1970 et 2038. En extraire les autres informations est alors un exercice simple tant que vous connaissez l'année en cours (que vous pouvez stocker avec le reste des informations aussi longtemps). car la plage de temps qui vous intéresse couvre moins de 70 ans (et même alors, vous pouvez simplement regrouper les horodatages en plages de 68 ans et stocker un décalage pour chaque plage).

14
Cubic

Une autre solution consiste à stocker les valeurs dans une variable 32 bits et à récupérer les éléments individuels avec décalage de bits.

uint32_t timestamp = xxxx;

uint8_t month = timestamp & 0x0F;
uint8_t date = (timestamp & 0x1F0) >> 4;
uint8_t hour = (timestamp & 0x3E00) >> 9;
uint8_t minute = (timestamp & 0xFC000) >> 14;
uint8_t second = (timestamp & 0x3F00000) >> 20;
12
Serve Laurijssen

Si vous pouvez gérer une précision de deux secondes, le format d'horodatage MS-DOS utilisait 16 bits pour contenir la date (année 1980 comme 7 bits, mois comme 4, jour comme 5) et 16 bits pour l'heure (heure comme cinq, minute comme six, secondes comme cinq). Sur un processeur comme l'Arduino, il peut être possible d'écrire du code qui divise les valeurs sur une frontière de 16 bits, mais je pense que le code sera plus efficace si vous pouvez éviter une telle division (comme MS-DOS l'a fait en acceptant deux secondes précision).

Sinon, comme cela a été noté dans une autre réponse, utiliser un nombre de secondes de 32 bits car un certain temps de base sera souvent plus efficace que d'essayer de garder une trace des choses au "format de calendrier". Si tout ce que vous avez à faire est d'avancer d'une date au format calendrier à la suivante, le code pour le faire peut être plus simple que le code pour convertir entre les dates calendaires et les dates linéaires, mais si vous avez besoin de faire autre chose (même revenir en arrière d'une date à la précédente), vous ferez probablement mieux de convertir les dates au/du format linéaire lorsqu'elles sont entrées ou affichées, et sinon, travaillez simplement avec des nombres linéaires de secondes.

Travailler avec des nombres linéaires de secondes peut être plus pratique si vous choisissez comme date de référence le 1er mars d'une année bissextile. Ensuite, alors que la date dépasse 1461, soustrayez-la de la date et ajoutez 4 à l'année (la comparaison et la soustraction 16 bits sont efficaces sur l'Arduino, et même en 2040, la boucle peut toujours prendre moins de temps qu'une seule division 16x16). Si la date dépasse 364, soustrayez 365 et incrémentez l'année, et essayez jusqu'à deux fois de plus [si la date est 365 après la troisième soustraction, laissez-la].

Un certain soin est nécessaire pour s'assurer que tous les cas d'angle fonctionnent correctement, mais même sur un petit micro 8 bits ou 16 bits, les conversions peuvent être étonnamment efficaces.

2
supercat