Unire 4 byte per creare un dato a 32 bit (Arduino 1)

Sto impazzendo non riesco ad unire 4 byte per ottenere un numero a 32 bit (long o unsigned long).

Come posso fare, l'operazione quasi mi riesce ma il risultato non è l'unione dei 4 byte qualche bit cambia, dove sbaglio ?

DATO = (inData3 * 16777216);
DATO = DATO + ((inData2 & 0xff) * 65536);
DATO = DATO + ((inData1 & 0xff) * 256);
DATO = DATO + (inData0 & 0xff);

Ho provato anche i registri a scorrimento ma nulla da fare.
Grazie.
Ciao.

Buona sera,
essendo il tuo primo post, nel rispetto del regolamento, ti chiedo cortesemente di presentarti QUI (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con attenzione il su citato REGOLAMENTO ... Grazie.

Guglielmo

.. ah ... se metti il codice competo (... mi raccomando, in conformità al regolamento, punto 7, racchiuso tra i tag CODE che, in fase di edit, ti inserisce il bottone </> ... primo a sinistra) magari capiamo un po' più di cose.

Guglielmo

typedef union
{
        uint8_t  byte[4];
        uint16_t half[2];
        uint32_t word;
} my_data_t;
    my_data_t my_data;

    my_data.byte[0] = 0x01;
    my_data.half[0] = 0x0123;
    my_data.word    = 0x01234567;

una cosa cosi' ti va bene :smiley: ?

Occhio che byte e word sono tipi già definiti in Arduino, non sono sicuro si possano usare impunemente come nomi dei campi di una struct/union.

Peraltro con "word" in ambito Arduino si indica solitamente un tipo a 16 bit, meglio adeguare la terminologia :).

azzarola che scelta poco felice
per il nome dei campi union

X____________________X

la mia proviene dal mondo RISC

non uso praticamente mai arduino
certo pero' che pure loro, potevano
evitare di ridefinire i tipi in quel modo

p.e.
"byte" si capisce, ma e' meglio "uint8_t"
gli altri, usati come tipi, sono + equivoci

quindi direi, rettifico con una cosa cosi'

typedef struct
{
    union
    {
        uint8_t  u08[4];
        uint16_t u16[2];
        uint32_t u32;
    } as;
} my_data_t;
    my_data_t my_data;

    my_data.as.u32    = 0x00000000;
    my_data.as.u08[0] = 0x01;
    my_data.as.u32    = 0x00000000;
    my_data.as.u16[0] = 0x0123;
    my_data.as.u32 = 0x01234567;

poco equivoco, abbastanza esplicito :smiley:

oppure cosi'

typedef struct
{
    union
    {
        struct
        {
            uint8_t ls;
            uint8_t lm;
            uint8_t ml;
            uint8_t ms;
        } u08;
        struct
        {
            uint16_t ls;
            uint16_t ms;
        } u16;
        struct
        {
            uint32_t ms;
        } u32;
    } as;
} my_data_t;

meno error prone perche' non permette
di sbagliare con gli indici

ecco un esempio d'uso, eseguito su linux

    my_data_t my_data;

    my_data.as.u32.ms = 0x00000000;
    my_data.as.u08.ls = 0x01; printf("1: %08lx\n", my_data.as.u32.ms);
    my_data.as.u32.ms = 0x00000000;
    my_data.as.u08.lm = 0x23; printf("2: %08lx\n", my_data.as.u32.ms);
    my_data.as.u32.ms = 0x00000000;
    my_data.as.u08.ml = 0x45; printf("3: %08lx\n", my_data.as.u32.ms);
    my_data.as.u32.ms = 0x00000000;
    my_data.as.u08.ms = 0x67; printf("4: %08lx\n", my_data.as.u32.ms);
    my_data.as.u32.ms = 0x00000000;
    my_data.as.u16.ls = 0x0123;        printf("5: %08lx\n", my_data.as.u32.ms);
    my_data.as.u32.ms = 0x00000000;
    my_data.as.u16.ms = 0x4567;        printf("6: %08lx\n", my_data.as.u32.ms);
    my_data.as.u32.ms = 0x00000000;
    my_data.as.u32.ms = 0x01234567;    printf("7: %08lx\n", my_data.as.u32.ms);

tutti questi esempi sono endian dependent
il codice e' scritto per macchina bigendian
ed eseguito su machine little endian ecco il risultato

1: 00000001
2: 00002300
3: 00450000
4: 67000000
5: 00000123
6: 45670000
7: 01234567

arduino, come avr8 e' ad 8 bit
come gestisce uint32?
come little endian
o come bigendian?

