Aggiornare valori letti da seriale su una nuova finestra
-
Salve a tutti, ho un problema con la velocità di aggiornamento dei valori in una finestra.
Partiamo dal principio: ho un microcontrollore STM32F7 che legge i dati provenienti da due encoder e da un sensore analogico e li invia tramite seriale (velocità 460800bps) al computer a 100hz circa.
La struttura contenente i dati acquisiti (due valori uint32 -encoder1, encoder2- , un int32 -analog_in- e 8bit del carattere di controllo '$') accupa 104bit, che vengono "impacchettati" attraverso una union.
Sul lato pc il mio programmino in ambiente QT si occupa di leggere i valori della seriale all'arrivo del readyRead() e li memorizza a sua volta in una union "gemella" (united_data.buffer_csv):QObject::connect(stm32_board, SIGNAL(readyRead()), this, SLOT(readSerial()));
La porta seriale è "stm32_board", mentre la funzione readSerial() è quella che si occupa di leggere i dati:
void Speak_Serial::readSerial() { uint32_t size = stm32_board->bytesAvailable(); stm32_board->flush(); if(size<SIZESTRUCT) { return; } stm32_board->read(temp_buffer, 1); while (temp_buffer[0]!='q') { stm32_board->read(temp_buffer, 1); } stm32_board->read(united_data.buffer_csv, SIZESTRUCT-1); QByteArray buffertemp; buffertemp = QByteArray(temp_buffer,1); //copio il carattere di controllo char tempo[SIZESTRUCT]; memcpy(tempo,temp_buffer,1); //tempo e' una var temporanea memcpy(tempo+1,united_data.buffer_csv,(SIZESTRUCT-1)); //ora in tempo è contenuto tutto united_data.buffer_csv memcpy(united_data.buffer_csv,tempo,SIZESTRUCT); count_frame++; // contatore a scopo debug qDebug() << united_data.csv_array.start_char[0] << "," << united_data.csv_array.encoder1 <<","<<united_data.csv_array.encoder2-<<","<<united_data.csv_array.analog_in; }
Se provo a stamparli a video attraverso un semplice qDebug() non ho alcun problema, i dati vengono mostrati senza alcun delay significativo e senza perdite.
A questo punto ho provato ad integrare il codice in un programma più complesso e sono iniziati i dolori.
La MainWindows() configura la porta e invia un segnale alla presenza di dati sulla seriale , chiamando una funzione che a sua volta -lette e organizzate le variabili- invia un altro segnale per aggiornare i valori (visualizzati attraverso QLCDNumber) in un' altra finestra.
La cosa funziona ma con latenze mostruose (anche 20sec) e sembra che i valori non vengano aggiornati contemporaneamente: non riesco a capire se il problema può dipendere dai signal
che forse non sono il sistema migliore per un sistema real time o se la velocità di aggiornamento dei valori in una finestra grafica ha delle limitazioni ....
Grazie mille a tutti! -
Non capisco perche' tu abbia complicato a tal modo il programma, usi 3 buffer temporanei che sono probabilmente inutili.
Ad ogni modo, per risolvere il problema aggiungireadSerial();
come ultima riga.in pratica tu stai implicitamente assumendo che
stm32_board->bytesAvailable();
sia sempre<=SIZESTRUCT
. Ma non e' cosi'. un singolo readyread puo' portare con se multipli segnali -
Forse la funzione "singola" non è chiarissima: se stm32_board->bytesAvailable() restituisce un valore inferiore a SIZESTRUCT , ovvero se non ci sono abbastanza dati per riempire la struttura, la funzione esce e aspetta un'altra chiamata .
Se a questo punto i dati sono sufficienti a riempire la struttura (che in realtà viene inviata e ricevuta come flusso di dati non formattato attraverso una union) allora estrae i dati e li stampa.
Quel brutto giro di memcpy e buffer temporanei c'è perchè in realtà il primo byte (il carattere di controllo $) non verrà archiviato nella struttura finale, quindi ci sarà solo:stm32_board->read(united_data.buffer_csv, SIZESTRUCT);
con SIZESTRUCT lungo solo 12byte, quindi temp_buffer e tempo (che avevo usato x debug) andranno a sparire.
Quello però che non mi convince è che, dopo aver effettuato la lettura, nel momento in cui vado a emettere un segnale per aggiornare i valori nella seconda finestra, il sistema si rallenta, come se la visualizzazione grafica e i due signal creassero delle eccessive latenze. -
-
Attualmente non ho sottomano il "programmone" (è sul pc fisso, purtroppo mi sono dovuto allontanare dall'ufficio e ho solo il portatile...) quindi la possibilità di fare prove è un po' limitata.
La logica della funzione readSerial (ma forse non è poi così logica :-) ) dovrebbe essere questa:- Lettura di quanti byte sono disponibili sulla seriale
Se sono insufficienti al riempimento della struttura, il sistema esce e aspetta una nuova chiamata. - Se sono sufficienti, vengono letti un numero di byte pari a "SIZESTRUCT" (es 12byte delle 3 variabili int32) e riempite le variabili con i valori acquisiti. Fatto ciò, verranno liberati "SIZESTRUCT" byte sulla porta e questa ora conterrà "size-SIZESTRUCT"byte , con:
size = stm32_board->bytesAvailable(). - La funzione ,dopo aver stampato e fatto poco altro, termina e passa il controllo al main, che -grazie al readyRead()- emetterà ancora un segnale (dal momento che rileva dati sulla seriale) e la porta verrà ancora letta, riprendendo il ciclo di controllo dei byte presenti sulla porta e ricominciando l'intero ciclo.
Bisogna tener conto che i dati vengono generati a circa 100Hz , quindi circa 9600bps teorici. Leggendo a 460800bps -per motivi tecnici- non ci dovrebbero essere problemi di progressivo "intasamento" del buffer.
Potrei introdurre un ciclo for per far scaricare tutto il buffer all'interno della funzione readSerial(), ma visto che il sistema mi ha retto anche 1000 letture al secondo -che non mi servono!- , mi è sembrato inutile e meno "pulito". La funzione readSerial come riga finale dovrebbe fare la stessa cosa ,anche se in maniera più concisa e forse elegante.
Domani , codice alla mano, proverò a fare qualche esperimento.
Ero curioso di sapere se la tecnica SIGNAL/SLOT era utilizzabile , da qualche parte ho letto essere poco efficiente per chiamate continue ma volevo sentire altri pareri.
Il problema potrebbe essere legato al meccanismo con cui vengono creati i valori QLCDNumber, esistono ad esempio widget più efficaci ?
Grazie mille ancora - Lettura di quanti byte sono disponibili sulla seriale
-
Risolto!
Chiedo scusa se scrivo oggi ma ho potuto metter mano al codice solo ora.
Inserendo un ciclo for che libera tutto il buffer PRIMA dell'uscita dalla funzione ( o , come consigliatomi da VRonin richiamando readSerial() alla fine della funzione readSerial()), il programma corre liscio, senza rallentamenti.
In pratica prima succedeva questo:-
Chiamata funzione readSerial() da parte del segnale (readyRead)
-
Verifica dati sufficienti a caricare la struttura
-
Se sì, caricamento dei dati nella struttura. Eventuali dati già disponibili saranno caricati al successivo passaggio.
-
Uscita dalla funzione
-
Nuova chiamata da readyRead in caso di nuovi dati sulla seriale ( o di dati ancora presenti).
Adesso invece il ciclo è:
-
Chiamata funzione readSerial() da parte del segnale (readyRead)
-
Verifica dati sufficienti a caricare la struttura
-
Se sì, caricamento dei dati nella struttura.
-
Verifica di eventuali dati in grado ancora di riempire la stuttura.
-
Se sì, caricamento dei dati nella struttura in modo ciclico, fino a svuotare il buffer.
-
Uscita dalla funzione
-
Nuova chiamata da readyRead in caso di nuovi dati sulla seriale .
In pratica nel secondo modo (sintetizzabile in una nuova chiamata di readSerial() alla fine della stessa readSerial(), come suggerito da VRonin) i dati già presenti sul buffer vengono scaricati dalla funzione stessa in modo autonomo, senza attendere nuove chiamate SIGNAL/SLOT.
Grazie mille ancora a tutti e in particolare a VRonin! -
-
Risolto!
Chiedo scusa se scrivo oggi ma ho potuto metter mano al codice solo ora.
Inserendo un ciclo for che libera tutto il buffer PRIMA dell'uscita dalla funzione ( o , come consigliatomi da VRonin richiamando readSerial() alla fine della funzione readSerial()), il programma corre liscio, senza rallentamenti.
In pratica prima succedeva questo:-
Chiamata funzione readSerial() da parte del segnale (readyRead)
-
Verifica dati sufficienti a caricare la struttura
-
Se sì, caricamento dei dati nella struttura. Eventuali dati già disponibili saranno caricati al successivo passaggio.
-
Uscita dalla funzione
-
Nuova chiamata da readyRead in caso di nuovi dati sulla seriale ( o di dati ancora presenti).
Adesso invece il ciclo è:
-
Chiamata funzione readSerial() da parte del segnale (readyRead)
-
Verifica dati sufficienti a caricare la struttura
-
Se sì, caricamento dei dati nella struttura.
-
Verifica di eventuali dati in grado ancora di riempire la stuttura.
-
Se sì, caricamento dei dati nella struttura in modo ciclico, fino a svuotare il buffer.
-
Uscita dalla funzione
-
Nuova chiamata da readyRead in caso di nuovi dati sulla seriale .
In pratica nel secondo modo (sintetizzabile in una nuova chiamata di readSerial() alla fine della stessa readSerial(), come suggerito da VRonin) i dati già presenti sul buffer vengono scaricati dalla funzione stessa in modo autonomo, senza attendere nuove chiamate SIGNAL/SLOT.
Grazie mille ancora a tutti e in particolare a VRonin!@lagodolio said in Aggiornare valori letti da seriale su una nuova finestra:
senza attendere nuove chiamate SIGNAL/SLOT.
Chiarimento minore:
readyRead
non viene chiamata di nuovo solo perche' ci sono dati non letti, viene chamata solo se arrivano nuovi dati dalla seriale -