Seriale 0.23 vs 1.02 vs 1.51

ho caricato il medesimo sketch con il quale ricevo una stringa inviata da processing La stringa e' di 500 caratteri e la ricevo sulla usb quindi uart pin 0-1 a 115200bps

Lo sketch caricato con IDE 0.23 ne riesce a ricevere ~150 Lo sketch caricato con IDE 1.02 ne riesce a ricevere ~70 Lo sketch caricato con IDE 1.51 ne riesce a ricevere ~70

come mai c'e' questa differenza ? C'e' un modo per ricevere tutta la stringa di 500 caratteri ?

Testato: ho caricato il medesimo sketch con il quale ricevo una stringa inviata da processing La stringa e' di 500 caratteri e la ricevo sulla usb quindi uart pin 0-1

Lo sketch caricato con IDE 0.23 ne riesce a ricevere ~150 Lo sketch caricato con IDE 1.02 ne riesce a ricevere ~70 Lo sketch caricato con IDE 1.51 ne riesce a ricevere ~70

come mai c'e' questa differenza ? C'e' un modo per ricevere tutta la stringa di 500 caratteri ?

Dall'IDE 1.x in poi la seriale è gestita via interrupt ed il buffer interno è stato dimezzato. Prova intanto ad aumentare i buffer modificando il file: 1.0.x: hardware/arduino/cores/arduino/HardwareSerial.cpp 1.5.1: hardware/arduino/avr/cores/HardwareSerial.cpp alla riga 59 trovi "64", metti "128", il valore che c'era nelle versioni 002x.

Testato: C'e' un modo per ricevere tutta la stringa di 500 caratteri ?

Devi svuotare il buffer della seriale mano a mano che arrivano i caratteri altrimenti lo riempi e quelli in più vanno persi. Fino all'IDE 023 il buffer era di 128 caratteri, gestito da interrupt in ricezione e tramite polling in trasmissione senza nessun buffer. Dall'IDE 1.x il buffer è di 64 caratteri in ricezione e 64 caratteri in trasmissione, tutti e due sono gestiti tramite interrupt quindi per inviare tot caratteri Arduino non si ferma fino a che non sono stati trasmessi tutti i caratteri come avveniva con l'IDE pre 1.x

Ho allargato a 128 il buffer descritto da Leo, e poi anche a 256

#if (RAMEND < 1000)
#define SERIAL_BUFFER_SIZE 16
#else
#define SERIAL_BUFFER_SIZE 256 // Originalmente 64
#endif

Il risultato e’ ottimo, con 128 sono ritornato alle vecchie prestazioni e con 256 le ho superate.

Domande:
0: Parliamo di 256Byte o bit ?
1: Quale e’ la contropartita ? Sono 256Byte/bit fermi, sempre allocati giusto, quindi tolti alla Ram ?, Ci sono altri problemi ?
2: Quell #if (RAMEND < 1000) che significa ? Forse che se la Ram scende sotto i 1000 Byte (oppure bit) il buffer si autoriduce ?
3: Astro io non ho usato nessun delay, la velocita’ di lettura e svuotamento buffer e’ al massimo, c’e’ un solo delay di un millesimo di secondo nel codice proprio della lettura seriale

