Partendo dalla necessità di sviluppare uno strumentino da pannello per visualizzare grafici e valori numerici con caratteri di varie dimensioni, ho rinunciato al classico LCD con standard HD44780 per utiizzare invece un pannello “grafico” ovvero con la possibilità di settare un pixel alla volta. Se vi interessa l’argomento vi consiglio davvero di comprenderne a fondo la logica di funzionamento, così potrete creare liberamente effetti grafici performanti adattandoli alle vostre esigenze.

Scegliendo che prodotto acquistare, vanno considerate diverse cose:

  • Il tipo di connettore
  • Il tipo di controller
  • La dimensione in pixel
  • Il colore della retroilluminazione
  • La tensione di contrasto dell’LCD

Riassumendo brevemente questi parametri, ho scelto un prodotto che avesse dei fori sullo stampato a passo standard 2.54mm, così da poter agevolmente saldare dei cavetti/connettori e rendere il tutto flessibile come primo prototipo.

Come controller ho scelto il KS0108, perché cercando online ho trovato molta documentazione ed informazioni sul suo funzionamento nel dettaglio.

Per la dimensione in pixel la mia scelta è ricaduta nel 128×64, il quale internamente è suddiviso in due controller separati ma con le stesse connessioni: inviando l’informazione al primo controller potete lavorare sul riquadro 64×64 pixel in alto, mentre con il secondo controller lavorate sul 64x64px in basso. avessi acquistato un 192×64 pixel avrei trovato tre controller separati. In realtà la gestione è più semplice di quello che pensate, lo vedrete più sotto.

Io non utilizzerò la retroilluminazione perché devo mantenere i consumi bassissimi, ma verificando nel datasheet ad esempio la retroilluminazione bianca consuma molto meno rispetto ad altri colori (tuttavia la vita in ore sembra minore!): da tenere in considerazione.

Come ultimo punto, parlando della tensione di regolazione del contrasto, alcuni LCD richiedono una tensione negativa: diventa una grossa noia perché non è facile ottenere una tensione negativa con una scheda prototipo se non avete i componenti necessari a disposizione, inotre non mi sembrava il caso impegnare un alimentatore da banco solo per ottenere la tensione negativa in questione. Il modello che ho scelto, nonostante abbia questa peculiarità, è provviso di un convertitore DC/DC interno in grado di fornire le tensione negativa necessaria: è sufficiente collegarlo con un trimmer al pin del contrasto ed il gioco è fatto… In realtà ho avuto qualche noia con il contrasto perché ho scoperto -dopo una minuziosa e noiosissima ricerca- che il pin di reset non va collegato a VCC direttamente: va invece utilizzata una resistenza di pull-up da almeno 10kohm per limitare la corrente.

Avendo a disposizione diverse FRDM_KL25Z, che utilizzo come programmatori per micro ARM, ho deciso di sfruttarne una per pilotare il display grafico in questione. Come spiegato in questo articolo KL25Z come programmatore OpenSDA ho prima di tutto preparato la board in modo tale da poterla programmare agevolmente via USB (con il jumper J6 inserito per collegare il clock SDA con il clock del micro della scheda!).

Prima di connettere l’LCD alla board ho verificato nel dettaglio di che connessioni si tratta e come potete notare sono tutti pilotabili con I/O digitali, quindi molto semplice:

  • CS1, CS2: abilitano il controller 1 oppure il controller 2, cioè le due metà dello schermo utilizzabili;
  • DB0-DB7: vanno abililtati per descrivere il byte di dati in uscita verso l’LCD;
  • EN: pin di Enable, il quale avvisa il controller in uso di considerare valido il byte descritto sui pin DB0-DB7;
  • RW/DI: indicano al controller che tipo di operazione voglio effettuare (lettura, scrittura, comando);
  • RST: pin di reset del micro, deve restare a livello alto per mantenere i controller attivi;
  • Vee: contrasto dell’LCD, va collegato con apposito trimmer, vedesi schema più sotto.

