Domande su codice oscilloscopio

Salve a tutti.

Ho passato un pò di tempo in cerca di un software che simuli un oscilloscopio (tipo visual analyzer) che accetti dei campioni in ingresso dalla seriale e non dalla schedaaudio , ma non sono riuscito a trovare niente di serio. Per questo sto cercando di comprendere per poi rielaborare, l'oscilloscopio presentato qui http://accrochages.drone.ws/en/node/90 (il sito ha qualche problema per il momento).

Il funzionamento è abbastanza semplice (P.S.: se usate Ubuntu ricordatevi di disabilitare il compix altrimenti vi crasha tutto). Quello che non capisco è l'utilità di inviare i dati con questa serie di comandi:

  Serial.print( 0xff, BYTE);
  Serial.print( (val >> 8) & 0xff, BYTE);
  Serial.print( val & 0xff, BYTE);

il primo è una sorta di "marcatore", per indicare al programma in processing che "sta arrivando il dato" (ma anche questo non si potrebbe evitare??)

poi non capisco l'utilità di eseguire l'operazione di shift e di concatenazione!!! Non sarebbe più semplice inviare direttamente il dato numerico??

Oltre a questo, qualcuno sa indicarmi qualche testo o guida (possibilmente in italiano) per documentarmi per bene su tutto quello che si dovrebbe implementare (tipo algoritmi di interpolazione, come acquisire un transitorio, come velocizzare le operazioni di lettura, ecc...)

Grazie a tutti!!!

poi non capisco l'utilità di eseguire l'operazione di shift e di concatenazione!!!

Non conosco il contesto di quelle operazioni. Però "(val >> 8) & 0xff" da un risultato che è sempre zero nel caso "val" vale 0xFF o meno, mentre se è maggiore di 0xFF restituisce > 0.

Poi considera che "val" è un "int" che contiene almeno 10 bit significativi sempre che "val" riceve il dato acquisito da ingresso analogico, mentre BYTE e un tipo grande 8 bit.

Ciao.

divite l'integer in due byte e li invia singolarmente. In effetti può essere evitato usando la Serial.write

lesto: divite l'integer in due byte e li invia singolarmente. In effetti può essere evitato usando la Serial.write

L'int può comunque essere diviso usando lowByte e highByte, se proprio vuol seguire quello schema di trasmissione.

@MauroTec: si, val è il risultato di un analogRead();

Ancora non capisco il perchè. E' un problema di velocità?? anche dal lato processing, non è più facile lavorare direttamente con un dato di tipo integer anzichè dover ricostruireil dato?

sì, seinvii il numero come char, se ha una cifra devi inviare un byte per la cifra + uno di fine numero. totale 2 byte, se il numero è di 2, 3 o 4 cifre, impieghi un byte addizionale per ogni cifra.

Inviando invece parte bassa e parte alta, a priori sai che un int è di 2 byte, quindi non solo invii solo 2 byte, ma non ti serve nemmeno avere il byte i fine numero, se sincronizzi bene il programma PC con arduino, perchè sai che ogni 2 byte devi trasformarlo in int. Ovvio che se nopn è sincronizzato, rischi di leggere il bytedi un numero con un altro....

però mi sta venedo il dubbio che la print(x, BIN), stampi un carattere (quindi un byte) per ogni bit, quindi in realtà peggiorando la velocità della seriale stampando 64 caratteri per byte...

Questo si che ha un senso!!!

Vediamo se ho capito bene:

val >> 8 : fa lo shift a destra di val; se per esempio val vale 00001000.00000000 (2048) diventa 00000000.00001000 (8) [ho messo i punti per distinguere i due byte); & 0xff: fa l'and con 11111111.11111111 ; ma a che serve??? non ritorna sempre il valore del primo elemento (in questo caso val >>8);

e poi invia l'intero val in formato byte????

non è più semplice usare qualcosa del tipo

Serial.print(lowByte(val),BYTE);
Serial.print(highByte(val),BYTE);

Ed in più, non sarebbe più veloce visto che si inviano, in tutto, 2 byte (mentre nel codice originale si inviano 4 byte)??

Sicuramente avrete capito che sono un pò confuso sulle operazioni con i bit, e su come ottimizzare la trasmissione seriale. Abbiate pazienza!!! :cold_sweat:

Fa l'AND con 0xff, cioè 11111111. E' un modo un po' "grezzo" per dire al sistema di prendere i primi 8 bit più bassi ignorando gli altri.

Come ti ho suggerito, usa le funzioni precostituite di Arduino lowByte e highByte. Nel tuo esempio. highByte(00001000.00000000) restituisce 00001000 lowByte(00001000.00000000) restituisce 00000000