while (Serial.available()) 
         {
	  delay(1);
	  if (Serial.available() >0) 
                 {
	          char c = Serial.read();

Posso provare a toglierlo o non serve ?
Tu hai altre idee che possano non farmi modificare manulmente il buffer ?

0 e 1: Parliamo di BYTE quindi se metti 256 come valore del buffer, consumi (256*2)=512 byte del quantitativo totale di memoria. Ricorda che i buffer sono 2 (RX e TX). Sono ovviamente byte PERSI se inizializzi la seriale e poi non la usi. 2: se la memoria totale (RAM) è inferiore a 1000, inizializza i buffer ristretti. E' un check per chip che hanno poca memoria RAM 3: quel delay(1) non serve a nulla. Essendo ricezione e trasmissione gestite via interrupt, una volta che hai messo il/i byte nel buffer corrispondente, vengono spediti finché non sono finiti

non avevo pensato al *2, ecco perche' allargand a 512 non andava piu' nulla :stuck_out_tongue_closed_eyes:

dai miei test risulta questo: con buffer a 128Byte riesco a ricevere sui 128, piu' o meno poco piu' poco meno, sembrerebbe che lui riempie tutto il buffer e poi lo svuota alla fine, (e' corretto dire che se invio 128 caratteri sulla seriale sto' inviando 128byte ? e nella Ram di arduino vengono occupati 128Byte ?)

con buffer a 256Byte riesco a ricevere massimo 220caratteri, se ne mando di piu' salta la Ram a volte fino al riavvio.

Non mi ritrovo invece sul delay di cui sopra: se lo tolgo ricevo solo una decina di caratteri se lo aumento non cambia nulla, resta solo un po' piu' freezato l'lcd durante la ricezione

Testato: non avevo pensato al *2, ecco perche' allargand a 512 non andava piu' nulla :stuck_out_tongue_closed_eyes:

Saturazione completa :stuck_out_tongue_closed_eyes:

dai miei test risulta questo: con buffer a 128Byte riesco a ricevere sui 128, piu' o meno poco piu' poco meno, sembrerebbe che lui riempie tutto il buffer e poi lo svuota alla fine, (e' corretto dire che se invio 128 caratteri sulla seriale sto' inviando 128byte ? e nella Ram di arduino vengono occupati 128Byte ?)

Non del tutto. La trasmissione standard PC/Arduino è 8N1, quindi 8 bit di dati, nessun bit di parità, 1 bit di stop, a cui si somma 1 bit di start, per cui ogni carattere in transito è composto da 10 bit al posto degli 8 di 1 byte. Questo però riguarda solo la trasmissione: una volta ricevuto, del carattere vengono memorizzati solo gli 8 bit dei dati, per cui in RAM alla fine ci sarà 1 byte.

con buffer a 256Byte riesco a ricevere massimo 220caratteri, se ne mando di piu' salta la Ram a volte fino al riavvio.

Saturazione ]:D

Non mi ritrovo invece sul delay di cui sopra: se lo tolgo ricevo solo una decina di caratteri se lo aumento non cambia nulla, resta solo un po' piu' freezato l'lcd durante la ricezione

Spiegami allora il motivo per cui hai messo quel delay(1) perché non l'ho capito.

vero, i bit di start e stop. ma i 10bit di ogni carattere entrano nel buffer oppure lo star e stop viene eliminato in hardware dalla uart ?

Il delay 1 e' presente di defoult in tutti gli sketch che gestiscono la seriale, non l'ho messo io, e cmq ripeto che ho rpovato a toglierlo e non riceve quasi piu' nulla, riceve solo una decina di caratteri, quindi e' giusto che ci sia, ed e' giusto che sia di un millisecondo

Testato: vero, i bit di start e stop. ma i 10bit di ogni carattere entrano nel buffer oppure lo star e stop viene eliminato in hardware dalla uart ?

Sono segnali usati solo in trasmissione. Fai finta proprio che non esistano. Tu devi ragionare: spedisco 1 byte, mi arriva 1 byte. Non devi fare i conti: allora, 8 bit ma diventano 10 in trasmissione che però tornano 8 quando li salvo... diventi grullo per nulla :stuck_out_tongue_closed_eyes:

Il delay 1 e' presente di defoult in tutti gli sketch che gestiscono la seriale, non l'ho messo io, e cmq ripeto che ho rpovato a toglierlo e non riceve quasi piu' nulla, riceve solo una decina di caratteri, quindi e' giusto che ci sia, ed e' giusto che sia di un millisecondo

Mai visto prima. Novità per me. Dov'è che c'è?

in giro c'e' un po' dappertutto, credevo fosse uno standard, vedi ad esempio questo che usa addirittura 100ms http://wiring.org.co/cgi-bin/yabb/YaBB.pl?num=1218069222

cmq se lo tolgo puff, solo 10 caratteri arrivano. a questo punto posso provare a cambiare metodo di ascolto della seriale, quale mi consigli ? ho visto ad esempio che uno degli esempi ufficiali si riserva uno spazio proprio per la strnga da ricevere, vedi esempi-Communication-SerialEvent, e' buona cosa secondo te ?

void setup() {
  // initialize serial:
  Serial.begin(9600);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
}

Ultima idea che mi e' venuta, ma non c'e' modo di dichiarare buffer differenti per rx e tx. se non devo spedire nulla sono veramente sprecati 256byte per tx

Testato: in giro c'e' un po' dappertutto, credevo fosse uno standard, vedi ad esempio questo che usa addirittura 100ms http://wiring.org.co/cgi-bin/yabb/YaBB.pl?num=1218069222

L'esempio citato è agganciato ad un post del 2008, però su Wiring. Conosci Wiring? Com'era Wiring nel 2008? Usava la seriale gestita da interrupt oppure no? Io non so risponderti. Un delay(1) a che serve? A far sì che arrivi un carattere altrimenti esci dal check? Allora forse è da rivedere quel check? Boh, senza vedere tutto il codice non so risponderti. Io non ho mai usato delay di 1 ms prima di controllare la seriale.

cmq se lo tolgo puff, solo 10 caratteri arrivano. a questo punto posso provare a cambiare metodo di ascolto della seriale, quale mi consigli ? ho visto ad esempio che uno degli esempi ufficiali si riserva uno spazio proprio per la strnga da ricevere, vedi esempi-Communication-SerialEvent, e' buona cosa secondo te ?

void setup() {
  // initialize serial:
  Serial.begin(9600);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
}

Secondo me questo l'hanno fatto per aggirare il buffer di 64 byte senza metter mano alla libreria. Quindi è una soluzione che va bene anche per utenti non capaci/non desiderosi di armeggiare con i file del core.

Ultima idea che mi e' venuta, ma non c'e' modo di dichiarare buffer differenti per rx e tx. se non devo spedire nulla sono veramente sprecati 256byte per tx

Certamente. Basta cambiare quella porzione di codice che ti ho linkato inserendo 2 define al posto di uno (per mettere la dimensione del buffer RX e di quello TX) e poi modificare i file CDC.cpp e HardwareSerial.cpp: ci sono 19 occorrenze in cui compare SERIAL_BUFFER_SIZE, devi controllarle tutte e verificare se sono da cambiare.

attenzione che sono buffer circolari: se lo riempi non rimani in attesa ma sovrascrivi i dati più vecchi. il fatto che se mandi più di circa 200 caratteri si impalla tutto è perchè immagino che hai creato la stringa di 200 e passa caratteri, quindi un 3° "buffer" nella ram! anche se la salvi in flash nel momento in cui viene usata mi pare venga caricata in un'area ram ad hoc. Se invece in un for scrivi i numeri da 0 a 1000, ti accorgerai del fatto del buffer circolare (perdita di dati) però dovresti vedere arrivare gli utimi TX buffer size byte!

grazie leo del tuo parere, faro’ delle rpove con il reserve e riporto i risultati.

lesto la stringa mi arriva dal pc, non la creo internamente ad arduino.
tu cosa intendi ?
la stringa che ricevo resta sempre nella ram, non la scrivo mai su flash, perche’ cambia spesso, cioe’ ogni volta che premo un pulsante sul pc parte sta stringa via seriale, e la acchiappo da arduino che la visualizza su lcd, quindi non la scrivo mai sulla flash di arduino. se spengo arduino perdo la stringa, e’ voluto.

io credo funzioni cosi’, datemene conferma:
Quando inizio a ricevere la stringa i caratteri vanno nel buffer, ci entrano a 115200bps. Arduino per conto suo, in modo asincrono, inizia a prendere carattere per carattere e metterlo nel buffer, che sarebbe un pezzo della ram.
Pero’, questo e’ importante, ad ogni carattere che preleva dal buffer e lo passa in ram, lo cancella dal buffer. Quindi non e’ che i 200 caratteri occupano 200byte nel buffer e 200 nella ram, ne occupano sempre 200 in tutto, anche durante l’operazione di trasferimento.

E’ corretto ?

Non potendo velocizzare arduino, mi verrebbe da dire: Basta che abbasso a 9600 la seriale di arduino ?
In questo modo mi rrivano piu’ lentamente i caratteri ? (questo va anche nella direzione indicata da Astro credo)

Astro quando dici:

astrobeed:
Dall’IDE 1.x il buffer è di 64 caratteri in ricezione e 64 caratteri in trasmissione, tutti e due sono gestiti tramite interrupt quindi per inviare tot caratteri Arduino non si ferma fino a che non sono stati trasmessi tutti i caratteri

Intendi che non ci sono problemi in trasmissione ? non parli di ricezione. In ricezione il fatto che sia gestita da interrupt e’ irrilevante rispetto al problema Ring ? cioe’ di sovrascrittura a rotazione dello spazio di memoria ?

karma per tutti :slight_smile:

I buffer di cui si parla sono solo quelli SW, cioè gli array creati e gestiti da HardwareSerial.cpp e .h. Come buffer hardware l'Atmega328 ha 2 registri e basta. Il primo è un registro a scorrimento in cui viene composto il dato man mano che arrivano i suoi bit. Una volta che è stata completata la ricezione il dato viene copiato in un altro registro, da cui il codice può leggerlo. Quando viene copiato in questo secondo registro, viene attivato un interrupt di ricezione terminata e dato pronto. E' a questo punto che scatta la ISR, che preleva il byte e lo copia nel buffer SW di cui stiamo parlando.

la stringa mi arriva dal pc

ok, ma il problema rimane, credo, vedi dopo

Pero’, questo e’ importante, ad ogni carattere che preleva dal buffer e lo passa in ram, lo cancella dal buffer. Quindi non e’ che i 200 caratteri occupano 200byte nel buffer e 200 nella ram, ne occupano sempre 200 in tutto, anche durante l’operazione di trasferimento.

ok, il byte viene letto ed entra nella prossima cella del buffer, se il buffer è pieno sovrascrive il byte più vecchio. Lo spazio del buffer è fisso.
Però poi prelevi il byte, e lo metti nel buffer dei dati destinati all’LCD. Sempre che possieda un buffer SW. Magari questo buffer esiste ed è dinamico, quindi è la libreria dell’LCD che intasa la ram. Che libreria usi?

Non potendo velocizzare arduino, mi verrebbe da dire: Basta che abbasso a 9600 la seriale di arduino ?
In questo modo mi rrivano piu’ lentamente i caratteri ?

sì ma è un pagliativo, bisogna capire bene da che parte arriva l’intasamento della ram o comunque ciò che impalla il micro.

Intendi che non ci sono problemi in trasmissione ? non parli di ricezione. In ricezione il fatto che sia gestita da interrupt e’ irrilevante rispetto al problema Ring ? cioe’ di sovrascrittura a rotazione dello spazio di memoria ?

stiamo parlando di ricezione, quindi il problema non si pone. In trasmissione prima la scrittura ti bloccava il codice finchè tutto non era scritto; ora tutto viene salvato in un buffer circolare e l’invio lavora tramite interrupt. Quindi se scrivi nel buffer più velocemente di quanto esso si svuoti vai a riscrivere i byte più vecchi che verranno persi. Ma chissene, ora pensiamo al TX che è più semplice usando un sistema “universale” in tutte le versioni della lib.

non si dice palliativo ? :)

