10 – Display LCD 20×4 con librería xlcd.h (XC8)

En la publicación #1, de este blog, se explicó como controlar el popular display LCD 16×2 con la librería xlcd.h, a raíz de esto, muchos me han consultado si podemos controlar un display con mas filas y caracteres ya que en ocasiones necesitamos mostrar mas información. Afortunadamente la librería es muy flexible y en efecto nos permite trabajar con otros tipos de display. En concreto, veremos como manipular el también popular LCD 20×4.

Las direcciones.

El código para controlar un display 20×4 es esencialmente el mismo para un display 16×2, la diferencia radica en las direcciones de las filas y las posiciones de los datos en cada una de ellas. La siguiente tabla muestra las direcciones correspondientes:

direcciones 20x4Tomando como base la información de la tabla, si deseamos colocar el caracter “C” en la tercera fila, posición 18, debemos ubicarnos en la dirección 0x25 y posteriormente enviar el caracter:

SetDDRamAddr(0x25);
putsXLCD("C");

El siguiente código de ejemplo muestra información en todas las filas del display 20×4:

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <plib/xlcd.h>
#include <plib/delays.h>

//BITS DE CONFIGURACION.....
#pragma config PLLDIV = 5, CPUDIV = OSC1_PLL2, USBDIV = 2
#pragma config FOSC = HSPLL_HS, FCMEN = OFF, IESO = OFF
#pragma config PWRT = OFF, BOR = OFF, VREGEN = OFF
#pragma config WDT = OFF, WDTPS = 32768
#pragma config MCLRE = ON, LPT1OSC = OFF, PBADEN = OFF
#pragma config STVREN = ON, LVP = OFF, ICPRT = OFF, XINST = OFF

#define _XTAL_FREQ 48000000

//Funciones requeridas por la librería XLCD
void DelayFor18TCY(void);
void DelayPORXLCD(void);
void DelayXLCD(void);

int main() {
//Configurando LCD
OpenXLCD(FOUR_BIT & LINES_5X7);
//Esperar hasta que el display esté disponible.
while(BusyXLCD());
//Mover cursor a la derecha...
WriteCmdXLCD(0x06);
//Desactivando el cursor.
WriteCmdXLCD(0x0C);

while(1)
{
//Primera fila
SetDDRamAddr(0x00);
putrsXLCD("PRIMERA FILA LCD20X4");
//Segunda fila
SetDDRamAddr(0x40);
putrsXLCD("SEGUNDA FILA LCD20X4");
//Tercera fila
SetDDRamAddr(0x14);
putrsXLCD("TERCERA FILA LCD20X4");
//Cuarta fila
SetDDRamAddr(0x54);
putrsXLCD("CUARTA FILA LCD20X4");
}
}

void DelayFor18TCY(void)
{
Delay10TCYx(120);
}

void DelayPORXLCD(void)
{
Delay1KTCYx(180);
return;
}

void DelayXLCD(void)
{
Delay1KTCYx(60);
return;
}

Obtendremos el siguiente resultado:

funcionamiento20x4

Como puede apreciarse siempre que se conozcan las direcciones, es muy sencillo manipular este tipo de display con xlcd.h.

Un saludo,

05 – Aplicación de ADC.h – Voltímetro digital

Anteriormente, hemos visto como utilizar ADC.h para configurar y utilizar el módulo de conversión analógico-digital del PIC18F4550. Ahora, vamos a darle una aplicación práctica a las librerías ADC.h y XLCD.h construyendo un sencillo voltímetro digital de 0 a 5V cuyos valores se muestren en un display LCD 16×2.

Inicialmente vamos a definir nuestro diagrama esquemático:

lcd16x2withRW

Como puede observarse, utilizaremos AN0 (RA0) como canal para la conversión, esto significa que podemos utilizar el puerto B para controlar nuestro display, de manera que podemos utilizar la configuración por defecto de la librería XLCD.h. Si tiene dudas sobre como utilizar XLCD.h y ADC.h puede consultar la entrada 01 y 03 respectivamente de este blog.

Conversión de entero a ASCII

Sabemos que el módulo A/D de nuestro microcontrolador nos dará como resultado un número entero de 10 bits equivalente a la señal analógica de entrada. Esto significa que si nuestro voltaje de entrada puede variar en el rango de 0V a 5V, tendríamos valores enteros que van desde 0 a 1023. Entonces ¿Porqué es necesario convertir de entero a ASCII? Simple, nuestro display funciona con caracteres ASCII o mejor dicho con valores enteros que tengan un equivalente en ASCII, de manera que si por ejemplo queremos mostrar el número 1, debemos sumar 48, debido a que 49 en ASCII equivale al número 1; Y así debemos proceder con cualquier otro valor. Ahora los invito a revisar la siguiente tabla de códigos ASCII.

Al observar la tabla de códigos ASCII podemos determinar que los números del 0 al 9, se pueden representar en ASCII sumando siempre la constante 48. Entonces, una manera de mostrar nuestros valores enteros generados por el módulo A/D consiste en convertirlo en un valor que represente el voltaje de entrada y posteriormente descomponer cada uno de sus dígitos para convertirlos en ASCII enviándolos al display  consecutivamente:

  1. Imaginemos que nuestro canal AN0 recibe un voltaje de entrada de 1.75V, si aplicamos la ecuación correspondiente, sabremos que a la salida del módulo A/D tendremos 358.0928994. Ahora, tenemos que convertirlo en un valor equivalente al voltaje de entrada multiplicando primero por 5000 lo cual nos da: 1,790,464.498 y dividiendo posteriormente por 1023 cuyo resultado será: 1,750.209675, si tomamos sólo la parte entera podemos apreciar que representa los 1.75V de entrada.
  2. Ahora que tenemos la cifra 1750, procedemos a descomponerlo en sus dígitos individuales, posteriormente a cada uno se le suma 48 y ya los podemos enviar al display LCD.

Como se puede apreciar los dos procedimientos anteriores no son nada complicados a nivel de programación. Aclaro que estos algoritmos son fáciles de encontrar en libros e Internet, por lo que no son de mi autoría. El siguiente segmento de código muestra la implementación del algoritmo:


void interrupt ADCInterrupt()
{
 if(ADIF)
 {
 //Capturando el resultado
 resultado = ReadADC();

 //Algoritmo para descomponer el resultado en digitos enteros
 vAnalogico = (long)resultado * 5000;
 vAnalogico = vAnalogico / 1023;
 digito = vAnalogico / 1000;
 SetDDRamAddr(0x09);
 putcXLCD(digito + 48);
 SetDDRamAddr(0x0A);
 putrsXLCD(".");
 digito = (vAnalogico / 100) % 10;
 SetDDRamAddr(0x0B);
 putcXLCD(digito + 48);
 digito = (vAnalogico / 10) %10;
 SetDDRamAddr(0x0C);
 putcXLCD(digito + 48);
 SetDDRamAddr(0x0D);
 putrsXLCD("V");

 ADIF = 0;
 ConvertADC();
 }
}

El algoritmo es muy simple y mostrará en el display un valor de un entero y dos cifras decimales. Se utiliza para este ejemplo la conversión A/D por interrupción ya que es el método mas eficiente.

El código completo es el siguiente:

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <plib/xlcd.h>
#include <plib/delays.h>
#include "../plib/adc.h"

//BITS DE CONFIGURACION.....
#pragma config PLLDIV = 5, CPUDIV = OSC1_PLL2, USBDIV = 2
#pragma config FOSC = HSPLL_HS, FCMEN = OFF, IESO = OFF
#pragma config PWRT = OFF, BOR = OFF, VREGEN = OFF
#pragma config WDT = OFF, WDTPS = 32768
#pragma config MCLRE = ON, LPT1OSC = OFF, PBADEN = OFF
#pragma config STVREN = ON, LVP = OFF, ICPRT = OFF, XINST = OFF

#define _XTAL_FREQ 48000000

//Funciones requeridas por la librería XLCD
void DelayFor18TCY(void);
void DelayPORXLCD(void);
void DelayXLCD(void);

int resultado;
long int vAnalogico;
char digito;

void main()
{
 //Configurando LCD 4 bits mutilínea
 OpenXLCD(FOUR_BIT & LINES_5X7);
 //Esperar hasta que el display esté disponible.
 while(BusyXLCD());
 //Mover cursor a la derecha...
 WriteCmdXLCD(0x06);
 //Desactivando el cursor.
 WriteCmdXLCD(0x0C);

 SetDDRamAddr(0x00);
 putrsXLCD("VOLTAJE: ");
 
 //==========================================
 /*Configuración del módulo AD
 * Fosc = 64
 * Alineación = derecha
 * 16 TAD
 * Canal AN0
 * Interrupción habilitada
 * VREF+ y VREF- conectados a VDD y VSS respectivamente
 * Valor de ADCON1 = 14 (Canal AN0 analógico, el resto digitales)
 */
 OpenADC(ADC_FOSC_64 &
 ADC_RIGHT_JUST &
 ADC_16_TAD,
 ADC_CH0 &
 ADC_INT_ON &
 ADC_VREFPLUS_VDD &
 ADC_VREFMINUS_VSS,
 14);
 
 //Retardo de 50 Tcy
 Delay10TCYx(5);
 
 ADC_INT_ENABLE(); //Habilitación de la interrupción AD
 ei();
 
 //Iniciar la conversión
 ConvertADC();

 while(1)
 {}
}

void interrupt ADCInterrupt()
{
 if(ADIF)
 {
 //Capturando el resultado
 resultado = ReadADC();

 //Algoritmo para descomponer el resultado en digitos enteros...
 vAnalogico = (long)resultado * 5000;
 vAnalogico = vAnalogico / 1023;
 digito = vAnalogico / 1000;
 SetDDRamAddr(0x09);
 putcXLCD(digito + 48);
 SetDDRamAddr(0x0A);
 putrsXLCD(".");
 digito = (vAnalogico / 100) % 10;
 SetDDRamAddr(0x0B);
 putcXLCD(digito + 48);
 digito = (vAnalogico / 10) %10;
 SetDDRamAddr(0x0C);
 putcXLCD(digito + 48);
 SetDDRamAddr(0x0D);
 putrsXLCD("V");

 ADIF = 0;
 ConvertADC();
 }
}


void DelayFor18TCY(void)
{
 Delay10TCYx(120);
}

void DelayPORXLCD(void)
{
 Delay1KTCYx(180);
 return;
}

void DelayXLCD(void)
{
 Delay1KTCYx(60);
 return;
}

Acá podemos ver la simulación:

demo ADC_XLCD

El siguiente video muestra la implementación del circuito:

Volveré posteriormente con ejemplos de uso de interrupciones, lo cual nos servirá como preámbulo para la implementación de temporizadores. !!!Hasta la pronto¡¡¡