Poi ho connesso l’LCD alla board controllando lo schematico della stessa in quanto alcuni pin del micro potrebbero essere open drain/collector (in quel caso per ottenere un livello logico alto è necessario per forza aggiungere un pull-up) oppure come nel caso del pin “PTD1” essere in comune con il LED RGB montato sulla scheda… Direi da evitare se possibile.

Graphical LCD pin diagram

In questo schema potete vedere tutte le connessioni necessarie, ma tenete ben presente la resistenza di pull-up sul pin RST. Mi raccomando: prima di alimentare il tutto assicuratevi di non superare la tensione indicata dal datasheet e di aver collegato correttamente i pin, altrimenti rischiate di compromettere irrimediabilmente i controller! A questo punto non mi resta che buttar giù un pò di codice! Senza entrare nel dettaglio specifico di Codewarrior con compilatore gcc-arm, vi spiego piuttosto come comunicare correttamente con il controller KS0108 così sarete in grado di utilizzarlo indipendentemente dal linguaggio,compilatore o micro utilizzato.

Visto che prima di tutto i controller vanno inizializzati, e per farlo dobbiamo dobbiamo inviare loro dei comandi, prepariamoci prima di tutto il codice necessario per inviare un byte all’LCD.

Nel mio caso, utilizzando CodeWarrior, setto i pin in ProcessorExpert indirizzandoli come indicato nello schema più sopra, e preparo due funzioni per impostarli come uscite e gestire i livelli logici come necessario.

// questa funzione setta tutti i pin I/O relativi ai bit D0-D7 come uscite
void DATA_DIR_OUT() {
	LCD_D0_SetOutput();
	LCD_D1_SetOutput();
	LCD_D2_SetOutput();
	LCD_D3_SetOutput();
	LCD_D4_SetOutput();
	LCD_D5_SetOutput();
	LCD_D6_SetOutput();
	LCD_D7_SetOutput();
}

// questa funzione suddivide il byte da trasmettere in singoli bit
// e setta a livello alto o basso i pin corrispondenti DB0-DB7
void WR_DATA(char lcddata) {
	
	LCD_D0_PutVal((lcddata) >> 1);
	LCD_D1_PutVal((lcddata >> 1) & 1);
	LCD_D2_PutVal((lcddata >> 2) & 1);
	LCD_D3_PutVal((lcddata >> 3) & 1);
	LCD_D4_PutVal((lcddata >> 4) & 1);
	LCD_D5_PutVal((lcddata >> 5) & 1);
	LCD_D6_PutVal((lcddata >> 6) & 1);
	LCD_D7_PutVal((lcddata >> 7) & 1);
}

// manda un semplice impulso sul pin EN
void send_enable() {
	LCD_EN_SetVal();
	Delay100us(1); // delay di 1 microsecondo
	LCD_EN_ClrVal();
}

// seleziona il controller
//    0               1
// CS1 = 1         CS1 = 0
// CS2 = 0         CS2 = 0
void ChipSelect(bool chip)
{   
      if (chip == 0)
      {
            LCD_CS2_ClrVal();
            LCD_CS1_SetVal();
      }
      if (chip == 1)
      {
            LCD_CS1_ClrVal();
            LCD_CS2_SetVal();
      }
}

glcd output bits
Il risultato è di trasmettere il byte bit per bit distribuendolo sui pin DB7-DB0. Questa viene chiamata normalmente trasmissione parallela. Oltre a trasmettere il bit però è anche necessario indicare al controller quali sono le mie intenzioni. Sfogliando il datasheet dell’GLCD ho trovato tutte le informazioni necessarie al driving dei controller.
control bits e data bits
Ho tolto da questo schema tutte le altre funzioni per concentrarci sulla funzione più importante, quella per attivare il pannello “Display on/off” e sulla funzione per scrivere un byte “Write data”. Inoltre ho suddiviso per colore i bit di controllo dai bit dati, rispettando i colori dello schema elettrico. Manca il pin di Enable, “EN” perché quest’ultimo è molto semplice: prima si preparano i bit di controllo ed il bit dati, poi si da un piccolo impulso con il bit EN per dire all’LCD “ok sono pronto, ascoltami”.

Ricapitolando, per attivare il display devo settare RW a 0, DI a 0, ed inviare il BYTE “3F” seguito da un impulso di ENABLE:

