PIC 32 + LCD 16x2

Stampa
( 3 Votes ) 
Valutazione attuale:  / 3
ScarsoOttimo 
Categoria principale: Elettronica Categoria: Microcontrollori
Data pubblicazione
Scritto da teppei68 Visite: 3426

PIC32 e LCD (16X2) HD44780

Introduzione

L'intento di questa pubblicazione è quello di rendere chiunque in grado di poter interfacciare il proprio pic qualunque esso sia con l'LCD alfanumerico hd44780, il nostro pic di riferimento sarà il 32 già anticipato nella pubblicazione precedente sarà inoltre aggiunta la funzione interrupt TMR1, diciamo in generale che  un display qualunque esso sia alfanumerico,grafico B/N o grafico a colori ha fondamentalmente bisogno di due cose: una istruzione ed un dato, mi spiego meglio se voglio scrivere o disegnare su di un foglio
la prima cosa che  eseguo è posizionare la matita o la penna nel punto in cui  voglio scrivere o disegnare e una volta posizionato comincio a scrivere o disegnare.
Quindi incosapevolmente mi sono dato una prima istruzione che è un "comando" il quale dice posizionati nel punto x del foglio e successivamente scrivi il "dato" che è un carattere o altro. Per display un pò più complessi come i grafici a colori il principio rimane chiaramente inalterato anche se il "comando" si traduce in una "serie" di comandi;
anche l'interfaccia di comunicazione può essere diversa tra un display e l'altro e quindi si adotteranno i vari protocolli (I2C,seriale,SPI ect.) ma in sostanza il principio rimane.


Diamo un'occhiata alla realizzazione:

panoramica

SCHEMA ELETTRICO:

schema
schema12

Esaminiamo lo schema elettrico:

Si può notare che il pilotaggio dell' LCD avviene con linea dati a 4 bit,mentre la linea di controllo avviene con linea bus a 3 bit, potete chiaramente aggiungere pulsanti e qualsiasi altra cosa vi venga in mente, la retroilluminazione LCD la gestite voi come meglio credete importante è rispettare le specifiche del datasheet, generalmente si alimenta con 4,2 volt (max intensità) e poi si può diminuire in base alle vostre esigenze, l'alimentazione +val 5 volt si suppone sia già opportunamente filtrata . Le porte del PIC possono essere, se non sono comode quelle da schema, cambiate l'importante è che poi le modificate anche nel file sorgente, nel nostro caso abbiamo:

RF1 = EN => il pin 59 del pic(RF1) sarà collegato al pin 6 dell'LCD (EN) quindi nel sorgente: #define EN PORTFbits.RF1 
RD7 = RW
RF0 = RS 

La stessa procedura si avrà per l'assegnazione di tutti gli altri pin PIC=>LCD e su tutto quello che si vorrà aggiungere (pulsanti,led ect).

Esaminiamo l'LCD 16x2 con microcontrollore HD44780:

PIN 1: Gnd.
PIN 2: + Val 5 volt .
PIN 3: Contrasto da 0 a + Val (vedi schema elettrico).
PIN 4: RS

  • cioè register selector , in fase di scrittura il valore presente nella linea dati D7=>D0 (cioè 8 bit) andrà a:
  • se RS=0 si attiva il registro ad 8 bit chiamato IR (Instruction Register) e quindi si accede alle impostazioni LCD:
  • Inizializzazione LCD (font, pulisci LCD ect).
  • In quale riga visualizzare il carattere.
  • In quale colonna visualizzare il carattere.
  • Cursore ect.
  • se RS=1 si attiva il registro ad 8 bit DR (Data Register) e quindi visualizzazione del carattere.

PIN 5: R/W,

  • se R/W=0 si sta scrivendo nella memoria interna LCD,
  • se R/W=1 si sta leggendo la memoria interna LCD (per verificare  lo stato di D7 busy flag).

PIN 6: E che significa “enable” in realtà è il vero sincronizzatore quindi il CLOCK dell’ LCD.

Vediamo la sua funzione:

