2
votes

Comment envoyer des données dans l'ordre little endian C

Je communique avec une carte qui nécessite que je lui envoie 2 octets signés.

explication du type de données
entrez la description de l'image ici ce que j'ai besoin d'envoyer entrez la description de l'image ici

Aurais-je besoin d'une manipulation au niveau du bit ou puis-je simplement envoyer un entier 16 bits comme suit?

int16_t rc_min_angle = -90; 
int16_t rc_max_angle = 120;

write(fd, &rc_min_angle, 2); 
write(fd, &rc_max_angle, 2);


7 commentaires

@CostantinoGrana Le manuel dit "Unités: 0,02197265625 degré." Je n'ai aucune idée de ce que signifient ces 2 chiffres mais je pense que c'est en degrés?


0.02197265625 est 45/2048. Les unités sont 45 / 2048e d'un degré? Faites attention!


Certains compilateurs ont des extensions pour définir les variables comme big ou little-endian. Ceci est très utile car certaines architectures, comme powerpc, sont capables de lire / écrire des entiers dans les deux modes.


@JohnKugelman est-ce que cette virgule est une faute de frappe dans le manuel? La période aurait plus de sens.


C'est un point décimal de style européen, je suppose.


@JohnKugelman Je ne vois plus mon commentaire original. Bref, je n'étais pas trop loin: ce ne sont pas des radians, mais c'est un point fixe même si je ne comprends pas pourquoi 45 / 2048e de degré au lieu d'un simple 1 / 2048e.


@bakalolo Comme l'a dit John Kugelman, vous devez prendre vos diplômes, les multiplier par 2048, puis les diviser par 45. Donc -90 devient -4096, 120 devient 5461 et ainsi de suite.


4 Réponses :


5
votes

int16_t a la taille correcte mais peut ou peut ne pas être la bonne endianness. Pour garantir un ordre little endian, utilisez des macros telles que celles de endian.h :

#define _BSD_SOURCE
#include <endian.h>

...

uint16_t ec_min_angle_le = htole16(ec_min_angle);
uint16_t ec_max_angle_le = htole16(ec_max_angle);

write(fd, &ec_min_angle_le, 2);
write(fd, &ec_max_angle_le, 2);

Ici htole16 signifie "host to little endian 16-bit". Il convertit l'endianness natif de la machine hôte en little endian: si la machine est big endian, il échange les octets; si c'est little endian c'est un no-op.

Notez également que vous devez passer l'adresse des valeurs à write () , pas les valeurs elles-mêmes. Malheureusement, nous ne pouvons pas intégrer les appels et écrire write (fd, htole16 (ec_min_angle_le), 2) .


5 commentaires

Pas d'accord avec "Malheureusement, nous ne pouvons pas intégrer les appels". Exemple de compteur write (fd, & (uint16_t) {htole16 (ec_min_angle)}, 2);


De même, d'autres plates-formes ont des fonctions hton () et ntoh () dans leurs bibliothèques C, pour placer l'échange d'octets dans le contexte du remplissage / vidage du tampon réseau stackoverflow.com/q/15863775 . "Host to network", et vice versa.


@vvaltchev Par en ligne , le répondeur voulait probablement dire «écrire l'appel en ligne» (sur une seule ligne), et non «étendre la fonction appelée en ligne».


@ user4815162342 Je suis tout à fait d'accord avec vous, je viens de lire le texte de loin trop rapidement, manquant cela. Je me concentrais sur htole16 () , alors que tout le "commentaire en ligne" concernait en fait l'interface de write () , pas htole16 () . Quoi qu'il en soit, c'est amusant d'observer à quelle vitesse la discussion est sortie du sujet, avec plusieurs commentaires sur l '"inlining" du 2ème argument de write () , ce qui est totalement hors de propos, étant donné la question :-)


@vvaltchev Bon point sur le hors-sujet - l'utilisation de write () a également généré une discussion similaire sous réponse. Ici c'est un peu mieux car l'expression inline n'est pas complètement hors sujet, car elle affecte l'ergonomie.



0
votes

Si vous n'avez pas besoin de l'avoir de type générique, vous pouvez simplement faire:

#include <stdint.h>
#include <unistd.h>

/*most optimizers will turn this into `return 1;`*/
_Bool little_endian_eh() { uint16_t x = 1; return *(char *)&x; }

void swap2bytes(void *X) { char *x=X,t; t=x[0]; x[0]=x[1]; x[1]=t; }

int main()
{
    int16_t rc_min_angle = -90; 
    int16_t rc_max_angle = 120;

    //this'll very likely be a noop since most machines
    //are little-endian
    if(!little_endian_eh()){
        swap2bytes(&rc_min_angle);
        swap2bytes(&rc_max_angle);
    }

    //TODO error checking on write calls
    int fd =1;
    write(fd, &rc_min_angle, 2); 
    write(fd, &rc_max_angle, 2);
}


0 commentaires

0
votes

Pour envoyer des données little-endian, vous pouvez simplement générer les octets manuellement:

int write_le(int fd, int16_t val) {
    unsigned char val_le[2] = {
      val & 0xff, (uint16_t) val >> 8
    };

    int nwritten = 0, total = 2;
    while (nwritten < total) {
      int n = write(fd, val_le + nwritten, total - nwritten);
      if (n == -1)
        return nwritten > 0 ? nwritten : -1;
      nwritten += n;
    }
    return nwritten;
}

Un bon compilateur reconnaîtra que le code ne fait rien et compilera la manipulation de bits en no-op sur un plateforme little-endian. (Voir par exemple gcc générant le même code pour la variante avec et sans le bit-twiddling.)

Notez également que vous ne devez pas ignorer la valeur de retour de write ( ) - non seulement il peut rencontrer une erreur, mais il peut aussi écrire moins que ce que vous lui avez donné, auquel cas vous devez répéter l'écriture.


2 commentaires

OK très bien. Pourtant, si le code examine la valeur de retour de write (fd, val_le, 2) , alors pourquoi pas aussi la valeur de retour de write (fd, val_le + 1, 1); ? Cela semble assez clair pour simplement coder return write (fd, val_le, 2); et laisser l'appelant décider de la gestion des erreurs.


@chux Vous avez raison, l'examen du deuxième write () ne peut être évité. return write (fd, val_le, 2) est insuffisant car un valide write () peut renvoyer 1, ce n'est tout simplement pas un cas d'erreur.



1
votes

Si les fonctions endian ne sont pas disponibles, écrivez simplement les octets dans le petit ordre endian.

Peut-être avec un littéral composé .

//         v------------- compound literal ---------------v
write(fd, &(uint8_t[2]){rc_min_angle%256, ec_min_angle/256}, 2);
write(fd, &(uint8_t[2]){rc_max_angle%256, ec_max_angle/256}, 2);
//                      ^-- LS byte ---^  ^-- MS byte ---^
//        &

J'ai ajouté le & en supposant que write () est similaire à write (2) - Linux .


0 commentaires