void InitDisplay(void)
{
	DATA_DIR_OUT();
	LCD_DI_ClrVal();
	LCD_RW_ClrVal();
	WR_DATA(0x3F);
        ChipSelect(0);
}

Se ora volessimo accendere un pixel sullo schermo, sarebbe sufficiente mandare un byte di dati. Ma un attimo… un byte? E se io volessi accendere un solo bit? Giusto, ma di questo ne parleremo dopo. La domanda ora è: dove comparirebbe questo pixel? Non abbiamo creato nessuna funzione per il posizionamento del “cursore” di scrittura. Torniamo quindi al datasheet ed aggiungiamo qualche dettaglio alla tabella delle funzioni:

ks0108 goto xy istructions

Quindi abbiamo un comando chiamato set page che corrisponde all’asse X, ed un comando set address che corrisponde all’asse Y. Entrambi sembrano lavorare con RW e DI a zero, però mentre set address va da 0 a 63, set page va solo da 0 a 7 (zero compreso, quindi solo 8 posizioni): questo perché sull’asse X scriviamo 8 bit alla volta, ovvero il nostro byte! Ovvio che quindi non serviranno 64 posizioni, bensì 64/8, ovvero 8.

Allora preparariamo le nostre funzioni di posizionamento e di scrittura di un byte:

// selezione della pagina (asse X diviso in 8 byte)
void PageSelect(unsigned char page)
{
        // setto RW e DI come da tabella
	LCD_RW_ClrVal();
	LCD_DI_ClrVal();
        // unisco (B8) ovvero 10111 come da tabella
        // con i 3 bit del numero di pagina e li invio all'LCD
	WR_DATA(0xB8 | page);
	send_enable();
}

// selezione della riga (asse y diviso in 64 punti)
void RowSelect(unsigned char row)
{
        // setto RW e DI come da tabella
	LCD_RW_ClrVal();
	LCD_DI_ClrVal();
	WR_DATA(0x40 | row);
	send_enable();
}

// questa funzione invia un byte al controller attualmente attivo
void WriteByte(char chardata)
{
DATA_DIR_OUT(); // setto pin DB0-DB7 come uscite
LCD_RW_ClrVal(); // RW a livello logico 0
LCD_DI_SetVal(); // DI a livello logico 1
Delay100us(1); // attendo 1uS per la ricezione del comando
WR_DATA(chardata); // setto bit per bit le uscite
send_enable();
}

Fatto questo non ci resta che provare a mandare un primo byte allo schermo, nella posizione 0,0px, modificando il main in questo modo:

InitDisplay(void);  // inizializzo display
ChipSelect(0);      // selezioni chip 0
PageSelect(0);      // x = 0
RowSelect(0);       // y = 0
WriteByte(0xFF);    // accendo 8 pixel di fila
while(1);           // ciclo infinito di attesa

Se avete fatto tutto correttamente, vi apparirà una linea di 8px. Cambiando 0xFF con ad esempio 0x01 si accenderà un solo pixel, mentre 0xAA alterna pixel acceso con pixel spento. Potete anche provare a cambiare i valori di PageSelect e RowSelect, per vedere che effetto fa sul posizionamento dei pixel.

glcd byte sent

Da questo iniziale risultato è possibile partire per sviluppare tutte le funzioni grafiche come la scrittura di caratteri oppure la visualizzazione di immagini bitmap, ma di questo ne parlerò in un secondo articolo!

Trasformare la freedom board KL25z in una USBDM Debugger interface permette di ottenere un programmatore per varie MCU Freescale o altri dispositivi compatibili (vedi qui)

  • RS08
  • HCS08,
  • HC12,
  • Coldfire V1-4 microcontrollers
  • MC56F800xx Digital Signal Controllers (very limited range of devices).
  • Kinetis ARM-cortex

E’ necessario prima di tutto preparare l’hardware come mostrato nel precedente articolo riguardante la KL25Z come programmatore OpenSDA (vedi qui). Preparata la board con le modifiche necessarie, non installate OpenSda, ma seguite i seguenti passi:

