LCD 20x4 scrive a righe alterne

MI aiuto da solo, fintanto che qualche anima buona non decida di passare da queste parti.
Ho scaricato, via sito di gioblu, una libreria liquidcrystal440.h, dovrebbe servire per usare LCD 40x4, chissà che non abbiano risolto.

L'altra cosa, cercando su Internet, che ho letto in un paio di siti, è che il display 20x4 necessita del collegamento a 8 bit, ma nessuno spiega perché.

Almeno mi sono procurato due prove da fare, la manipolazione software funziona abbastanza bene ma è estremamente problematica.

Altre informazioni utili?

Il link ad 8 bit io so che serve solo a dimezzare la velocità di trasmissione. Questa cosa che risolva il problema della scrittura a righe alterne non la sapevo.

Comunque resta un problema non problema, secondo me. Io so che la riga è lunga X caratteri, non mando una stringa più lunga di X caratteri :smiley:

leo72:
Il link ad 8 bit io so che serve solo a dimezzare la velocità di trasmissione. Questa cosa che risolva il problema della scrittura a righe alterne non la sapevo.

non c'è scritto che lo risolve, ma usano la frase "DEVE essere gestito a 8 bit", quindi ho dedotto, più che altro sperato, che potesse essere una possibilità.

Comunque resta un problema non problema, secondo me. Io so che la riga è lunga X caratteri, non mando una stringa più lunga di X caratteri :smiley:

Ma io ho (poi ho fatto modifiche estreme, ma restiamo sul problema iniziale) un menu con sei righe, e non c'è verso di farglielo vedere come sei stringhe, ho provato anche a spegnere la seriale dopo ogni riga, ma niente, ricomincia da dove ha lasciato, quindi se non risolvo con questi due ultimi accorgimenti mi devo rassegnare ai "caratteri speciali" e tante righe di programmazione :fearful:

Te l'ho spiegato, un LCD non lavora come un editor di testo.
Devi usare il cursore: devi posizionare sempre il cursore nel punto in cui vuoi andare poi a far apparire il tuo testo, fosse anche la riga successiva.

lcd.Cursor(x,y);
lcd.Print(stringa);
lcd.Cursor(x,y);
lcd.Print(stringa);
ecc...

Considera che la posizione del cursore viene aggiornata in automatico per cui anche se scrivi un solo carattere, il cursore viene spostato. E' invisibile ma c'è: se lo rendi visibile, vedrai che va sempre a posizionarsi alla fine di quello che viene spedito in stampa. A meno che, ovviamente, che tu non lo sposti fisicamente da un'altra parte.

Sì ma il fatto è che la stringa arriva dall'altro micro, mentre il cursore lo posso impostare solo in locale, ecco perché ho fatto ricorso ai caratteri "speciali", così in base al carattere gli dò la posizione; solo che con "§" non mi funzionava, poi sono dovuto rientrare a casa :(, mentre con ^ sono riuscito a pilotare il "clear"

0x00
0x01
0x02
...
usa i primi valori di un byte come comandi.

menniti:
L'altra cosa, cercando su Internet, che ho letto in un paio di siti, è che il display 20x4 necessita del collegamento a 8 bit, ma nessuno spiega perché.

Assolutamente falso, il modo a 4 bit è previsto dal controller per far risparmiare pin di connessione, è perfettamente identico al modo a 8 bit con la differenza che li scrivi 4 per volta in due operazioni distinte.
La questione righe sfalsate dipende da come viene gestita la memoria caratteri, 80 byte, dal controller in funzione della geometria del display, questo vuol dire che la memoria non è lineare con la posizione del carattere sul display.
Normalmente la memoria caratteri è divisa in due banchi da 40 byte che rappresentano due righe, previste di default dal controller, da 40 caratteri e questo è l'unico caso in cui scrivendo di continuo i caratteri vanno nelle corrette posizioni sempre, con tutte le altre geometrie è necessario controllare da software la posizione del cursore e spostarlo sulla nuova riga, ovvero farlo puntare sulla corretta cella di memoria, quando necessario.

astrobeed:

menniti:
L'altra cosa, cercando su Internet, che ho letto in un paio di siti, è che il display 20x4 necessita del collegamento a 8 bit, ma nessuno spiega perché.

Assolutamente falso, il modo a 4 bit è previsto dal controller per far risparmiare pin di connessione, è perfettamente identico al modo a 8 bit con la differenza che li scrivi 4 per volta in due operazioni distinte.
La questione righe sfalsate dipende da come viene gestita la memoria caratteri, 80 byte, dal controller in funzione della geometria del display, questo vuol dire che la memoria non è lineare con la posizione del carattere sul display.
Normalmente la memoria caratteri è divisa in due banchi da 40 byte che rappresentano due righe, previste di default dal controller, da 40 caratteri e questo è l'unico caso in cui scrivendo di continuo i caratteri vanno nelle corrette posizioni sempre, con tutte le altre geometrie è necessario controllare da software la posizione del cursore e spostarlo sulla nuova riga, ovvero farlo puntare sulla corretta cella di memoria, quando necessario.

