MPLAB X & XC8 – #3: Display LCD – Parte 2

Libreria XC8 per display LCD.

 

 

 

 

INTRODUZIONE
In questo articolo viene riportato un codice di esempio che fa uso della libreria per display LCD basati sull’integrato HD44780 presentata in XC8 – #3 – Parte 1.
I passi eseguiti per importare la libreria per il display sono valide per tutte le librerie.

CREAZIONE PROGETTO
Il primo passo da eseguire è la creazione del progetto, per il quale vale la stessa procedura utilizzata XC8 – #1.

 

 

IMPORTARE LIBRERIA NEL PROGETTO
Dopo aver creato il progetto è possibile importare la libreria così da poterla utilizzare all’interno del progetto. Per fare questo è sufficiente selezionare il nome del progetto nella scheda Projects in alto a sinistra, fare tasto destro >> Properties >> XC8 Compiler e selezionare il tasto “” alla voce “Include directories“.

Nella schermata che compare selezionare “Browse” e navigare fino alla cartella contenete il file LCD_HD44780.h e LCD_HD44780.c. Questi file possono essere contenuti nella stessa cartella o in cartelle separate. La cosa più pratica sarebbe usare una sola cartella, chiamata per esempio Library o con altro nome, contente tutte le librerie suddivise in due cartelle separate, una per gli header file ed una per i source file. In questo modo si ha il vantaggio di avere tutte le librerie in un solo punto e poterle modificare senza il rischio di avere diverse versioni nei vari progetti. Infatti se si dovesse modificare la libreria definita nella cartella comune Library, tutti i progetti che puntano a quel percorso verrebbero aggiornati alla prima ricompilazione. Se invece si dovesse usare una stessa libreria ma salvata in diverse cartelle sparse sul computer si avrebbe il problema di dover aggiornare le copie locali ad ogni modifica.
Dopo aver incluso il percorso o i percorsi dei due file .h e .c premere “Apply” e quindi “OK“.
Le operazioni sopra riportate sono valide per qualunque libreria voglia essere importata in un progetto.

CODICE ESEMPIO
Viene proposto un codice di esempio che fa uso di un microcontrollore PIC18F252 per pilotare un display LCD 20×4. Lo schema elettrico è il seguente:

Il progetto fa uso di 4 file, il file main.c che contiene il codice che determina ciò che viene visualizzato sul display, il file config.h per la configurazione del microcontrollore, il file config_LCD.h per la configurazione del display e il file LCD_CustomChar per definire alcuni caratteri personalizzati.

Header Files
Il file config.h è scritto utilizzando la Configuration Bits accessibile dalla toolbar selezionando Window >> Target Memory Views >> Configuration Bits. Seguire la procedura riportata in XC8 – #1 che è valida anche in questo caso. Il file config.h appare così:

#define _XTAL_FREQ 8000000

// PIC18F252 Configuration Bit Settings
// 'C' source line config statements

// CONFIG1H
#pragma config OSC = HS         // Oscillator Selection bits (HS oscillator)
#pragma config OSCS = OFF       // Oscillator System Clock Switch Enable bit (Oscillator system clock switch option is disabled (main oscillator is source))

// CONFIG2L
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOR = OFF        // Brown-out Reset Enable bit (Brown-out Reset disabled)
#pragma config BORV = 20        // Brown-out Reset Voltage bits (VBOR set to 2.0V)

// CONFIG2H
#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
#pragma config WDTPS = 128      // Watchdog Timer Postscale Select bits (1:128)

// CONFIG3H
#pragma config CCP2MUX = OFF    // CCP2 Mux bit (CCP2 input/output is multiplexed with RB3)

