dovrei salvare in una variabile di Arduinbo il valore ricavato da un analizzatore di rete elettrica che comunica in protocollo RS 485 Modbus RTU. Questo protocollo consiste in richieste al dispositivo tramite frame e risposte dallo stesso con altro frame dove si possono trovare i valori richiesti.
i dati utilizzati come risposta sono in formato floating point 32bit IEEE Per capire un po' questo formato ho trovato un software che crea le stringhe e le invia registrandone le risposte dallo slave. Allego le schermate dell'applicazione per comprendere:
formatting link
Facendo un'interrogazione dei registri interessati (2, sempre adiacenti) la stringa di
formatting link
I valori utili per il dato da estrapolare sono questi [43] [61] [B3] [33]
formatting link
Il formato selezionato nella immagine mi restituisce il valore corretto della lettura (in questo caso in volt)
Alla fine di questa pappardella vi chiedo: esiste qualche modo/metodo/libreria che mi converta questi 4 numeri esadecimali in un valore rispondente al valore da ottenere?
nell'esempio:snelheid = u.fval;tu devi fare l'opposto riempire l'array snelheide poi avere il float:u.fval=snelheid;la conversione e' fatta con l'assegnazione che fa una conversione di tipo il penultimo post assomiglia a cio' che ho fatto in altri linguaggi in pratica hai due variabili "sovrapposte" negli stessi 4 indirizzi di memoria ram se ci metti un float (32 bit) puoi vedere ogni byte come e' oppure se tu metti i 4 byte(che e' quel che vuoi fare) leggi la variabile float equivalente, molto tempo fa avevo fatto una demo (in turbopascal) che permetteva di impostare 1/0 ogni singolo bit e vedere cosa usciva. Ho usato cose simili in plm/80 bascom, ma finora non ho ancora usato arduino.
ciao delo
"Franzthepanz" ha scritto nel messaggio news:mtdvm7$3no$ snipped-for-privacy@speranza.aioe.org...
Non ho capito cosa non hai capito :) In pratica le union sovrappongono nella stessa area di memoria le variabili che sono specificate all'interno come membri. Nell'esempio variabile_1 usa la stessa area di memoria dell'array byte.
Tutto questo permette di far accedere il compilatore all'area di memoria prelevando o scrivendo i dati sotto varie forme. In questo esempio se accedi come data.variabile_1 carichi il dato in memoria come float, se invece accedi come data.byte[] carichi i dati come char (di solito a 8 bit, ma non e' detto, dipende dal target).
Si potrebbe fare la stessa cosa usando i puntatori, ma e' un po' piu' complesso da capire pur essendo in definitiva la stessa cosa.
Se mi spieghi cosa non ti e' chiaro, posso essere piu' esauriente.
Certi compilatori accettano come tipo di dato unsigned char. Per comodita' io mi sono fatto delle typedef chiamandolo uchar. E' piu' comodo scrivere uchar invece che unsigned char, e sono talmente abituato ad usarlo che mi e' stato un riflesso automatico :)
Prendendo come target un sistema ad 8 bit, devi vedere la memoria come un insieme continuo di locazioni di memoria che vengono definite come byte.
Sperando di non farti casino ulteriore, provo a sintetizzare.
Ogni dato viene memorizzato in questi byte secondo delle modalita' ben specifiche.
1 char (8 bit) occupa 1 byte e i valori che puo assumere sono da 0 a 255
1 int (16 bit) occupa 2 byte contigui e puo' assumere valori da 0 a 65535.
Gia' a questo punto bisogna aprire 2 parentesi: a) Per definire valori con segno si definisce il bit piu' significativo come segno per cui i valori che possono essere espressi sono per il char da -128 a +127, mentre diventano -32768 a +32767 per l'int. b) A seconda del micro utilizzato, l'int puo' essere memorizzato in modalita litte endian o big endian a seconda che il primo byte indichi i bit da 0 a 7 (LSB) o quelli da 8 a 15 (MSB)
A questo punto dovrebbe essere evidente che l'int puoi vederlo come due char consecutivi per cui istruendo opportunamente il compilatore C puoi far coincidere l'indirizzo di memoria dell'int con quello del primo char chiamando ad esempio la variabile int come iVal e i char come cVal0 e cVal1.
Quando il compilatore legge o scrive iVal, imposta automaticamente memaddress 0 e memaddress 1 con il valore a 16 bit di iVal. Quando il compilatore legge o scrive cVal0 o 1, imposta memaddress 0 o memaddress 1 rispettivamente. A questo punto immagina di scrivere cVal0 con 1 e cVal1 con 2. Se vai a leggere iVal, il compilatore legge memaddress 0 e memaddress 1 per cui il valore letto sara' 1 sui bit da 15 a 8 e 2 sui bit da 7 a 0. In binario puoi vederlo come 0000000100000010 quindi 2^8 + 2^1 = 258.
Se sei arrivato fino a qui sano :) , possiamo estendere il concetto ai valori long e float. long prende 4 byte contigui mentre float (standard IEEE) prende a sua volta 4 byte.
La domanda sorge spontanea che differenza c'e' tra long e float? Risposta il modo in cui viene interpretata la sequenza di bit. Per il long la storia e' uguale a quella dell'int mentre per il float e' un po' piu' incasinata e se sei interessato puoi cominciare a vedere qui: "
formatting link
"
Alla fine di tutto questo panegirico, quello che interessa a te e' prendere due int da 16 bit, metterli in 4 byte contigui e rileggere il valore interpretando il dato come float. L'interpretazione float la fa il compilatore, per cui puoi fregartene. L'unica cosa e' sovrapporre due int con un float. Scrivi due int e rileggi il tutto come float. Per fare questo la maniera piu' elegante e' usare le union, quella piu' usata dai "duri e puri" sono i puntatori.
"Franzthepanz" ha scritto nel messaggio news:mtjh53$ur8$ snipped-for-privacy@speranza.aioe.org...
ora ti incasino un po le idee anche io :-)
e' una questione di come i dati vengono interpretati o usati.
prendi 4 locazioni di memoria consecutive e ci scrivi dentro 4 valori: [0] = 0x63 [1] = 0x69 [2] = 0x61 [3] = 0x6F
li puoi considerare come 4 valori da 8 bit indipendenti tra loro oppure come testo ascii sono la parola "ciao" oppure come valore 32bit BigEndian sono il valore 0x6369616F = 1667850607 oppure come valore 32bit LittleEndian sono il valore 0x6F616963 =
1868654947 oppure come floating point sono il valore 6.976153E+28
come vedi sono sempre gli stessi 4 byte, ma da come li unisci cambia il significato o valore del risultato
la union che ti hanno proposto e' un tipo si dato che in C ti permette di usare uno spazio di memoria unico per tipi di dato diversi, e nel tuo caso ti aiuta a fare la conversione voluta.
se il tuo valore floating point e' memorizzato in un array di char nel giusto ordine (esponente seguito da mantissa)
- unsigned char value[4]; un'altra strada e' la semplice istruzione:
- *(float*)value; leggerebbe il valore dell'array come floating point, esattamente come la union
Ok, lavevo immaginato che c'era il trucco. Cmq non sono arrivato ancora al capitolo typdef lol
CUT E menomale che hai sintetizzato... ;-) :-Z A parte gli scherzi, ti ringrazio della spiegazione ma io purtroppo sono ancora indietro con questi argomenti e purtroppo ho capito molto poco .
Non approfitto oltre della tua pazienza, sei stato fin troppo disponibile.
"Franzthepanz" ha scritto nel messaggio news:mtkb3f$8ns$ snipped-for-privacy@speranza.aioe.org...
in linguaggio C il nome di un array equivale al suo indirizzo in memoria, se hai studiato almeno un po' le basi, gli indirizzi possono anche essere memorizzati in variabili che si definiscono "puntatori"
unsigned char * p;
quel "p" e' un puntatore, ossia puo' contenere un indirizzo di memoria.
p = value; e' una assegnazione corretta, proprio perche' value (nome dell'array di char) rappresenta l'indirizzo in cui l'array e' memorizzato.
avendo l'indirizzo di memoria in una variabile la si puo' usare per leggere il contenuto memorizzato a quell'indirizzo. per farlo si usa il simbolo * davanti al nome del puntatore
*p ritorna un singolo "unsigned char", il primo elemento dell'array.
*p e p[0] sono 2 modi per leggere il primo elemento dell'array.
unsigned char primo; primo = *p; ed equivale a primo = p[0]; ed equivale anche a primo = value[0]; e primo = *value;
tutto questo per dire che value e' si un array di unsigned char, ma il simbolo value e' anche un puntatore a unsigned char.
ma come per tutte le variabili, anche con i puntatori si puo' usare l'operazione di "cast", ossia forzare una conversione esplicita in modo che punti ad un tipo di dato diverso. la scrittura: (float*)value dice di interpretare value, non piu' come puntatore a "unsigned char" ma come puntatore a "float", quindi con
*(float*)value non verra' piu' letto un solo byte, ma 4 (un float occupa 4 byte)
alla fine, per rispondere alla tua domanda, questo e' il codice per convertire il tuo array di 4 byte in float: float f; f = *(float*)value;
i puntatori sono tipi di dati a volte pericolosi, ma imparare ad usarli e' basilare per poter padroneggiare il linguaggio C. se ti era piu' chiaro l'esempio con union, usa quello, tanto le 2 strade portano allo stesso risultato.
Dopo aver strapazzato l'unico neurone rimasto, alfio il 19/09/2015 ha pensato bene di dire:
CUT
Grazie mille a te ed anche a Giuseppee per la pazienza e la competenza. Le ultime vostre risposte sono state messe nella mia "libreria di testi da non dimenticare". Alle prossime
ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here.
All logos and trade names are the property of their respective owners.