OK, comunque NON ho letto che risolveva il problema, più che altro era una speranza, in effetti con 4 bit funziona ed è anche velocissimo, quindi devo per forza usare il sistema di caratteri speciali per settare il puntatore; alla fine si tratta di una quindicina di comunicazioni, un po' di pazienza e risolvo.

leo72:
0x00
0x01
0x02
...
usa i primi valori di un byte come comandi.

Questo metodo forse mi incasina la vita perché lui mi restituisce i valori dei fuse in questa forma, ecco perché avevo scartato i numeri. Comunque l'importante era verificare che dialogassero correttamente e questo c'è XD e la cosa ottima è che posso usare il connettore seriale, già previsto per eventuali convertitori USB-seriali esterni, anche per questa scheda aggiuntiva. Per il resto prima sviluppo il firmware definitivo per la versione seriale e poi ne creo una versione specifica per l'LCD. Riprendo se continuo ad avere difficoltà con i "caratteri speciali".
Grazie ragazzi!

Ma devi instaurare un protocollo di trasmissione, altrimenti non ne esci.
0x00 può essere benissimo un comando come un dato, è solo la posizione in cui lo leggi che gli fa assumere il giusto significato.

leo72:
Ma devi instaurare un protocollo di trasmissione, altrimenti non ne esci.
0x00 può essere benissimo un comando come un dato, è solo la posizione in cui lo leggi che gli fa assumere il giusto significato.

Azz Leo, mi vuoi far passare la vecchiaia a fare questa cosa, ogni volta che uso un "if" devo aprire il reference per la sintassi e tu vuoi farmi costruire un protocollo? 4 caratteri significa che sul 328 i/o devo realizzarmi un buffer, controllare che prima ci sia una carattere "normale" altrimenti potrebbe essere un valore di fuse; il valore di fuse lo farei pecedere da uno spazio così farei la distinzione; praticamente se la serie di caratteri che mi arriva è:
00x0agemta questo è un carattere di controllo, se invece mi arriva 00x0 esuf_h è un valore di fuse. Potrei anche farcela, in realtà a me servono solo 5 caratteri:
clear, 1a, 2a, 3a, 4a riga, sempre in posizione 0; se mi riconoscesse cose come ^ & % $ £ mi semplificherei la vita, basta mandarli prima della stringa.
Credo di dover mettere dei ritardi dopo questi caratteri, anche con 4 bit la trasmissione è velocissima, forse era questo il problema ieri, non faceva a tempo ad eseguire il posizionamento che già gli arrivava la stringa. :roll_eyes:

Non puoi metterti a leggere sulla seriale "a caso", se salti il byte di comando, non sai più a che cosa si riferiscono i byte successivi né quanti byte devi leggere ecc...

Prendiamo come dati per inizio e fine trasmissioni rispettivamente 0xAA e 0x55 (non sono scelti a caso, sono 10101010b e 01010101b). Immaginiamo che vuoi pulire l'LCD:
0xAA
0x00 -> comando di clear
0x55

Adesso vuoi scrivere a 2,5:
0xAA
0x01 -> comando di posizionamento del cursore
0x02 -> X
0x05 -> Y
0x55

Adesso vuoi scrivere CIAO
0xAA
0x02 -> comando di scrittura del testo: qui si mette in ascolto fino all'arrivo del carattere di fine trasmissione
0x43 -> C
0x49 -> I
0x41 -> A
0x4F -> O
0x55