// CONFIG4L
#pragma config STVR = OFF       // Stack Full/Underflow Reset Enable bit (Stack Full/Underflow will not cause RESET)
#pragma config LVP = OFF        // Low Voltage ICSP Enable bit (Low Voltage ICSP disabled)

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection bit (Block 0 (000200-001FFFh) not code protected)
#pragma config CP1 = OFF        // Code Protection bit (Block 1 (002000-003FFFh) not code protected)
#pragma config CP2 = OFF        // Code Protection bit (Block 2 (004000-005FFFh) not code protected)
#pragma config CP3 = OFF        // Code Protection bit (Block 3 (006000-007FFFh) not code protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot Block (000000-0001FFh) not code protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM not code protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection bit (Block 0 (000200-001FFFh) not write protected)
#pragma config WRT1 = OFF       // Write Protection bit (Block 1 (002000-003FFFh) not write protected)
#pragma config WRT2 = OFF       // Write Protection bit (Block 2 (004000-005FFFh) not write protected)
#pragma config WRT3 = OFF       // Write Protection bit (Block 3 (006000-007FFFh) not write protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot Block (000000-0001FFh) not write protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM not write protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 (000200-001FFFh) not protected from Table Reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 (002000-003FFFh) not protected from Table Reads executed in other blocks)
#pragma config EBTR2 = OFF      // Table Read Protection bit (Block 2 (004000-005FFFh) not protected from Table Reads executed in other blocks)
#pragma config EBTR3 = OFF      // Table Read Protection bit (Block 3 (006000-007FFFh) not protected from Table Reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot Block (000000-0001FFh) not protected from Table Reads executed in other blocks)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.


Si passa al file config_LCD.h che permette di specificare le connessioni con il microcontrollore. Questo è il file che deve essere modificato quando si vuole utilizzare un diverso interfacciamento con il microcontrollore.
Tramite le define TRIS_pin e LCD_pin si stabilisce a quali pin del microcontrollore sono connessi i pin del display. Il file risulta essere:

#ifndef config_LCD_H
#define config_LCD_H

// LCD Display Initialization
#define TRIS_D7 TRISBbits.TRISB5 // D7
#define TRIS_D6 TRISBbits.TRISB4 // D6
#define TRIS_D5 TRISBbits.TRISB3 // D5
#define TRIS_D4 TRISBbits.TRISB2 // D4
#define TRIS_EN TRISBbits.TRISB1 // EN
#define TRIS_RS TRISBbits.TRISB0 // RS

#define LCD_D7 PORTBbits.RB5 // D7
#define LCD_D6 PORTBbits.RB4 // D6
#define LCD_D5 PORTBbits.RB3 // D5
#define LCD_D4 PORTBbits.RB2 // D4
#define LCD_EN PORTBbits.RB1 // EN
#define LCD_RS PORTBbits.RB0 // RS

#endif

Le prime due righe controllano che non sia già stato chiamato il file config_LCD_H all’interno del progetto, mentre l’ultima chiude il blocco if.

#ifndef config_LCD_H
#define config_LCD_H
...
#endif

All’interno del blocco if si trova una serie di define che stabiliscono le connessioni tra microcontrollore e display e permettono di settarle correttamente come output. Inoltre permettono di usare i nomi mnemonici all’interno della libreria senza che questa sappia a priori quale pin del PIC fa capo a un dato pin del display.
Modificando i valori associati ad ogni define si stabilisce quale pin deve essere collegato al display. Ogni pin è associato a due define, una che stabilisce la direzione, TRIS_pin, e una che stabilisce lo stato, LCD_pin, che devono ovviamente avere lo stesso pin. Se per esempio si vuole associare il pin EN non più al pin RB1 ma al pin RC5 si dovranno modificare le due righe corrispondenti in:

#define TRIS_EN     TRISCbits.TRISC5    // EN
#define LCD_EN PORTCbits.RC5 // EN