CICLO SCRITTURA:
timing write
RS e R/W sono settati contemporaneamente, si carica nella linea dati D7/D0 il dato o l’istruzione, dopo un ritardo di 60 n/sec (Tas), viene settato E=1 , dopo un ritardo di 450 n/sec (Tw)si setta E=0, il valore presente nella linea dati viene scritto nel microcontrollore HD47780 sul fronte di discesa di E . il valore dev’essere mantenuto nella linea dati almeno altri 10 n/sec (Th) e RS R/W per altri 20n/sec (Tah) dopo che E è stato settato a 0.

Tradotto in C ansi: R/W=0;
                               RS=0/1;
                               PORTx=valore che si vuole (8 bit);
                               delay 60 usec;
                               E=1;
                               delay 450 usec;
                               E=0;
                               delay 1 msec;

CICLO LETTURA:



Il ciclo lettura si effettua generalmente per verificare il busy flag fornito su D7, Il microcontrollore dell’LCD richiede del tempo per eseguire delle istruzioni interne, se è occupato non rileverà nessun comando o istruzione presente nella linea dati. Quindi D7=1 significa microcontrollore occupato, mentre D7=0 significa che il microcontrollore è pronto a gestire altre istruzioni.

PIN 7 => PIN 14: Linea dati 8 bit dove D7 (pin 14)è il bit più significativo (MSB) e D0 (pin7) il meno significativo (LSB), D7 ha inoltre la funzione di busy flag.
PIN 15/16: Led illuminazione LCD (guardatevi il datasheet del vostro lcd perchè i terminali di tale LED variano da LCD a LCD compresa la sua alimentazione), quindi troverete
                  costruttori che pongono il LED retroilluminazione nella posizione pin 1,2 oppure 15,16.

CARATTERISTICHE DEL MICROCONTROLLORE LCD HD44780
CGROM Character Generator Read Only Memory, la Memoria interna del microcontrollore LCD contenente i font dei 208 caratteri da 5x8 punti e dei 32 caratteri da 5x10 punti. Il carattere che si desidera visualizzare viene identificato all’interno di questa memoria, secondo la seguente tabella :

lcd char rom

e che si può concretizzare nella tabella codici ASCII:

tabella-ascii

Per visualizzare il carattere "a" bisognerà inviare il numero (all'LCD) che lo identifica al’interno della memoria e cioè in decimale 97 in hex 0x61 oppure in binario 0b11000001. La tabella ASCII prevede 256 tra caratteri ed istruzioni ovviamente nella memoria dell'LCD troveremo solo i caratteri.

CHARACTER GENERATOR RANDOM ACCESS MEMORY
Il microntrollore dell’LCD inoltre mette a disposizione 8 bytes di CGRAM, in questa memoria si possono salvare dei caratteri personalizzati precedentemente creati. Gli indirizzi di suddetta memoria vanno da 0x00 a 0x07, verificare, però, sempre il datasheet.
a tal proposito un programmino molto utile per creare simboli personalizzati è CUSTOM LCD CHARACTER il quale ha la possibilità di fornirci i dati opportuni per ricreare il simbolo voluto in matrice.

DISPLAY DATA RANDOM ACCESS MEMORY
Nel caso di un LCD 20 caratteri per 4 righe abbiamo a disposizione 80 bytes di memoria DDRAM.
Le locazioni di memoria identificano in quale riga e colonna visualizzare il carattere, esempio nella tabella qui sotto abbiamola rappresentazione fisica delle locazione della DDRAM in un LCD 20x4 (se fosse di un 16x2 le locazioni totali sarebbero 32). Se vogliamo visualizzare un carattere in riga 1 colonna 1 la locazione di memoria interessata per la visualizzazione del nostro carattere sarà 0x80, fare attenzione però perchè anche qui alcuni costruttori variano le locazioni tipo in cambio dell'iniziale 0x80 si può trovare 0x40,
quindi verificare sempre anche qui le specifiche dal datasheet.

Ricapitolando: per visualizzare un carattere il microcontrollore LCD necessita di una istruzione o comando (riga/colonna) ed una informazione (carattere) ed ora vediamo come si traduce in C ansi:

  • - Istruzione: R/W=0;           // scrittura
  • - RS=0;                               // istruzione
  • - PORTX=0x80;                 // valore dell'istruzione cioè posizionati su riga 1 colonna 1
  • - delay 60 usec;
  • - E=1;
  • - delay 450 usec;
  • - E=0;
  • - delay 1 msec;                   // dopo questo ritardo il microcontrollore è pronto a processare altri comandi
  • - informazione: R/W=0;      // scrittura
  • - RS=1;                               // informazione
  • - PORTX=0x61;                 // valore dell'iinformazione cioè carattere "a"
  • - delay 60 usec;
  • - E=1;
  • - delay 450 usec;
  • - E=0;
  • - delay 1 msec;                   // dopo questo ritardo il microcontrollore è pronto a processare altri comandi

la sequenza deve essere rigorosamente rispettata e cioè 1)istruzione 2)informazione per ogni carattere che si vorrà visualizzare. Il ritardo a fine ciclo delay 1 msec serve per aspettare che il microcontrollore dell'LCD termini tutte le sue operazioni interne altrimenti bisogna mettersi in polling su D7 (busy flag) affinchè D7=0, sinceramente trovo meno dispendioso il ritardo, anche perchè su un LCD 20x4 in poco meno di 200 msec scrive l'intero LCD quindi si ha una percezione visiva di una scrittura simultanea di tutto l'LCD.
Vedremo poi che nell'implementazione del sorgente i ritardi "delay 60 uSec" ,previsti peraltro dai cicli lettura/scrittura, saranno omessi in quanto non si è mai verificata una criticità
nel funzionamento (*).

