Go Down

Topic: Codificatore/decodificatore dati seriali. (Read 159 times) previous topic - next topic

astrobeed

Nov 12, 2014, 06:02 pm Last Edit: Nov 16, 2014, 10:09 am by astrobeed
Spesso salta fuori il problema di come fare per estrapolare i vari dati contenuti in una comunicazione seriale, idem per l'opposto, ecco una soluzione semplice, elegante e che richiede poche risorse.
Premetto che quanto segue non riguarda la ricezione/trasmissione dei dati e relativo protocollo, indipendentemente dal tipo di bus, siamo nella condizione di avere un buffer pieno con i dati ricevuti oppure di doverlo riempire con quelli da inviare.
La soluzione è usare una unione composta da un struttura e il buffer dati, in pratica prima si crea una struttura che contiene tutti i dati che dobbiamo inviare/ricevere, possono essere di tutti i tipi meno che stringa perché non è un tipo dati standard del C, si devono usare gli array di char al loro posto, dopo di che la struttura viene inserita in una unione assieme ad un buffer, di tipo byte, con una dimensione pari alla somma di tutti i dati inseriti nella struttura.
Vediamo un esempio pratico, ipotizziamo di voler inviare/ricevere quattro dati di tipi char, una stringa char di quattro caratteri e due numeri unsigned long int, la struttura da realizzare è questa:

Code: [Select]

// struttura dati
typedef struct
{
  char dato1;
  char dato2;
  char dato3;
  char dato4;
  char dato5[4];
  unsigned long int val1;
  unsigned long int val2;
} Dati_S;


Dopo di che creiamo l'unione tra la struttura e un buffer di tipo byte grande 16 byte, è la somma dei vari dati, 1+1+1+1+4+4+4 = 16.

Code: [Select]

// union che gestisce il buffer
union Gest_Dati
{
  Dati_S myData;
  char buffer_s[16];
}
Dati_U;


Ora abbiamo gli strumenti per poter codificar e decodificare il buffer dati inviato/ricevuto tramite seriale/I2C/SPI, etc.
Per decodificare i dati prima si copia il buffer ricevuto nel buffer dell'unione, o più semplicemente si usa direttamente questo per ricevere i dati, dopo di che è sufficiente leggere i singoli campi della struttura per avere i nostri dati estratti dal buffer.
Per trasmettere si fa il contrario, prima si caricano i singoli campi della struttura con i dati da inviare dopo di che si trasmette il buffer dell'unione, che automaticamente contiene tutti i byte da inviare già formattati.

Esempio di codice completo funzionante, i dati caricati nei singoli campi della struttura vengono poi stampati sul terminale seriale usando il buffer dell'unione.

Code: [Select]


// struttura dati
typedef struct
{
  char dato1;
  char dato2;
  char dato3;
  char dato4;
  char dato5[4];
  unsigned long int val1;
  unsigned long int val2;
}
Dati_S;

// union che gestisce il buffer
union Gest_Dati
{
  Dati_S myData;
  char buffer_s[16];
}
Dati_U;

// CodeDecode.buffer_s[0]

void setup() {
  Serial.begin(115200);

  // init dati struttura
  Dati_U.myData.dato1 = 'a';
  Dati_U.myData.dato2 = 'b';
  Dati_U.myData.dato3 = 'c';
  Dati_U.myData.dato4 = 'd';
  Dati_U.myData.dato5[0] = 'T';
  Dati_U.myData.dato5[1] = 'e';
  Dati_U.myData.dato5[2] = 's';
  Dati_U.myData.dato5[3] = 't';
  
  Dati_U.myData.val1 = 123456;
  Dati_U.myData.val2 = 654321;

  // stampa buffer
  Serial.println(Dati_U.buffer_s);
}

void loop() {

}


MauroTec

La cosa bella che tutto sto codice non consuma risorse CPU in più che accedere direttamente al buffer senza la struttura.

C'è un modo più sporco e portatore di errori che comunque ho visto usare tanto. Mi riferisco al cast sporco da puntatore buffer a puntatore a struct, o viceversa da struct a puntatore uint8_t.

Ciao.



AvrDudeQui front end per avrdude https://gitorious.org/avrdudequi/pages/Home

Erik86

ciao ragazzi, ma quindi io questo lo posso utilizzare anche con una comunicazione RS485?

astrobeed

ciao ragazzi, ma quindi io questo lo posso utilizzare anche con una comunicazione RS485?
Come ho specificato questo metodo è indipendente dal tipo di trasmissione seriale, sia come bus che come protocollo, in quanto agisce direttamente sul buffer dati ricevuto o da trasmettere.

MauroTec

Questo codice funziona:
Code: [Select]

(*(Dati_S*)(buffer_s)).dato1 = 1;        // accesso tramite operatore .
((Dati_S*)(buffer_s))->dato1 = 1;       // accesso tramite operatore ->


Tuttavia il compilatore ora mi avvisa:
warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

Con la union è tutto meno misterioso.

Ciao. 
AvrDudeQui front end per avrdude https://gitorious.org/avrdudequi/pages/Home