Si passa infine all’ultimo header file, il LCD_CustomChar che deve essere aggiunto solo se si vuole far uso di un carattere speciale non inserito fra il set standard del display.
Per poter utilizzare dei caratteri personalizzati si deve passare alla funzione Lcd_CustomChar() il numero di caratteri che si vogliono memorizzare nel registro CGRAM e il carattere espresso come un array di 8 elementi, ognuno dei quali rappresenta una delle 8 righe che formano il carattere, avente 1 o 0 a seconda se il pixel corrispondente deve essere rispettivamente acceso o spento. Volendo riprodurre il fantasma di Pac-Man si avrebbe una cosa del genere:

Un altro carattere che si può inserire è una matrice vuota, formata da tutti i pixel spenti, a cui è associato un array di ‘0’.
Il file risulterà essere una cosa del genere:

#ifndef LCD_CustomChar_H
#define LCD_CustomChar_H

const uint8_t Custom_Char[] = {
0b00000,0b00000,0b00000,0b00000,0b00000,0b00000,0b00000,0b00000,
0b01110,0b11111,0b11010,0b11111,0b11111,0b10101,0b00000,0b00000
};

#endif

Per poter utilizzare uno dei caratteri memorizzati nel registro CGRAM è sufficiente indicare la posizione di memoria occupata, che va da 0 a 7. Per il primo carattere si usa quindi 0, 1 per il secondo e così via. In questo caso si utilizzerà 0 per il carattere “vuoto” ed 1 per il fantasma.


Source File
Si passa quindi al file main.c che inizia con le include di xc.h che contiene le informazioni relative al microcontrollore e di stdint.h che permette di definire il tipo di variabile con un nome mnemonico combinazione del segno e della dimensione della variabile.
Seguono quindi gli include dei file di configurazione config.h e config_LCD.h, il file contenete i caratteri personalizzati.
Le ultime due infine permettono di aggiungere i file LCD_HD44780.h e LCD_HD44780.c della libreria per il display tra quelli da compilare. Con le operazioni eseguite all’inizio subito dopo la creazione del progetto si erano solamente aggiunti i percorsi della libreria al progetto, mentre ora si sta specificando che i due file della libreria devono essere compilati e diventare parte integrante del progetto che verrà caricato sul microcontrollore.

#include <xc.h>
#include <stdint.h>
 
#include "config.h"
#include "config_LCD.h"
#include "LCD_CustomChar.h"
 
#include "LCD_HD44780.h"
#include "LCD_HD44780.c"

Viene quindi definita la variabile usata nel ciclo for e le costanti contenti una serie di numeri e lettere per testare il display.

uint8_t i;
 
const uint8_t word1 [] = {'1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0'};
const uint8_t word2 [] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T'}; 

Segue la funzione CFG_Init() che contiene la configurazione delle direzioni e degli stati delle PORTA e PORTC. La PORTB non è settata in quanto a questo provvede il file config_LCD.h.

void CFG_Init(void){
TRISA = 0b11111111;
TRISC = 0b11111111;
   
  PORTA = 0b00000000;
  PORTC = 0b00000000;
}

Si ha quindi la funzione Display_Init() che inizializza il display LCD.
Per prima cosa si utilizza la funzione di libreria Lcd_Init() che permette di settare la libreria a funzionare con display LCD da 20 colonne e 4 righe. Segue il comando di pulizia del display e di abilitazione del display.
Tramite la funzione Lcd_CustomChar() si memorizzano nella CGRAM i caratteri speciali definiti in LCD_CustomChar.
Infine si ha il comando che riporta il cursore alla posizione iniziale.

void Display_Init(void){
Lcd_Init(LCD_20x4);
  Lcd_Command(LCD_CLEAR);
Lcd_Command(LCD_D1_C0_B0);
__delay_ms(100);
  Lcd_CustomChar(2,(uint8_t *)Custom_Char);
Lcd_Command(LCD_RETURN_HOME);   
}

Segue la funzione main() che per prima cosa richiama le funzioni di inizializzazione CFG_Init e Display_Init(), seguite dal corpo del while infinito all’interno del quale si eseguono le operazioni per stampare a display caratteri e testi.
Il codice è intuitivo e non viene fatto altro che utilizzare le funzioni presentate in XC8 – #3 – Parte 1, a cui si passano carattere o testo voluti. L’unica accortezza è usare (uint8_t *) che permette di interpretare la variabile che lo segue come un dato tipo, che in questo caso è un intero ad 8 bit di tipo unsigned, che corrisponde al tipo char.
Il file main.c completo risulta essere:

#include <xc.h>
#include <stdint.h>

#include "config.h"
#include "config_LCD.h"
#include "LCD_CustomChar.h"

#include "LCD_HD44780.h"
#include "LCD_HD44780.c"

uint8_t i;

const uint8_t word1 [] = {'1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0'};
const uint8_t word2 [] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T'};

void CFG_Init(void){
  TRISA = 0b11111111;
  TRISC = 0b11111111;

  PORTA = 0b00000000;
  PORTC = 0b00000000;
}

void Display_Init(void){
  Lcd_Init(LCD_20x4);
  Lcd_Command(LCD_CLEAR);
  Lcd_Command(LCD_D1_C0_B0);
  __delay_ms(100);
  Lcd_CustomChar(2,(uint8_t *)Custom_Char);
  Lcd_Command(LCD_RETURN_HOME);
}

void main (void){
  CFG_Init();
Display_Init();

  while(1){
     Lcd_Command(LCD_CLEAR);
     Lcd_Text(1,5,(uint8_t *)"www.NE555.it");
     __delay_ms(1000);
     Lcd_Text(2,5,(uint8_t *)"XC8 - #3");
     __delay_ms(1000);

Lcd_Command(LCD_CLEAR);
     Lcd_Command(LCD_RETURN_HOME);
     Lcd_TextCP((uint8_t *)"Display 20x4");
     __delay_ms(1000);

     Lcd_Command(LCD_CLEAR);
     Lcd_Text(1,1,(uint8_t *)"Row 1");
     __delay_ms(250);
     Lcd_Text(2,1,(uint8_t *)"Row 2");
     __delay_ms(250);
     Lcd_Text(3,1,(uint8_t *)"Row 3");
     __delay_ms(250);
     Lcd_Text(4,1,(uint8_t *)"Row 4");
     __delay_ms(1000);

     Lcd_Command(LCD_CLEAR);
     Lcd_Command(LCD_RETURN_HOME);

     for(i = 0; i <= 19 ; i++){
        Lcd_CharCP(word1[i]);
        __delay_ms(100);
     }

     for(i = 0; i <= 19 ; i++){
        Lcd_Char(2,(uint8_t)(i+1),word2[i]);
        __delay_ms(100);
     }

     Lcd_Text(3,4,(uint8_t *)"R = ");
     Lcd_CharCP(0b11100110);
     Lcd_CharCP((uint8_t)'x');
     Lcd_TextCP((uint8_t *)"l/A [");
     Lcd_CharCP(0b11110100);
     Lcd_CharCP((uint8_t)']');
     __delay_ms(1000);

for(i = 3; i <= 17 ; i++){
Lcd_Char(4,(uint8_t)i,0);
Lcd_Char(4,(uint8_t)(i+1),1);
__delay_ms(500);
}

      __delay_ms(5000);

    }
}

PROGRAMMAZIONE
Per programmare il microcontrollore usando un programmatore Microchip, per esempio tramite il PICkit3, si può usare l’ambiente di sviluppo MPLAB X IDE oppure MPLAB X IPE. Per i vari step da seguire si rimanda a XC8 – #1.

DOWNLOAD
Al seguente link è possibile scaricare i file .c e .h utilizzati (la libreria è scaricabile da XC8 – #3 – Parte 1): DOWNLOAD.

 

 

[Voti Totali: 0 Media Voti: 0]
Segui la Nostra Pagina Facebook: Facebook

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *