Archives du mot-clé BCD

PIC32, MPLAB Harmony, data logger, RTCC et BCD

Quel titre !

But

Coder un enregistreur de données (data logger) en utilisant l’horloge RTC d’un PIC32 via le framework MPLAB Harmony de Microchip.

L’objectif est donc d’obtenir, par exemple, un fichier texte où chaque ligne commence par la date et l’heure correspondant à un événement :

08/10/15;10:14:57;...
08/10/15;10:17:28;...
08/10/15;10:22:30;...
08/10/15;10:24:33;...
08/10/15;10:30:07;...

Immédiat ? Pas si sûr…

Problématique

Avec le framework Harmony, la date est récupérée avec la fonction DRV_RTCC_DateGet() de la bibliothèque RTCC :

uint32_t date = DRV_RTCC_DateGet();

Or les valeurs renvoyées sont au format BCD.

Pour le jeudi 8 Octobre 2015, la variable date contient (en notation hexadécimale) 0x15100804.

En séparant chaque groupe de 2 digits :

  • 15 : l’année (2015)
  • 10 : le mois (octobre)
  • 08 : le jour (8)
  • 04 : le numéro du jour de la semaine (0 pour dimanche, 1 pour lundi, 2 pour mardi…), ici jeudi

Comme 0x15100804 correspond à la valeur décimale 353372164, on ne peut pas séparer l’année, le mois et le jour par de simples divisions.

Il faut extraire chaque digit hexadécimale un par un.

Solution n°1 : division et modulo

Une première solution consiste à effectuer une série de divisions en séparant à chaque fois la partie entière du reste de la division.

Par exemple en base 10 (décimale), pour obtenir tout les digits de 1587 soir 1, 5, 8 et 7 :

1587/1000 => partie entière = 1 et reste de la division = 587
  587/100  => partie entière = 5 et reste de la division = 87
   87/10    => partie entière = 8 et reste de la division = 7

En langage C :

#include <stdio.h>

void main(void) {
   
    int date = 1587;

    printf("%d ", date / 1000);
    date = date % 1000;
    printf("%d ", date / 100);
    date = date % 100;
    printf("%d ", date / 10);
    date = date % 10;
    printf("%d", date);

    printf("\n");
   
}

L’exécution de ce code affiche 1 5 8 7.

On applique le même principe à la valeur de la date en BCD mais en utilisant la base 16 (hexadécimale) :

#include <stdio.h>
#include <stdint.h>

void main(void) {
   
    uint32_t date = 0x15100804;
   
    printf("%d ", date / 0x10000000);
    date = date % 0x10000000;
    printf("%d ", date / 0x1000000);
    date = date % 0x1000000;
    printf("%d ", date / 0x100000);
    date = date % 0x100000;
    printf("%d ", date / 0x10000);
    date = date % 0x10000;
    printf("%d ", date / 0x1000);
    date = date % 0x1000;
    printf("%d ", date / 0x100);
    date = date % 0x100;
    printf("%d ", date / 0x10);
    date = date % 0x10;
    printf("%d" , date);

    printf("\n");

}

L’exécution de ce code affiche 1 5 1 0 0 8 0 4.

Solution n°2 : masque binaire et décalage de bits

Intéressons-nous au codage la valeur de la date 0x15100804.

Chaque digit est un nombre hexadécimal compris entre 0 et 9. Il faut donc 4 bits pour coder chaque digit de 0000 à 1001. Avec 8 digits, la date prend 8*4 = 32 bits en mémoire (d’où le type uint32_t).

Écrivons chaque digit et son équivalent en notation binaire :

  1    5    1    0    0    8    0    4
0001 0101 0001 0000 0000 1000 0000 0100

Voici la méthode à appliquer à chaque digit :

  • isoler le paquet de 4 bits correspondant avec un masque binaire 1111 et un ET logique
  • décaler le paquet masqué jusqu’à la dernière position à droite, soit n*4 où n est le numéro du digit (origine 0 = digit de droite)

Pour le digit n°7 (le 1 de l’année) :

  0001 0101 0001 0000 0000 1000 0000 0100
+ 1111 0000 0000 0000 0000 0000 0000 0000
-----------------------------------------
  0001 0000 0000 0000 0000 0000 0000 0000

Faire 28 décalages (7*4) vers la droite :

0001 0000 0000 0000 0000 0000 0000 0000
>>>>
0000 0001 0000 0000 0000 0000 0000 0000
>>>>
0000 0000 0001 0000 0000 0000 0000 0000
>>>>
0000 0000 0000 0001 0000 0000 0000 0000
>>>>
0000 0000 0000 0000 0001 0000 0000 0000
>>>>
0000 0000 0000 0000 0000 0001 0000 0000
>>>>
0000 0000 0000 0000 0000 0000 0001 0000
>>>>
0000 0000 0000 0000 0000 0000 0000 0001

La conversion du résultat en décimal donne bien 1

Pour le digit n°6 (le 5 de l’année) :

  0001 0101 0001 0000 0000 1000 0000 0100
+ 0000 1111 0000 0000 0000 0000 0000 0000
-----------------------------------------
  0000 0101 0000 0000 0000 0000 0000 0000

Faire 24 décalages (6*4) vers la droite :

0000 0101 0000 0000 0000 0000 0000 0000
>>>>
0000 0000 0101 0000 0000 0000 0000 0000
>>>>
0000 0000 0000 0101 0000 0000 0000 0000
>>>>
0000 0000 0000 0000 0101 0000 0000 0000
>>>>
0000 0000 0000 0000 0000 0101 0000 0000
>>>>
0000 0000 0000 0000 0000 0000 0101 0000
>>>>
0000 0000 0000 0000 0000 0000 0000 0101

La conversion du résultat en décimal donne bien 5

On répète ensuite la méthode pour chaque digit.

Voici le code C correspondant à cette méthode :

#include <stdio.h>
#include <stdint.h>

void main(void) {
   
    uint32_t date = 0x15100703;
   
    printf("%d ", (date & 0xF0000000) >> (4*7));
    printf("%d ", (date & 0x0F000000) >> (4*6));
    printf("%d ", (date & 0x00F00000) >> (4*5));
    printf("%d ", (date & 0x000F0000) >> (4*4));
    printf("%d ", (date & 0x0000F000) >> (4*3));
    printf("%d ", (date & 0x00000F00) >> (4*2));
    printf("%d ", (date & 0x000000F0) >> (4*1));
    printf("%d",  (date & 0x0000000F));
   
    printf("\n");
   
}

Conclusion

Vous pouvez maintenant convertir les dates du format BCD vers un format plus approprié pour leurs manipulations.

Les codes présentés ici sont volontairement simples. À vous de les optimiser si nécessaire.

N’hésitez pas à poster un commentaire si vous connaissez une solution alternative, ou si vous voulez plus d’informations.