se fosse littleendian, basterebbe invertire
i campi delle struct interne

        struct
        {
            uint8_t ms;
            uint8_t ml;
            uint8_t lm;
            uint8_t ls;
        } u08;
        struct
        {
            uint16_t ms;
            uint16_t ls;
        } u16;

ed eseguito su macchina little endian
questo e' il risultato

1: 01000000
2: 00230000
3: 00004500
4: 00000067
5: 01230000
6: 00004567
7: 01234567

riprendendo quanto proposto in cima al topic
l'altro approccio e' questo:

uint32_t data;
uint8_t  coin[4];

coin[3]=0x01;
coin[2]=0x23;
coin[1]=0x45;
coin[0]=0x67;

data = 0x00000000;
data = data bitwiseOr (coin[0]);
data = data bitwiseOr (coin[1] shiftLeft 8);
data = data bitwiseOr (coin[2] shiftLeft 16);
data = data bitwiseOr (coin[3] shiftLeft 24);

l'operatore bitwiseOr, in C: "|"
l'operatore shiftLeft, in C: "<<"

attenzione che coin ha lo stesso problema di endian
degli esempi precedenti

se le cose non tornano, basta specchiare gli indici

coin[0]=0x01;
coin[1]=0x23;
coin[2]=0x45;
coin[3]=0x67;

Io mi confonderei meno con gli indici, piuttosto che con ls/lm/ml/ms...

data = 0x00000000;
data = data bitwiseOr (coin[0]);
data = data bitwiseOr (coin[1] shiftLeft 8);
data = data bitwiseOr (coin[2] shiftLeft 16);
data = data bitwiseOr (coin[3] shiftLeft 24);

Credo che qua sia necessario qualche cast perché il tutto funzioni su Arduino. Difatti lo standard C prevede che gli operatori dello shift siano automaticamente promossi a int, ma int è 16 bit, quindi gli ultimi 2 verrebbero troncati a zero, o almeno credo. Vedi quel che avevo scoperto qua, dal post #8.

ms e compagnia, provengono sempre dal mondo RISC
devo dire che fatto l'occhio sono comodissimi

"ms" sta per most significant (byte/half)
"ls" sta per least significant(byte/half)

quel secondo "m" sta per middle, nel mezzo

direi che sono orecchiabili, ed e' la scelta
fatta da Patterson & Hennessy, tanto per dare
un nome ai vari byte e half word

sicuramente meno ambiguo di byte0
di cui non sai mai se e' il MSB o LSB :smiley:

SukkoPera:
lo standard C prevede che gli operatori dello shift
siano automaticamente promossi a int, ma int è 16 bit

bah, "int" e' ambiguo, come lo e' "short"
il C fa veramente pena da questo punto di vista

da me "int" e' sempre 32bit, difatti questo codice

printf("sizeof(int)=%d\n", sizeof(int));

testato su

{ HPPA2, MIPS32, m68k/Coldfire, PowerPC32, Intelx86, BlackfinBF5xx }

da sempre come risultato

sizeof(int)=4

con toolchain { gcc, VisualDSP++, CodeWarrior }