fabpolli

Ciao,
  ho provato a utilizzare il codice postato ma in compilazione (IDE 1.06 windows 7 64 bit ) mi restituisce quest'errore e non riesco a capirne il motivo:
Code: [Select]
Arduino: 1.0.6 (Windows 7), Board: "Arduino Uno"
F:\Fabrizio\Software\arduino_editor\hardware\tools\avr\bin\avr-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=106 -IF:\Fabrizio\Software\arduino_editor\hardware\arduino\cores\arduino -IF:\Fabrizio\Software\arduino_editor\hardware\arduino\variants\standard C:\Users\Fabrizio\AppData\Local\Temp\build7981574632788313861.tmp\TestStruttura.cpp -o C:\Users\Fabrizio\AppData\Local\Temp\build7981574632788313861.tmp\TestStruttura.cpp.o

TestStruttura.ino: In function 'void setup()':
TestStruttura:43: error: call of overloaded 'println(byte [16])' is ambiguous
F:\Fabrizio\Software\arduino_editor\hardware\arduino\cores\arduino/Print.h:71: note: candidates are: size_t Print::println(const String&) <near match>
F:\Fabrizio\Software\arduino_editor\hardware\arduino\cores\arduino/Print.h:72: note:                 size_t Print::println(const char*) <near match>
F:\Fabrizio\Software\arduino_editor\hardware\arduino\cores\arduino/Print.h:73: note:                 size_t Print::println(char) <near match>
F:\Fabrizio\Software\arduino_editor\hardware\arduino\cores\arduino/Print.h:74: note:                 size_t Print::println(unsigned char, int) <near match>
F:\Fabrizio\Software\arduino_editor\hardware\arduino\cores\arduino/Print.h:75: note:                 size_t Print::println(int, int) <near match>
F:\Fabrizio\Software\arduino_editor\hardware\arduino\cores\arduino/Print.h:76: note:                 size_t Print::println(unsigned int, int) <near match>
F:\Fabrizio\Software\arduino_editor\hardware\arduino\cores\arduino/Print.h:77: note:                 size_t Print::println(long int, int) <near match>
F:\Fabrizio\Software\arduino_editor\hardware\arduino\cores\arduino/Print.h:78: note:                 size_t Print::println(long unsigned int, int) <near match>

astrobeed

Ciao,
  ho provato a utilizzare il codice postato ma in compilazione (IDE 1.06 windows 7 64 bit ) mi restituisce quest'errore e non riesco a capirne il motivo:
Cambia "byte buffer_s[16];" con "char buffer_s[16];" e l'errore sparisce, è stata una distrazione mia quando ho ricopiato il codice e ho modificato il buffer da char in byte senza riprovare il tutto.

fabpolli

Grazie mille adesso funziona alla perfezione!

signorbarro

Buongiorno a tutti,
sono molto interessato al topic,
posso farti qualche domanda Astrobeed?
credi che possa usare questa soluzione per inviare contemporaneamente 6 variabili di tipo int (ingressi analogici) ad un secondo arduino ricevendone da esso altrettanti indietro utilizzando comunicazione seriale?
con tutti i tipi di software che avevo utilizzato per test (atoi, parseint) mi si è sempre accumulato un ritardo nel loop (credo causato da un errato codice) fino anche a 5 secondi e talvolta uno slittamento delle variabili.
grazie mille
Signori si nasce, io spero di esserlo diventato

astrobeed

credi che possa usare questa soluzione per inviare contemporaneamente 6 variabili di tipo int (ingressi analogici) ad un secondo arduino ricevendone da esso altrettanti indietro utilizzando comunicazione seriale?
Certo che si, però ti rammento che quanto spiegato si applica solo ad un set dati già pronto all'uso, non ha nulla a che vedere con il protocollo di ricezione/invio dei dati che deve comunque essere interpretato.
Se l'invio/ricezione dei dati avviene non frequentemente, p.e. una volta al secondo, puoi semplicemente preparare l'array da inviare tramite l'unione e poi usare una serial.print(buffer) per inviare tutto quanto in una volta sola, dal lato ricezione non devi fare altro che attendere la ricezione del corretto numero di dati, p.e. 12 byte nel caso di sei interi, per poi recuperare i vari dati dai singoli elementi della struttura.

signorbarro

grazie per la risposta, infatti l'avevo notato tentando di utilizzarlo.
giusto per curiosità, ho provato il tuo schetch, che in uscita mi manda questa seriale:

abcdTest@â

forse ho frainteso, ma non avrei dovuto ricevere abcdTest123456654321


credo di aver capito la tua spiegazione infatti pensavo al discorso della struttura a byte definiti in ricezione (anche se in realtà vorrei una frequenza di trasmissione un po' maggiore) per poter in pratica ottenere i valori degli analogici del primo arduino sul secondo.
giusto una curiosità, quando recupero per esempio un valore dalla mia struttura in ricezione il mio arduino capisce che è un numero oppure lo interpreta come una stringa? 

buonanotte e grazie di nuovo


Signori si nasce, io spero di esserlo diventato

Go Up