Scaricate i driver USBDM al seguente link:

opensda

Quando avete installato i drivers, scaricate a questo link (verificate sia l’ultima versione) il software relativo ad USBDM:

download programs

Avviate il setup e durante la procedura assicuratevi di abilitare l’installazione dei componenti relativi al vostro ambiente di sviluppo (nel mio caso Codewarrior 10.6.4) e anche del programmatore Stand-alone, che preferisco come semplicità, o come piano B in caso l’ambiente di sviluppo non riesca ad integrare i plugin necessari:

setup

Completato il setup non vi resta che preparare la vostra FRDM-KL25Z per aggiornarla all’applicazione USBDM Debug. Sfogliate la cartella di default per l’installazione del software sul vostro pc, troverete “PGO” ed al suo interno la cartella creata dall’ultimo setup USBDM. All’interno di quest’ultima entrate nella cartella “FlashImages” e quindi “MKxx”, riferito alla famiglia del micro nella board. Sul mio pc il percorso completo è “C:\Program Files (x86)\pgo\USBDM 4.12.1.70\FlashImages\MKxx”.

All’interno di questa cartella troveremo il file “OpenSDAvX.X.bin” dove la X sta ad indicare la versione disponibile al momento. Tenete questo file a portata di mano!

usbdm flash application

E’ giunto il momento di collegare la board al computer con un cavetto usb, tenendo premuto il tasto di reset un secondo o due, questa si avvierà in modalità bootloader e vi comparirà come periferica di archiviazione di massa usb (Bootloader). Se avete internet attivo, aprendo il file “sda_info.htm” potete verificare le versioni di bootloader e app attualmente caricate nella flash. Noi comunque andremo ad aggiornare tutto quanto, per trasformare la board in un programmatore “semi-universale” via SWD.

20160426_152446

Infatti, non vi resta che caricare il file “OpenSDAvX.X.bin” nella periferica di archiviazione di massa, attendere qualche secondo, poi sconnettere e riconnettere il dispositivo. A questo punto, come conferma di avvenuto aggiornamento del firmware, windows vi avviserà che “è stato rilevato un nuovo hardware”: saranno caricati automaticamente i drivers relativi ad USBDM, ed il dispositivo verrà riconosciuto come programmatore da Codewarrior e dal programmatore stand-alone sviluppato per USBDM.

Fate attenzione: con Windows 7  e Windows 10 per motivi ancora ignoti non sono riuscito ad aggiornare il firmware della board come da ultimo passaggio, però avevo a disposizione un computer con Windows XP ed in quel caso ha funzionato immediatamente. Questo passaggio va fatto solo una volta quindi ad oggi non ho approfondito ulteriorimente il problema.

Per verificare se funziona tutto, potete collegare il connettore SWD alla board che volete programmare ed avviare il programmatore stand-alone (nel mio pc è in “C:\Program Files (x86)\pgo\USBDM 4.12.1.70\UsbdmFlashProgrammer.exe”):

stand alone usbdm flash programmer test

Prima di tutto, dalla scheda “Interface” tramite il tasto “Detect” ho verificato il corretto riconoscimento della board KL25Z rilevata come programmatore SWD. Successivamente, nella scheda “Target” (immagine più a destra) tramite il tasto “Detect Chip” eseguo una veloce verifica del dispositivo che ho connesso via SWD. Riconosce correttamente il mio micro ed il suo ID!

Tuttavia, inizialmente non fu così semplice, in quanto il micro “MKV10” non veniva riconosciuto, essendo presente nella lista ma non completamente configurato. Ho tentato di configurarlo manualmente inserendo tutti i parametri come da datasheet, ma senza successo. Ho deciso così di contattare direttamente lo sviluppatore di USBDM Peter O’Donoghue, il quale stava casualmente affrontando lo stesso problema: così in meno di 24h ne siamo venuti a capo ed ora funziona perfettamente! Sicuramente, con micro meno recenti o con più fortuna non avrete lo stesso mio inconveniente.

Alla prossima!

Trovate altri riferimenti a questo link:
https://mcuoneclipse.com/2013/04/27/debug-external-processors-with-usbdm-and-freedom-board/