cmq la libreria e' la standard dell'IDE arduino

uff precisino :P

la libreria è bloccante, quindi in teoria nessun problema da quella parte... uhmmm molto strano

stavo pensando pero', che al primo invio io ricevo sti 200byte, li metto in ram, e quindi in ram sono occupati 200byte, quando invio per la seconda volta i 200Byte prima devono essere ricevuti, e poi vanno a sostituire i 200 vecchi giusto ?

dato = stringadaseriale

ora quando ricevo i nuovi 200byte, nell'istante prima che vengono copiati in "dato" la ram e' occupata sia dalla stringa vecchia che dalla nuova secondo voi ? a questo punto occuperei 400Byte per un tot di tempo

che poi, scusate se rompo troppo, ma voi potete fare una prova in tal senso ? quindi un programmino che invia 500 caratteri via seriale ad arduino. se anche voi avete problemi e' interessante.

allora esiste un piccolo buffer HW, una specie di micro ram dedicata alla seriale (credo forse un paio di registri, ma non ho mai letto nulla al riguardo). quando arriva un byte seriale, viene parcheggiato in questo buffer e viene lanciato un inerrupt. l’interrupt prende questo byte e lo “sposta” nel buffer seriale.

Il buffer seriale è creato dal setup, ed è fisso, quindi dal memnto in cui chiami setup hai X byte di RAM in meno ma non cambiano più.