persino su con gcc-v3.3, su macchina stack oriented ad 8bit
con registri softfloat (68hc11 ha solo l'accumulatore)
da sempre come risultato 4

semmai ho grossi, e gravi problemi
con i puntatori a 64bit @______________@

della serie su MIPS64 (ma pure su ARM64)
sizeof(p_uint64_) non e' sempre 8
a volte e' 4, quando (uint64_t*) e' invece 8

sono i casini del C … non ho alcuna idea di come la cosa
sia gestita su Avr8, pero' mi sembra MOLTO improbabile
che uno shift sia a 16bit, tanto la macchina sotto e' ad 8bit
e tutta la parte a 32bit e' gestita software
(con un minimo di carry in/out da parte della ALU)

in ogni caso un cast non fa male :smiley:

... aho .. state a fa un casino quando ... secondo me, barnstormer76, che tra l'altro è pure sparito, ha solo sbagliato il tipo per la variabile DATO :smiling_imp: :smiling_imp: :smiling_imp:

Guglielmo

LoooooL
dici che e' "scompa-vanito"
perche' l'abbiamo spaventato
@_________@ ?

Non è che int sia ambiguo, è solo che dipende dalla piattaforma e dal compilatore, basta saperlo. Se ti servono dati di una dimensione ben precisa, i vari uint32_t & compagnia sono stati introdotti proprio per questo.

E ti garantisco che su Arduino int è a 16 bit, così come ti garantisco che shiftare un byte a sinistra di 16 posizioni dà 0, per i motivi succitati.

Concordo comunque che stiamo facendo una confusione esagerata :D.

non e' che non ci credo
e' che non ho toolchain
e board arduino sottomano

SukkoPera:
Non è che int sia ambiguo,
è solo che dipende dalla piattaforma
e dal compilatore, basta saperlo

certo, bastava esserne consapevoli
quando hanno implementato in quel modo
il machine layer di gcc-avr8

una scelta, porre int=16bit, che io non avrei fatto
visto che poi tocca correggerla a colpi di casting

la confusione degli int è uno dei primi problemi che spunta passando dalle architetture ad 8 bit alle architetture a 32 bit o 64.
La mia personale opinione è che la scelta originale è stata infelice anche se chiaramente scritta nelle specifiche che però finisce che dimentichi.

Y888099

a suo tempo K&R decisero che gli int dovessero essere "almeno" a 2 byte. La fregatura sta nell'almeno.
E' una fregatura perché non varia solo con l'architettura ma può variare anche con il compilatore.

infatti su Cavium OCTEON (MIPS64)
ho lo stesso problema che c'e' su
Arduino, ovvero casini con gli shift

questo codice funziona

    uint64_t data;
    uint64_t toad;
    uint8_t  coin[8];

    coin[7] = 0x01;
    coin[6] = 0x23;
    coin[5] = 0x45;
    coin[4] = 0x67;
    coin[3] = 0x89;
    coin[2] = 0xab;
    coin[1] = 0xcd;
    coin[0] = 0xef;

    data = 0x0000000000000000;
    data = data bitwiseOr((uint64_t) coin[0]);
    data = data bitwiseOr((uint64_t) coin[1] shiftLeft 8);
    data = data bitwiseOr((uint64_t) coin[2] shiftLeft 16);
    data = data bitwiseOr((uint64_t) coin[3] shiftLeft 24);
    data = data bitwiseOr((uint64_t) coin[4] shiftLeft 32);

    toad = coin[5];
    data = data bitwiseOr( toad shiftLeft 40);
    toad = coin[6];
    data = data bitwiseOr( toad shiftLeft 48);
    data = data bitwiseOr((uint64_t) coin[7] shiftLeft 56);

se e solo se si fa cast esplicito

    data = data bitwiseOr((uint64_t) coin[4] shiftLeft 32);

oppure se si usa una variabile di appoggio dello stesso size

    uint64_t data;
    uint64_t toad;
...
    toad = coin[6];
    data = data bitwiseOr( toad shiftLeft 48 );

senza questi due accorgimenti
gli shift NON lavorano in modo corretto

zoomx:
la confusione degli int è uno dei primi problemi
che spunta passando dalle architetture ad 8 bit
alle architetture a 32 bit

dipende da quanto e' stata ponderata
la scelta implementativa del machine layer
pero' dipende anche dalle constraints

p.e. 68hc11 e' un macinino ad 8bit
ha solo 1 registro ad 8 bit, estendibile
a 16bit, per poter usare gcc tocca
usare i softregister, ovvero usare la ram
interna al macinino (256 ... 512 byte)
come "registri CPU"

ebbene, nel supportare il macinino
potevano optare per due scelte

  1. assumere che int sia 16bit, due byte
  2. assumere che int sia 32bit, quattro byte

chi ha seguito il port, ha optato per la 2)
lasciando la 1) come opzionale, ovvero ..
.. se proprio vuoi, o sei alle strette
puoi forzare il compilatore C in quel modo

che differenza c'e' tra la 1) e la 2) ?

a livello ISA il macinino ha supporto solo per
il registro accumulatore, che puo' lavorare
a 16bit solo fondendo assieme due registri da 8

la ALU fornisce ha carry in/out allo scopo
ma cio' vale solo per le operazioni aritmetiche
( e per i puntatori, max 64Kbyte indirizzabili
attraverso due speciali registri indice a 16bit)
per quelle logiche tocca infilare supporto
dentro a librerie specifiche di crt0 e crt1

p.e. lo shift a sinistra, fatto sull'accumulatore
fisicamente pialla tutti i bit oltre il 15th
proprio perche' quel registro e' largo 16bit

quindi la vera differenza e' che avere int=32bit
su macchine ad 8bit costa molto + glue-code

ed ecco perche' il C fa questa bizzarra scelta
implementativa: serve a ridurre le dimensioni
delle librerie di supporto a C run time: perche'

la "colla" del glue-code occupa spazio codice

68hc11, unsigned int

Integers are either 16 or 32-bit entities.
They are 32-bit by default.
The -mshort option turns on the 16-bit integer mode.
The parameter passing rule applies
to either short or long depending on its size.

ecco un esempio di macinino ad 8bit
dove int vale 32bit di default
scelta molto saggia :smiley:

Ho usato il 68HC11 alcuni lustri fa su una board con inteprete Basic (senza numeri di riga!) ma ho usato anche l'assembly per alcune cosette veloci. Oggi non ricordo più niente anche perché mi pare che non lo producono più.