Inizializzazione dell'LCD

In fase di accensione come per i PIC anche il microcontrollore LCD necessita di una inizializzazione:

  • - Per i PIC si inizializzano i registri TRISx se ingressi/uscite, le PORTx se inizializzarle a 0 o 1 ect.
  • - Per l'LCD si inizializza l'interfaccia se 4 o 8 bit , il font del carattere se 5x8 o 5x10 ect.

Quindi l'inizializzazione dell'LCD seguirà immediatamente dopo, nel nostro listato, l'inizializzazione del PIC.

Come da datasheet per la fase d'inizializzazione il costruttore prevede:

  • - Dopo aver alimentato l'LCD aspettare che la +Val arrivi a regime ed il microcontrollore predisponga
  • - I propri registri e porte si impone un ritardo di almeno 40 msec.
  • - Inviare il dato con il tipo di interfaccia 4 o 8 bit il dato non terrà conto dei 4 bit meno significativi quindi 0b0011xxxx.
  • - Ritardo di almeno 4,1 msec che per comodità noi poniamo 5 msec.
  • - Reinviare il dato con il tipo di interfaccia 4 o 8 bit il dato non terrà conto dei 4 bit meno significativi quindi 0b0011xxxx.
  • - Ritardo di almeno 100 usec che per comodità noi poniamo 1 msec.
  • - Reinviare il dato con il tipo di interfaccia 4 o 8 bit il dato non terrà conto dei 4 bit meno significativi quindi 0b0011xxxx.
  • - Ritardo di almeno 100 usec che per comodità noi poniamo 1 msec.


Attenzione non è un errore di battitura il ripetersi delle istruzioni questa è la sequenza inizializzazione LCD!! Si utilizza per inviare queste istruzioni all'LCD il ciclo di scrittura precedentemente spiegato. A questo punto abbiamo ottenuto la sicronizzazione dell'interfaccia del microcontrollore ma mancano ancora da definire on/off LCD, quale font se 5x8 o 5x10, il cursore se visualizzarlo o meno, se pulire l'LCD ect. Quindi insieme alle istruzioni precedenti seguiranno :

PORTX=0x38; 0b00111000 8 bit, 2 lines, font 5x7

PORTX=0x08; 0b00001000 Display OFF

PORTX=0x0C;0b00001100 Display ON, cursor OFF

PORTX=0x01; 0b00000001 Display clear

Ogni volta che si vuole eseguire l'istruzione "display clear" il microcontrollore necessita almeno un ritardo di 2 msec.

Vediamo ora nel dettaglio il set d'istruzioni dell'IS (INSTRUCTION REGISTER):

Istruzione

D7

D6

D5

D4

D3

D2

D1

D0

Clear LCD

0

0

0

0

0

0

0

1

Cursore a capo

0

0

0

0

0

0

1

x

Control LCD

0

0

0

0

1

D

C

B

Function

0

0

1

DL

N

F

x

x

 

Bit

0

1

D

LCD off

LCD on

C

Cursore off

Cursore on

B