Come vedi hai delle situazioni in cui ti servono solo 3 byte, altre (l'ultimo caso) in cui non sai a priori quanti caratteri arriveranno. Se "pensi" con campi di grandezza predefinita, tagli fuori dei byte.

Ma quindi devo iniziare e finire ogni cosa che mando con questi due byte 0xAA e 0x55.
In seconda posizione metto 0x00, 0x01, 0x02 in base a cosa voglio fare (clear, posizionamento cursore, invio testo)
Dalla terza in poi mando (non nel caso del clear) o i valori di posizionamento o il testo
E' così?

Dubbi:
Nel trasmittente devo scrivere fisicamente 0xAA e 0x55? In questo caso il ricevente non deve analizzare quattro caratteri prima di capire che è un comando di inizio o fine stringa? Oppure c'è un modo per dire ad entrambi di trattarli come byte?

Ma il testo posso continuare a inviarlo come stringa o devo codificare in byte ogni singolo carattere?

Insomma :grin: :blush: :grin: :blush: potresti postarmi solo una riga tipica di trasmissione e ciò che devo scrivere sul ricevente per analizzarla correttamente? Una volta compresa la tecnica non ho più difficoltà ad implementare tutto.

In ogni caso questa cosa vale SOLO per l'LCD, i terminali seriali non ho possibilità di "programmarli" per i caratteri di controllo, giusto?

menniti:
Ma quindi devo iniziare e finire ogni cosa che mando con questi due byte 0xAA e 0x55.
In seconda posizione metto 0x00, 0x01, 0x02 in base a cosa voglio fare (clear, posizionamento cursore, invio testo)
Dalla terza in poi mando (non nel caso del clear) o i valori di posizionamento o il testo
E' così?

Sì.

Dubbi:
Nel trasmittente devo scrivere fisicamente 0xAA e 0x55? In questo caso il ricevente non deve analizzare quattro caratteri prima di capire che è un comando di inizio o fine stringa? Oppure c'è un modo per dire ad entrambi di trattarli come byte?

Serial.print(0x00, BYTE); //trasmittente

var=Serial.read(); //ricevente

Ma il testo posso continuare a inviarlo come stringa o devo codificare in byte ogni singolo carattere?

Te l'avevo già scritto :stuck_out_tongue:
Lo spedisci con Serial.print. Essendo la trasmissione seriale una trasmissione byte-byte, arriverà sempre come array di byte e verrà memorizzato nel buffer. Tu da lì lo prelievi byte per byte con Serial.read.

Insomma :grin: :blush: :grin: :blush: potresti postarmi solo una riga tipica di trasmissione e ciò che devo scrivere sul ricevente per analizzarla correttamente? Una volta compresa la tecnica non ho più difficoltà ad implementare tutto.

Di righe ne hai quante ti pare nei firmware che ti ho spedito. Devi controllare dove ho messo i loop di controllo sui byte di inizio trasmissione e poi gli switch per interpretare il comando ricevuto. Quando invii l'inizio delle trasmissioni, il codice del 328 I/O si mette in ascolto ed esegue tutto quello che gli viene detto di fare. Alla fine delle trasmissioni, esce dalla ricezione e ci rientra solo col codice di inizio.

In ogni caso questa cosa vale SOLO per l'LCD, i terminali seriali non ho possibilità di "programmarli" per i caratteri di controllo, giusto?

Non ho capito.

Tutto abbastanza chiaro, scusa ma per me sono cose completamente nuove, a parte serial.print e serial.read che sto già usando; ora mi è chiaro che per ogni stringa o comando devo fare un certo numero di trasmissioni, una iniziale ed una finale in formato "byte", le altre idem (se parliamo di clear o cursore) oppure normali stringhe per i testi.
In ricezione, per ogni carattere ricevuto, verifico se è un comando o un carattere di stringa e mi regolo di conseguenza.
L'ultima richiesta era riferita all'uso del serial monitor perché a volte mi arriva qualche "disturbo" e lui mi mostra caratteri strani, però quando faccio serial.print poi non ho più il controllo di cosa arriva al PC, quindi intendevo dire che mentre con l'LCD posso tentare di ignorare caratteri strani, perché intercetto il passaggio serial.read->lcd.print, col serial monitor non posso fare nulla.

Aggiungo, forse te l'avevo già detto una volta, che quanto ti metti d'impegno a spiegare in modalità "for dummies" hai una capacità che farebbe sfigurare molti miei colleghi (non me, che a spiegare sono bravo quanto te ;)), mentre io, nella veste del dummy, sono perfetto, non capisco un c... nemmeno se me le porgi col cucchiaino :blush:

Grazie Leo, a buon rendere :~

Sono a lavoro, non ho la libertà che ho a casa per stare a spiegare nei dettagli, comunque poniamo il caso che tu voglia spedire una stringa:

TRASMETTITORE:

Serial.beginTransmission(xxx);
Serial.write(0xAA); //equivale a print(xx, BYTE)
Serial.print(testo);
Serial.write(0x55);
Serial.endTransmission();

RICEVITORE

void loop() {
    do {
        if (Serial.available()) {
            comando=Serial.read();
            switch (comando) {
                case 0x00: //stampo la stringa
                    dato=0;
                    do {
                        dato = Serial.read();
                        x=0;
                        if (dato != 0x55) { //carattere terminatore
                            lcd.print(char(dato));
                            x++;
                        } else {
                            break;
                        }
                    } while ((Serial.available()) && (x<20)); // scrive finché ci sono dati o finché non si arriva a fine riga
                    break;
                case 0x01: //altri case
                    ........ ecc .....
                    break;
           }
        }
    }
}

O qualcosa di simile, insomma... codice non provato :sweat_smile:

