Clarification sur warning à la compilation

Bonjour à tous,

Je suis actuellement entrain de faire un petit programme pour envoyer un paquet série. J'ai représenter mon paquet avec une structure. Voici mon code pour le moment

struct __attribute__ ((packed)) PaquetSerie {
  uint8_t synchro       : 1;
  uint8_t identifiant   : 3;
  uint8_t id_commande   : 1;
  uint8_t size_commande : 2;
  uint8_t data          : 1;
  uint8_t padding       : 1;
  uint8_t checksum      : 2;
};

void setup(){
  struct PaquetSerie mon_paquet;
  mon_paquet.synchro = 0x7E;
  mon_paquet.identifiant = 0x6C; 

  //Serial.write((uint8_t*)&mon_paquet, sizeof(mon_paquet));
}

void loop(){

}

Pour chaque attribut de mon paquet je spécifie la taille en octet de l'attribut. Mais quand je compile actuellement j'obtiens ces messages:
warning: conversion from 'uint8_t' {aka 'unsigned char'} to 'unsigned char:1' changes value from '126' to '0' [-Woverflow]

warning: conversion from 'uint8_t' {aka 'unsigned char'} to 'unsigned char:3' changes value from '108' to '4' [-Woverflow]

Je ne comprend pas pourquoi est-ce que je fait quelque chose de mal? Ma méthode n'est pas la bonne ? Pourquoi est-ce que j'ai un overflow alors que mes valeurs sont bonnes pour les padding associé.

Ceci est un avertissement, pas une erreur. Que se passe-t-il lorsque vous le téléchargez ? Votre configuration fonctionne-t-elle comme prévu ?

Je n'ai pas essayé de téléverser, mais je ne comprend pas pourquoi le compilateur me dit de changer la valeur 126 à 0 et 108 à 4

On voit assez rarement ce genre de syntaxe, je l'ai utilisée une seule fois.
De mémoire, ça veut dire que tu vas coder synchro sur un bit et identifiant sur 3 bits.

Là, tu passes la valeur la valeur 0x7E qui vaut 126 en décimal et 1111110 en binaire. Mais tu as spécifié que tu ne voulais qu'un seul bit pour cette variable, donc il conserve le dernier uniquement, soit 0.

C'est ce qui est expliqué ici.

De même, lorsque tu passes 0x6C à identifiant, il conserve les 3 derniers bits soit 100, c'est à dire 4. Ce qui est indiqué dans le second warning.

Que veux-tu faire exactement ?

Effectivement c’est moi qui fait n’importe quoi :blush: je veux que ce soit des octets et non dès bits je me suis emmêlé les pinceaux tout seul.

Au final ce que je veux faire c’est avoir une structure qui représente un paquet de configuration qui sera envoyé en série.

Donc je veux en faire quelque chose comme ça

struct PaquetSerie {
  uint8_t synchro[1]; //1 octet
  uint8_t identifiant[3]; //3 octets
  //etc
};

Mais du coup je n’ai jamais fait comme ça comment set par exemple la valeur 1000 à mon attribut identifiant ?

le plus simple:

utilisez le type qui est adapté à votre valeur, ça prendra le bon nombre d'octets. Mais il n'y en a pas de 3 octets donc on prend 4

struct PaquetSerie {
  uint8_t synchro;      // 1 octet
  uint32_t identifiant; // 4 octets
  //etc
};

PaquetSerie monPaquet;
monPaquet.synchro = 42;
monPaquet.identifiant = 1000;

ou alors vous trichez avec des champs de bits

struct PaquetSerie {
  uint32_t synchro : 8;      // 1 octet
  uint32_t identifiant : 24; // 3 octets
  //etc
};

PaquetSerie monPaquet;
monPaquet.synchro = 42;
monPaquet.identifiant = 1000;

Et pour éviter qu'il y ait du padding fait par le compilateur on utilise des attributs ici __attribute__ ((__packed__))

struct __attribute__ ((__packed__)) PaquetSerie {
  uint32_t synchro : 8;      // 1 octet
  uint32_t identifiant : 24; // 3 octets
  //etc
};

PaquetSerie monPaquet;
monPaquet.synchro = 42;
monPaquet.identifiant = 1000;
struct PaquetSerie {
  uint8_t synchro; //1 octet
  uint16_t identifiant; //2 octets
  //etc
};
...
  mon_paquet.identifiant =1000;

EDIT: oups, grillé par J-M-L

J'ai une question qui me taraude, sachant que du côté du récepteur dans la doc il est spécifié que identifiant doit avoir un format de 3 octets, non signé valeur acceptable [0, 999 999], quel est la version la plus judicieuse à utiliser ?

uint32_t identifiant : 24; // 3 octets

ou

uint32_t identifiant; // 4 octets

ou alors quelque chose du genre ?

uint8_t[3] identifiant;
 
identifiant[0] = (n >> 16) & 0xFF;      // n étant la valeur à enoyer
identifiant[1] = (n >> 8) & 0xFF;
identifiant[2] = (n >> 0) & 0xFF;

Si vous avez une trame binaire avec un format bien défini il faut utiliser attribute packed et les champs de bits pour vraiment avoir 3 octets. Avec GCC ca devrait suffire pour conserver la cohérence de la structure

Le tableau d’octets fonctionne aussi et est plus portable mais un peu plus cassé pied à utiliser. Il faudrait aussi savoir si votre entier sur 3 octets est en little endian ou big endian

Juste par curiosité est ce vraiment un format binaire ou alors c’est de l’ASCII qu’il faut mettre (3 octets pour « 000 » à « 999 » ça semble logique parce que sinon 2 octets auraient suffit pour un nombre entre 0 et 999

Effectivement oui c'est du little endian en plus du coup mon exemple est faux je crois, il devrait être

identifiant[0] = (n >> 0) & 0xFF;      // n étant la valeur à envoyer
identifiant[1] = (n >> 8) & 0xFF;
identifiant[2] = (n >> 16) & 0xFF;

Mon champ identifiant est bien un champ de 3 octets pour pouvoir stocker des valeurs allant de 0 à 999999. Du coup j'utilise la méthode * attribute packed* et les champs de bits

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.