Blink cursore off

Blink cursore on

DL

Interfaccia 4 bit

Interfaccia 8 bit

N

1 linea

2 o più linee

F

Font 5x7

Font 5x10

Se confrontiamo le tabelle con le istruzioni illustrate precedentemente troviamo il comando relativo al commento.

Passiamo ora al sorgente:

/* *******************************************************************************
 * Program: Start LCD 16x2, PULS and LED, internal clock and      *
 *          minimal configuration pin (see datasheet).                            *
 * MCU: PIC32MX440F128H    64 Pin QFN                                      *
 * Compiler: C32 V2.02                                                                      *
 * Development software: MPLAB IDE 8.83 + PICKIT2                     *
 * Author : Mirko Musto 30/01/2013                                                   *
 * *******************************************************************************/ 
#include <p32xxxx.h>
#include                                          // Adds support for PIC32 Peripheral Library functions and macros
#include <16x2.h>                                        // full support 16x2
#include                                 // delay support only ms
 
#pragma config FNOSC    = FRCPLL          // Oscillator Selection internal clock 8mhz
#pragma config FPLLIDIV = DIV_2               // PLL Input Divider  
#pragma config FPLLMUL  = MUL_24          // PLL Multiplier
#pragma config FPLLODIV = DIV_8             // PLL Output Divider
#pragma config FPBDIV   = DIV_1                // Peripheral Clock divisor  
#pragma config FWDTEN   = OFF                // Watchdog Timer
#pragma config WDTPS    = PS1                  // Watchdog Timer Postscale
#pragma config FCKSM    = CSDCMD                    // Clock Switching & Fail Safe Clock Monitor
#pragma config OSCIOFNC = OFF                       // CLKO Enable
#pragma config POSCMOD  = OFF                        // Primary Oscillator   
#pragma config IESO     = OFF                       // Internal/External Switch-over
#pragma config FSOSCEN  = OFF                       // Secondary Oscillator  
#pragma config CP       = OFF                       // Code Protect
#pragma config BWP      = OFF                       // Boot Flash Write Protect
#pragma config PWP      = OFF                       // Program Flash Write Protect
#pragma config ICESEL   = ICS_PGx2                  // ICE/ICD Comm Channel Select
#pragma config DEBUG    = OFF                       // Debugger  
 
#define puls   PORTDbits.RD1
#define yellow PORTCbits.RC14
#define green  PORTCbits.RC13

***NOTA BENE : i define  per la linea bus EN, RS, RW e per la linea dati D7, D6, D5, D4 sono impostati nel file 16x2.h**********

int main(void){
 
    mPORTCClearBits                (BIT_13 | BIT_14 );                                   // Turn off leds before configuring IO pin as output
    mPORTDClearBits             (BIT_3| BIT_7);                                              // clear RD3 output TX UART1,RD7 RW
    mPORTEClearBits                (BIT_0 | BIT_1 | BIT_2 | BIT_3 );              // clear RE0,1,2,3
    mPORTFClearBits                (BIT_0 | BIT_1 );                                       // clear RF0,1
    mPORTCSetPinsDigitalOut        (BIT_13 | BIT_14 );                             // Set RC13 and RC14 as outputs  
    mPORTDSetPinsDigitalIn         (BIT_1);                                                // Set RD1 as input
    mPORTDSetPinsDigitalIn      (BIT_2);                                                  // Set RD2 as input RX UART1
    mPORTDSetPinsDigitalOut      (BIT_3 | BIT_7);                                  // Set RD3 as output TX UART1
    mPORTESetPinsDigitalOut        (BIT_0 | BIT_1 | BIT_2 | BIT_3 );     // Set PORTE as output
    mPORTFSetPinsDigitalOut        (BIT_0 | BIT_1 );                              // Set RF0 and RF1 as outputs     
 
    OpenTimer1(T1_ON | T1_SOURCE_INT | T1_PS_1_256, 0xFFE);            // configure the core timer roll-over rate (250msec)
    ConfigIntTimer1(T1_INT_ON | T1_INT_PRIOR_2);
             
    INTEnableSystemMultiVectoredInt();
 
    initLCD();    
    DelayMs(1000);    
    stringLCD(0x80," Hello World!   ");
    stringLCD(0xC0,"  PIC32MX440F   ");
    DelayMs(5000);
    cleanLCD();                          
while(1){                                            // endless loop
            DelayMs(20);
            if(puls==0)
            {
                stringLCD(0x80," Welcome to :");
                stringLCD(0xC0,"INFOPORTAL.IT ");
                yellow=!yellow;
                DelayMs(2000);
            }
        }        // end endless loop
        }        // end main
 
 
 
void __ISR(_TIMER_1_VECTOR, ipl2) _Timer1Handler(void)                // routine interrupt
{
        green=!green;                // flicker green Led   
    mT1ClearIntFlag();                 // clear the interrupt flag
}

Contenuto della libreria : 16x2.h.

DelayMs(40);                                                                                            // Inizializzazione LCD
RS=0; RW=0;                                                                                           // RS=0 significa che il dato presente in PORTX e’ una istruzione RW=0 scrittura
PORTX=0x20;                                                                                         // carico 0x20 = interfaccia 4 bit
DelayUs(60);                                                                                           // se avete problemi con le delay questo ritardo puo’ essere omesso
EN=1;                                                                                                     // setto enable E=1
DelayUs(450);                                                                                       // se avete problemi con le delay potete mettere 1 msec in cambio dei 450 Usec
EN=0;                                                                                                    // setto enable E=0
DelayMs(5);
EN=1; DelayUs(450); EN=0;
DelayMs(1);
EN=1; DelayUs(450); EN=0;
DelayMs(1);
PORTX=0x28;                                                                                    // imposto interfaccia 4 bit il font 5x7..ect
EN=1; DelayUs(450); EN=0;
DelayMs(1);
PORTX=0x08;
EN=1; DelayUs(450); EN=0;
DelayMs(1);
PORTX=0x0C;
EN=1; DelayUs(450); EN=0;
DelayMs(1);
PORTX=0x01;
EN=1; DelayUs(450); EN=0;
DelayMs(2);                                                                                                 // Fine inizializzazione

RS=0; RW=0;
PORTX=0x80;                                                         // riga 1 colonna 1
DelayUs(60);                                                           // se avete problemi con le delay questo ritardo puo’ essere omesso
EN=1; DelayUs(450); EN=0;
DelayMs(1);                                                             // dopo questo ritardo il microcontrollore è pronto a processare altri comandi

RS=1; RW=0;                                                         // RS=1 significa che il dato presente in PORTD e’ un dato RW=0 scrittura
PORTX=0x61;                                                       // carattere "a"
DelayUs(60);                                                         // se avete problemi con le delay questo ritardo puo’ essere omesso
EN=1; DelayUs(450); EN=0;
DelayMs(1);}

Questa è la base della libreria LCD da dove con le spiegazioni precedenti ogniuno sarà in grado di costruire la propria!!
 La libreria delay invece dipenderà dal vostro pic e potrete utilizzare le librerie preconfezionate dalla microchip, per quanto riguarda invece il PIC32
leggetevi l'articolo precedente in aggiunta spieghiamo l'interrupt e la sua libreria:

OpenTimer1(T1_ON | T1_SOURCE_INT | T1_PS_1_256, 0xFFE); // configure the core timer roll-over rate (250msec)

Utilizzando le librerie risulta il tutto molto semplice ed intuitivo, T1_ON “accende” il TMR1, la sorgente dell'interrupt è l'overflow del contatore stesso T1_SOURCE_INT, il prescaler infine settato a 256 T1_PS_1_256, ed infine abbiamo un registro identificato come periodo nei PIC inferiori tanto per capirci era fisso a 256 qui invece si può impostare una comodita non da poco.

ConfigIntTimer1(T1_INT_ON | T1_INT_PRIOR_2);
Con questa stringa si abilita il Timer 1 e si setta il livello della priorità .

INTEnableSystemMultiVectoredInt();
Si abilitano gli interrupt.

E questa è la nostra routine nel nostro esempio fa lampeggiare un LED.

void __ISR(_TIMER_1_VECTOR, ipl2) _Timer1Handler(void) // routine interrupt

{

green=!green; // flicker green Led

mT1ClearIntFlag(); // clear the interrupt flag

}
Ed ecco una foto del progetto ultimato con i miei saluti :

x 

 


Joomla 1.7 Templates designed by College Jacke