porc... mi sa che sto perdendo colpi anche a spiegare. Leo! avevo capito sul serio tutto quanto, l'ultima parte era per "giustificare" il fatto che ti eri dovuto sobbarcare tutti quei post per farmi capire questa cosa :blush:
GIURO che non ti stavo più chiedendo il codice :blush:
Però, ormai la fatica è fatta :grin:
Quel "Transmission" mi è nuovo, non ti stai riferendo ad una lib particolare vero? io non ne sto usando nessuna, a parte quella per il keypad e per l'LCD.
Comunque domani mi metto al lavoro e provo; dal punto di vista hardware vado meglio XD, infatti ho rivisto l'intero progetto ed ho deciso di fare qualcosa di ancora più professionale, ormai la cosa merita un aspetto serio, entro fine settimana avrò le idee più chiare.
Grazie ancora. XD XD

Uhm. Forse ho fatto un po' di mix fra Seriale e I2C XD XD

Cmq, ormai che te l'ho pubblicato, fanne buon uso.
Ti volevo spiegare la logica, ma ero a lavoro e non ne avevo il tempo.
Come ti avevo detto, nel ricevitore metti tutto in loop() e controlli continuamente se c'è qualcosa nel buffer di ricezione della seriale.
Se è arrivato qualche byte, controlli che il primo byte sia quello di inizio trasmissione, così che risolvi il problema del ricevere cose a pezzi oppure a metà della trasmissione. Se infatti non è il byte di inizio, ignora tutto il resto ma, siccome la read svuota anche il buffer di 1 carattere alla volta, sei certo che se la trasmissione è stata spedita male (errore nel codice del trasmettitore) o se ne è perso qualche pezzo per strada, il buffer ti verrà svuotato in automatico 1 byte alla volta.

Mettiamo adesso che sia stato identificato il byte di inizio trasmissione: a questo punto prelevi il 2° byte in arrivo, quello che identifica il comando o l'istruzione, e lo interpreti con un bello switch.

Ho messo il caso più difficile, l'arrivo di una stringa di dati, così che puoi verificare che non esci dal ciclo finché non hai ricevuto il byte di fine delle trasmissioni. Il byte di fine delle trasmissioni ti farà uscire anche dal ciclo do..while.
A tal proposito, mi sono dimenticato un pezzo di codice.

boolean esci=false;
do {
    if (controllo) {
      switch ....:
        case 0x00:
          ......
          if (dato != 0x55) { //carattere terminatore
            lcd.print(char(dato));
            x++;
          } else {
            esci=true;
            break;
          }
          ....
          break;
       }
    } while (!esci);
  }
}

Per far uscire il codice dal ciclo do..while uso una variabile booleana che metto a true quando arriva il carattere di fine delle trasmissioni.
In questo modo eseguo sempre 1 ed 1 solo compito per volta. Richiede più codice lato trasmettitore perché ogni volta devi aprire la comunicazione col byte di inizio trasmissioni e devi chiuderla subito dopo l'invio del byte del comando e dei dati ma, per contro evita errori proprio di comunicazione.

Tutto chiarissimo, anche la logica, penso di poterci lavorare senza particolari difficoltà.
ho integrato la "esci" nel code precedente, prima avevi previsto il controllo dei 20 caratteri ma la nuova soluzione va meglio, tanto io sto ben attento a non superarli, per questioni di leggibilità, nel caso suddivido la stringa in due diverse trasmissioni, così evito il problema delle righe incrociate. Per questa ragione immagino di poter eliminare l'incrementale "x".
Un solo dubbio: i byte che stai usando 0x00, 0xAA, ecc. non corrispondono comunque a dei caratteri? se sì quali sono? Non vorrei usarne qualcuno nelle stringhe di testo per errore e combinare casini.

Cosa siano sinceramente non importa, tanto sono caratteri di "controllo", non andranno mai sul display.
Potresti usare anche 0x41, che corrisponde ad "A", ma tanto se venisse usato come carattere di inizio trasmissione non verrebbe mai interpretato come lettera da visualizzare.

leo72:
Cosa siano sinceramente non importa, tanto sono caratteri di "controllo", non andranno mai sul display.
Potresti usare anche 0x41, che corrisponde ad "A", ma tanto se venisse usato come carattere di inizio trasmissione non verrebbe mai interpretato come lettera da visualizzare.

Ah, ok, pensavo il contrario, ecco perché ero preoccupato...
Domani mi metto a fare un po' di prove, ma devo risolvere un problema al gruppo di continuità (urgente!), venerdì invece ho l'intera giornata libera e quindi conto di concludere la parte generale, poi posso procedere col progetto generale.
Comunque mi hai dato davvero un grande aiuto. Grazie Leo XD. Ora vado a nanna, sono sfinito :sleeping: