Archives du mot-clé Microchip

Microchip XC32 1.40 – libc – multiple definition of `_atexitptr’

Problème

Erreur pendant la compilation d’un projet avec le compilateur XC32 de Microchip :

c:/microchip/xc32/v1.40/bin/bin/../../lib/gcc/pic32mx/4.8.3/../../../../pic32mx/lib\libc.a(stdclean_full.o):(.bss+0x0): multiple definition of `_atexitptr'
c:/microchip/xc32/v1.40/bin/bin/../../lib/gcc/pic32mx/4.8.3/../../../../pic32mx/lib\libc.a(stdclean_simple.o):(.bss+0x0): first defined here

Explication

C’est un bug de la version 1.40 du compilateur XC32. Le problème est lié à la fonction exit() et à certaines fonctions de la bibliothèque standard stdio.

Solution

Pour corriger le bug, il va falloir modifier le fichier libc.a fournit avec XC32. Mais, comme ce compilateur est livré avec plusieurs fichiers libc.a, il faut trouver le bon. Pour ce faire, il suffit d’analyser le chemin donné dans le message d’erreur.

Identification le bon fichier libc.a

Dans l’exemple décrit précédemment :

c:/microchip/xc32/v1.40/bin/bin/../../lib/gcc/pic32mx/4.8.3/../../../../pic32mx/lib\libc.a

Simplifions le chemin vers le fichier libc.a en supprimant successivement les chemins relatifs :

c:/microchip/xc32/v1.40/bin/bin/../../lib/gcc/pic32mx/4.8.3/../../../../pic32mx/lib\libc.a
c:/microchip/xc32/v1.40/bin/../lib/gcc/pic32mx/4.8.3/../../../../pic32mx/lib\libc.a
c:/microchip/xc32/v1.40/lib/gcc/pic32mx/4.8.3/../../../../pic32mx/lib\libc.a
c:/microchip/xc32/v1.40/lib/gcc/pic32mx/../../../pic32mx/lib\libc.a
c:/microchip/xc32/v1.40/lib/gcc/../../pic32mx/lib\libc.a
c:/microchip/xc32/v1.40/lib/../pic32mx/lib\libc.a

Une dernière itération et voici le chemin du fichier fautif :

c:/microchip/xc32/v1.40/pic32mx/lib\libc.a

Nous pouvons maintenant appliquer un correctif à ce fichier.

Application le correctif

/!\ Avant toute chose, faite une copie de ce fichier dans son dossier d’origine.

Ensuite, copiez le fichier libc.a dans un dossier temporaire. Par exemple D:\temp

Ouvrir une Invite de commandes avec Windows et se placer dans le dossier temporaire :

cd /d d:\temp

Puis exécutez successivement les commandes suivantes :

xc32-ar x libc.a stdclean_simple.o
xc32-strip -N _atexitptr stdclean_simple.o
xc32-ar r libc.a stdclean_simple.o

Voici la méthode en image :

Modification libc.a de XC32

Replacez le fichier libc.a modifié dans son dossier d’origine.

Conclusion

Vous pouvez maintenant compiler à nouveau votre projet, le message d’erreur ne doit plus apparaitre.

La solution décrite dans ce billet est inspirée par une discussion du forum officiel de Microchip :
Bug in XC32 standard libraries? (stdclean_full.o and stdclean_simple.o both linking)

Ce problème sera sans doute corrigé avec la prochaine version du compilateur XC32.

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.