val >> 8 : fa lo shift a destra di val; se per esempio val vale 00001000.00000000 (2048) diventa 00000000.00001000 (smiley-cool [ho messo i punti per distinguere i due byte); & 0xff: fa l'and con 11111111.11111111 ; ma a che serve??? non ritorna sempre il valore del primo elemento (in questo caso val >>smiley-cool;

hai ragione! forse serve per forzare il numero ad essere un byte? bho (leo conferma questa idea)

Ed in più, non sarebbe più veloce visto che si inviano, in tutto, 2 byte (mentre nel codice originale si inviano 4 byte)??

non si inviano 4 byte, ma 128! sono andato a controllare:

Serial.print(78, BIN) gives "1001110"

ciò vuol dire che ogni 0 e 1 stampto è un char, ovvero un byte, ovvero 8 bit!!

se posti anche il codice lato PC allora si capisce meglio!

invece facendo:

int numero=78;
Serial.write( &numero, sizeof(int) )

vai a scrivere solo 2 byte (o meglio sizeof(int), notare come sia comodo per cambiare il tipo di variabile, o un array ), notare anche la write al posto della print: la print forza tutto quello che viene stampato ad essere una stringa leggibile agli esseri umani usando la tabella ascii: invece la write scrive direttamente i bite fregandose allegramente, e permettendo di sfruttare al 100% la banda della seriale.

Certo, sei obbligato a interpretare i valori tramite un programma lato PC, che legga i 2 byte, li unisca e poi faccia la conversione in stringa da stampare, ma il processo a lato pc è molto più veloce che da lato arduino, e con la seriale al massimo (115200 baud), anzichè inviare 115200/128=900 letture al secondo, ne invii 115200/2=57600!!

Non fa l'AND con 11111111.11111111 ma con 11111111. E' differente. Serve comunque solo per prendere i primi 8 bit più bassi, come ho detto. E' un sistema "sicuro" per avere un risultato lungo 1 byte. Con val>>8 avresti lo shift a destra ma continueresti sempre a trattare un int. Aggiungengo l'AND selezioni solo 8 bit.

Cmq, RIPETO per ter: usa lowByte e highByte. Sono più pulite.

Inoltre, come ha detto lesto, spedire un int spezzato in 2 byte, comporta una piccola operazione per recuperare il valore. E poi un int tiene anche il segno, per cui una volta "rimontato" il numero te lo ritrovi già segnato.

lesto:

val >> 8 : fa lo shift a destra di val; se per esempio val vale 00001000.00000000 (2048) diventa 00000000.00001000 (smiley-cool [ho messo i punti per distinguere i due byte); & 0xff: fa l'and con 11111111.11111111 ; ma a che serve??? non ritorna sempre il valore del primo elemento (in questo caso val >>smiley-cool;

hai ragione! forse serve per forzare il numero ad essere un byte? bho (leo conferma questa idea)

Ed in più, non sarebbe più veloce visto che si inviano, in tutto, 2 byte (mentre nel codice originale si inviano 4 byte)??

non si inviano 4 byte, ma 128! sono andato a controllare:

Serial.print(78, BIN) gives "1001110"

ciò vuol dire che ogni 0 e 1 stampto è un char, ovvero un byte, ovvero 8 bit!!

sono andato a controllare male: lui fa BYTE, non BIN, qindi il codice è già ottimizzato.

E' un sistema "sicuro" per avere un risultato lungo 1 byte. Con val>>8 avresti lo shift a destra ma continueresti sempre a trattare un int.

vero, ma la pritnt (x, BYTE), dovrebbe già troncare.

size_t HardwareSerial::write(uint8_t c)

Questo su dovrebbe essere il metodo più veloce per inviare il dato, che prevede sia "unsigned int 8 bit", quindi devi usarlo due volte, inviando prima il low byte e poi high byte. Tuttavia devi considerare le due chiamate a funzione di classe write.

Mentre il metodo "write" suggerito da lesto usa Print::write il cui prototipo è: size_t Print::write(const uint8_t *buffer, size_t size)

Come vedi prende un puntatore a interi 8 bit sensa segno chiamato buffer, per questo nella chiamata è stato inserito l'operatore riferimento "&" prima "numero"

Solo che non ho provato se con numero > di 255 (es 1000) funziona, dovrebbe perché un int è grande 16bit e sono scritti in modo contiguo in ram per cui si può usare l'aritmetica dei puntatori. Questo potrebbe essere anche più veloce di quello precedente e lo sarebbe di sicuro se questo metodo fosse "inline", ma è virtual e volendo lo puoi reeimplementare.

Comunque per aumentare la velocità devi trovare un metodo di impacchettare i dati campionati salvandoli prima in un buffer, ci sarebbe anche da vedere se è possibile usare le struct e/o union per ridurre i bit inutilizzati. Ad esempio il dato analogico può essere massimo 1023 dato i 10 bit, in pratica del byte alto ci sono solo due bit significativi. Inoltre sarebbe anche il caso di fare la cosa a basso livello evitando il più possibile chiamate a funzione, il goto in questo caso porta grandi vantaggi.

Ciao.

È bello leggere queste discussioni, s'impara un botto!!

grazie.

per completezza aggiungo che per i float (e quindi i double) la cosa non è semplice come un bitshif, e bisogna usare le union:

union u_tag { byte b[4]; float fval; } u;

ora in u puoi settare fval, e quindi leggere il rispettivo valore in byte dall'array b, e viceversa. ovviamente la stessa cosa può essere usata anche con gli interi o long etc...