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,

09 – Generando señales PWM con el PIC18F4550

Inicialmente, no profundizaré respecto a que es una señal PWM y sus utilidades ya que considero que hay una amplia bibliografía al respecto. Nos concentraremos en la puesta en marcha del módulo PWM.

Típicamente, los microcontroladores utilizan  temporizadores para generar señales PWM. En los PIC de 8 bits, el módulo encargado de proveer la señal PWM se llama Capture/Compare/PWM. En el PIC18F4550 hay dos salidas PWM: CCP1 y CCP2.

CCP

CCP1, se encuentra conectado a RC2 y CCP2 a RC1, aunque RB3 puede utilizarse como una salida CCP2 alternativa.
Cuando el módulo Capture/Compare/PWM funciona en modo PWM los pines CCPX pueden producir una salida PWM con una resolución 10 bits, siempre que se utilice el TRIS apropiado para PORTC y PORTB.

Librerías especiales XC8 para Timer y PWM

Para generar señales PWM se requiere de una base de tiempos que es proporcionada por el Timer2 del PIC, por lo que se requiere de:

OpenTimer2(): este método inicializa el Timer 2 del microcontrolador. Se le debe especificar como argumento el prescaler que puede ser:

prescaler_tm2.png

Por lo general, se utiliza el prescaler 1:16. Una vez configurado Timer2, se requiere de la librería pwm.h para poder utilizar los siguientes métodos:

OpenPWMx(): Inicializa el módulo PWM 1 o 2. Se le debe especificar como argumento el período (en hexadecimal) de la señal PWM que se desea generar.

SetDCPWMx(): Establece el ciclo de trabajo de la señal PWM. Se debe especificar como argumento el ciclo de trabajo deseado (debe ser un número entero).

Cálculo del período y del ciclo de trabajo.

Ecuación para encontrar el período de la señal PWM que será el argumento de OpenPWMx():

periodoPWM.png

Ecuación para calcular el ciclo de trabajo del 100% de la señal PWM, servirá como argumento de SetDCPWMx():

cicloTPWM

Ejemplo de aplicación:

EjemploPWM

 

El código del programa sería el siguiente:

#include <xc.h>
#include <plib/timers.h>
#include <plib/pwm.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

void main(){
TRISCbits.TRISC2 = 0;
unsigned char prescaler = T2_PS_1_16;
OpenTimer2(prescaler);
OpenPWM1(0x95);
while(1){
SetDCPWM1(300);
}
}

Simulamos y observamos con el osciloscopio la señal generada:

simulacionPWM

Variación de ciclo de trabajo PWM + Timer

Como se puede apreciar, la generación de señales PWM es muy simple. Sin embargo, la técnica PWM requiere de la variación del ciclo de trabajo para “regular” la energía que se entrega a una carga. En el siguiente ejemplo, observaremos como generar un ciclo de aumento y disminución del CT de la señal PWM anteriormente generada.

Según los cálculos PWM realizados, el argumento para SetDCPWM1 es de 600 para un CT del 100%, de esta manera necesitamos incrementar de uno en uno desde 0 hasta 600 y posteriormente decrementar de uno en uno hasta llegar a 0, esto se repetirá infinitamente.

Como ya hemos aprendido a utilizar temporizadores, trabajaremos con el Timer0 para proveer una base de tiempo de 0.1 segundos para efectuar los incrementos/decrementos (ya no debemos utilizar delays). El código sería el siguiente:

#include <xc.h>
#include <plib/timers.h>
#include <plib/pwm.h>

#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

int dutyCicle;
unsigned int accion;

void main(){
accion = 0; //Aumentar
dutyCicle = 0;
TRISCbits.TRISC1 = 0;
unsigned char Timer0Config;
unsigned char prescaler = T2_PS_1_16;
Timer0Config = TIMER_INT_ON &amp; T0_16BIT &amp; T0_SOURCE_INT &amp; T0_PS_1_256;
OpenTimer0(Timer0Config);
WriteTimer0(0xEDAF);
TMR0IF = 0;
OpenTimer2(prescaler);
OpenPWM1(0x95);
ei();
while(1){}
}

void interrupt high_isr(){
if(TMR0IF){
if(accion == 0){
dutyCicle++;
SetDCPWM1(dutyCicle);
if(dutyCicle == 600){
accion = 1;
}
}
else if(accion == 1){
dutyCicle--;
SetDCPWM1(dutyCicle);
if(dutyCicle == 0){
accion = 0;
}
}
TMR0IF = 0;
WriteTimer0(0xEDAF);
}
}

Los invito a comprobar el funcionamiento del módulo PWM y probar distintos escenarios.

Un saludo,

08 – Temporizador de 16 bits del PIC18F4550

Cuando se diseñan aplicaciones de control electrónico, es común encontrarse con la necesidad de incorporar tiempos de espera para los dispositivos “lentos” que se controlan ya que el microcontrolador es demasiado rápido. Por otra parte, los temporizadores son ideales para proveer bases de tiempo de alta precisión para la implementación de relojes de tiempo real.

El PIC18F4550 posee cuatro temporizadores: TIMER0 a TIMER3, al igual que los demás módulos especiales, cada uno de los temporizadores es controlado por un registro de configuración.

Temporizador Registro de control
TIMER0 T0CON
TIMER1 T1CON
TIMER2 T2CON
TIMER3 T3CON

En este post se describirá como configurar y utilizar el temporizador TIMER0, está información servirá como base para poder configurar los demás temporizadores ya que el procedimiento es similar.

El TIMER0 posee las siguientes características:

  • Modo de operación de 8 y 16 bits: El modo de operación de 8 y 16 bits determina la cantidad de conteos máxima que puede realizar el timer. Si es de 8 bits contará hasta 255, si es de 16 bits hasta 65535.
  • Registros de control de lectura y escritura.
  • Prescaler dedicado de 8 bits: el timer incrementa el valor del conteo cada Fosc / 4 pulsos de reloj. Para un microcontrolador que opera a 20Mhz, significa que el incremento se realiza 5 millones de veces en un segundo. Esta cantidad puede ser demasiado alta para ciertas aplicaciones en donde las bases de tiempo son bastante grandes. Para solucionar este inconveniente, se dispone del prescaler que es básicamente un divisor de frencuencia, la frecuencia se puede dividir entre: 2, 4, 8, 16, 32, 64, 128 y 256.
  • Fuente de reloj seleccionable (interna / externa).
  • Interrupción por desbordamiento: cada vez que el valor del conteo llega a su límite se genera un desbordamiento (overflow) que genera una interrupción interna que activa el bit TMR0IF del registro INTCON.

Librerías C18 para el manejo del módulo temporizador

Puede utilizarse la librería timers.h de C18 (hoy XC8) para facilitar la configuración del módulo temporizador, los métodos que proporciona son los siguientes:

timers

En la tabla, sustituimos la letra x al final del nombre de la función, por el número del timer con el cual estamos trabajando. Ej. OpentTimer0.

Para hacer funcionar el módulo de temporización, se requiere únicamente de OpenTimer y WriteTimer.

El método OpenTimer(), se utiliza para configurar el temporizador, definiendo:

  1. Activación de la interrupción: TIMER_INT_OFF, TIMER_INT_ON
  2. Modo de operación (8 , 16 bits): T0_8BIT, T0_16BIT
  3. Origen de la señal de reloj: T0_SOURCE_INT, T0_SOURCE_EXT
  4. Prescaler: T0_PS_1_1 1:1, T0_PS_1_2 1:2, T0_PS_1_4 1:4, T0_PS_1_8 1:8, T0_PS_1_16 1:16, T0_PS_1_32 1:32, T0_PS_1_64 1:64, T0_PS_1_128 1:128, T0_PS_1_256 1:256

El siguiente ejemplo muestra como configurar el método:

unsigned char Timer0Config;
Timer0Config = TIMER_INT_ON &T0_16BIT & T0_SOURCE_INT & T0_PS_1_25;
OpenTimer0(Timer0Config);
WriteTimer0(0x48E4);

Observe como se utiliza una variable tipo char Timer0Config para almacenar la configuración de timer0. Posteriormente el valor de la variable se pasa como argumento del método OpenTimer0. La configuración establecida se lee: Interrupción activada, modo de operación de 16 bits, señal de reloj interna y, prescaler de 1:256.

El método WriteTimerx, se utiliza para indicar (en formato hexadecimal) al temporizador la cantidad de conteos que debe realizar (de acuerdo a un intervalo de tiempo establecido) antes de activar la bandera de interrupción TMRxIF. Mediante la activación de TMRxIF, sabremos que ha transcurrido el tiempo correspondiente.

En este ejemplo, se desea un temporizador de:

  • Timer: 1 segundo
  • Fosc: 48MHZ

Procedimiento:

  1. Si Fosc es 48MHZ, para el módulo de temporización se divide por cuatro = 48MHZ / 4 = 12MHZ
  2. El período es 1 / 12000000 = 8.3333333×10-8 Seg
  3. Utilizando un prescaler de 1:256, el período es: 8.3333333×10-8 Seg x 256 = 2.133333×10-5 Seg
  4. Como el timer requerido es de 1 segundo, el ciclo de instrucción efectivo es: 1 seg / 2.133333×10-5 seg = 46,875
  5. Si el temporizador trabaja en modo 16 bits, la activación de la bandera de interrupción ocurrirá cada: 65,535 – 46,875 = 18,660 conteos.
  6. Se convierte este valor a hexadecimal 18,66010 = 48E416
  7. El valor 48E416 será el argumento del método WriteTimer(0x48E4) para que active la bandera de interrupción cada 1 segundo.

El código de aplicación propuesto es el siguiente:

#include <xc.h>
#include <plib/timers.h>

//Bits de configuración para Fosc = 48Mhz
#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
unsigned char Timer0Config;
void main()
{
TRISBbits.RB4 = 0; //RB4 es salida.
LATB4 = 1; //Poner RB4 4n uno.

Timer0Config = TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_PS_1_256;
OpenTimer0(Timer0Config);
WriteTimer0(0x48E4);

INTCONbits.TMR0IF = 0; //inicializar la bandera de interrupción

ei();

while(1)
{}
}

void interrupt TimerOverflow()
{
if(INTCONbits.TMR0IF == 1)
{
LATB4 = ~LATB4; //Cambiar el estado de RB4
INTCONbits.TMR0IF = 0;
WriteTimer0(0x48E4);
}

}

Como puede observarse, exactamente cada un segundo, se activará TMR0IF, indicando el desbordamiento del timer (interrupción), lo cual será utilizado para activar/desactivar un led conectado a RB4.

Conclusiones

Existen otros métodos para implementar timers. Sin embargo, se ha descrito este procedimiento ya que lo considero mas sencillo que los demás, aunque tiene como desventaja que no se profundiza en el funcionamiento del módulo, lo cual es esencial para aplicaciones mas complejas. A todo interesado en este tema lo invito a implementar su temporizador con estos valores de ejemplo y posteriormente  hacer modificaciones.

En el siguiente post, utilizaremos el timer2 para hacer funcionar el módulo PWM del PIC18F4550. Un saludo.

Nota importante: He recibido algunos comentarios de post anteriores respecto a que no les funcionan los ejemplos. Les comento a mis apreciables lectores que todo principiante debe pasar por una curva de aprendizaje, por lo que es posible que no le funcione el código a la primera. Pero deben prestar atención a los detalles ya que allí se encuentra la solución, no es recomendable solo copiar y pegar el código en nuestro editor sin antes haber leído la teoría correspondiente y haberla comprendido. Si no se hace esto, es casi seguro que los programas no les funcionarán. Se debe analizar el  código y asegurarse que esté acorde a nuestras necesidades o adaptarlo convenientemente. Esto es lo mismo que aconsejo a mis estudiantes, con la práctica y análisis constante al final las cosas funcionarán correctamente por lo que los animo a seguir intentando hasta lograrlo.

De igual manera agradezco sus comentarios y propuestas. Mas adelante publicaré ejemplos de aplicación mas complejos donde se integrarán varios módulos, pero antes los explicaré de forma individual con ejemplos sencillos para que tod@s aquellos que se inician en el mundo de los microcontroladores los conozcan y sepan utilizarlos.

07 – Interrupciones externas del PIC18F4550

Las computadoras y equipos electrónicos modernos son gobernados por microprocesadores y microcontroladores, estos equipos poseen una gran cantidad de dispositivos periféricos que por lo general requieren ser atendidos por el microcontrolador alterando el orden natural del programa. Para atender las peticiones de los dispositivos periféricos, se utilizan las interrupciones. Continuando el tema de las interrupciones, en esta ocasión veremos cómo utilizar las interrupciones externas INTx del PIC18F4550.

Interrupciones externas

Se configuran por medio de los registros INTCON, INTCON2 e INTCON3, los cuales poseen bits de habilitación, prioridad y banderas de estado. El registro INTCON es el más importante para la configuración e implementación de interrupciones externas, la estructura del registro se muestra en la siguiente figura:

Registro INTCON

En el contexto de INT0, los siguientes bits de INTCON son los mas importantes:

  • GIE, se utiliza para habilitar las interrupciones en general, debe ser = 1.
  • INT0IE, se utiliza para habilitar la interrupción INT0, debe ser = 1.
  • INT0IF, es la bandera de interrupción de INT0, se colocará en 1, cada vez que se produzca una interrupción INT0, posteriormente debe ser inicializada “manualmente” con 0.

Adicionalmente, en el registro INTCON2, el bit INTEDG0 se utiliza para definir si la interrupción se activará con un flanco de subida (Rising edge) o flanco de bajada (Falling edge) de la señal. Por defecto INTEDG0 posee un valor de 1, lo que equivale a una activación en flanco de subida.

NOTA: Las interrupciones INT1 e INT2 se configuran por medio del registro INTCON3.

Existen otras configuraciones como, la definición de prioridad de interrupciones, pero este tema no lo abordaremos aquí.

Ejemplo de implementación

A continuación, veremos una aplicación sencilla de la interrupción INT0. El circuito es el siguiente:

Circuito INT0

El microcontrolador tiene una rutina permanente que es hacer un blink en el led conectado a RA0 a intérvalos de 0.25 segundos. Cuando el botón haga una transición de 0 a 1 (Rising edge) se activará INT0, la cual activará una rutina que encenderá o apagará el led conectado a RA1 durante 1 segundo. Posteriormente, el microcontrolador continuará con la rutina del blink hasta que se produzca una nueva interrupción INT0.

El código del ejemplo es el siguiente:


#include <xc.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

void retardo(int t);

void main()
{
 //Todas las entradas ANx son digitales
 ADCON1 = 0x0F;
 
 //RA0 y RA1 son salidas
 TRISAbits.RA0 = 0;
 TRISAbits.RA1 = 0;
 
 //INT0 (RB0) es entrada
 TRISBbits.RB0 = 1;
 
 //Leds apagados
 PORTBbits.RB0 = 0;
 PORTBbits.RB1 = 0;
 
 //Configuración de INT0
 INTCONbits.GIE = 1; //Habilitando interrupciones
 INTCONbits.INT0IE = 1; //Habilitar INT0
 INTCON2bits.INTEDG0 = 1; //Interrupción se activa en flanco de subida
 
 while(1)
 {
 //Blink
 PORTAbits.RA0 = 1;
 retardo(25);
 PORTAbits.RA0 = 0;
 retardo(25); 
 } 
}

void interrupt high_isr()
{
 if(INT0IF) //Si la bandera de interrupción es 1
 {
 PORTAbits.RA1 = ~PORTAbits.RA1; //Se invierte el estado del led
 retardo(100);
 //inicializar bandera
 INT0IF = 0;
 }
}

void retardo(int t)
{
 for(int i = 0; i <= t; i++)
 {
 __delay_ms(10);
 }
}

En el siguiente video se muestra la simulación del ejemplo:

Como se puede apreciar, la implementación de interrupciones externas es muy simple pero de gran utilidad para nuestros proyectos. En futuras publicaciones abordaremos el tema de las interrupciones periféricas de los módulos internos del PIC, iniciaremos con los temporizadores. ¡¡¡Un saludo a tod@s!!!

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¡¡¡

04 – Conversion de Analógico a Digital por Interrupción

En el post 03 de este blog expliqué como utilizar la librería ADC.h para realizar conversiones A/D mostrando el ejemplo  “tradicional”. Sin embargo, dado que el módulo A/D del microcontrolador es un periférico externo integrado a la estructura del microcontrolador (dentro del encapsulado del PIC), posee una interrupción asociada denominada ADCInterrupt que se indica por medio de la bandera ADIF del registro PIR1. En el microcontrolador, cada vez que se produce una interrupción se ejecuta un método asociado a esta o el método general interrupt high_isr().

El funcionamiento es simple, cada vez que el módulo A/D termine de realizar una conversión lo indicará poniendo en 1 la bandera ADIF, utilizaremos el método ADCInterrupt para capturar esta interrupción y evaluar el estado de la bandera. Siempre que sea posible, es conveniente utilizar interrupciones ya que es una manera mas eficiente de realizar varias actividades con el microcontrolador.

void interrupt ADCInterrupt()
{
 if(PIR1bits.ADIF)
 {
   /*Acciones a ejecutar
   .
   .
   .*/

   /*Siempre se debe colocar la bandera
   en 0 antes de salir.*/
   PIR1bits.ADIF = 0;
 }
}

Por otra parte, debemos activar la interrupción de la conversión con el parámetro ADC_INT_ON en la función OpenADC():

 OpenADC(ADC_FOSC_64 &
 ADC_RIGHT_JUST &
 ADC_16_TAD,
 ADC_CH0 &
 ADC_INT_ON &
 ADC_VREFPLUS_VDD &
 ADC_VREFMINUS_VSS,
 14);

Utilizaremos el mismo circuito:

Circuito de aplicación

El código es el siguiente:

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

//Bits de configuración para Fosc = 48Mhz
#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

int resultado;

void main()
{
 TRISD = 0;
 PORTD = 0;

 /*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();

 /*Como el resultado es de 10 bits
 * y sólo se dispone de un puerto de 8 bits
 * para mostrarlo. El resultado se convierte
 * en su equivalente de 8 bits dividiendo
 * por cuatro.
 */
 resultado = resultado / 4;

 //Mostrando el resultado
 PORTD = resultado;

 //Antes de salir se debe poner la bandera en 0
 ADIF = 0;

 //Continuar con la conversión
 ConvertADC();
 }
}

Como se puede apreciar, al bucle while(1) {} lo dejamos sin código ya que todo el proceso es controlado desde el método ADCInterrupt(), podemos entonces dejar funcionando automáticamente al módulo A/D mientras el microcontrolador se encarga de realizar otras tareas.

En el siguiente post, utilizaremos la conversión por interrupción para construir un voltímetro digital.

03 – Conversión de Analógico a Digital con librería ADC.h de C18

Continuando con la implementación de librerías C18, ha llegado el turno de adc.h, esta librería nos facilita el trabajo de conversión A/D ahorrándonos bastante código. Como siempre nos basaremos en el documento de Microchip: MPLAB_C18_Libraries_51297f.

Circuito de aplicación

Partimos describiendo el circuito de aplicación que es el siguiente:

Circuito de aplicación

El circuito es sencillo ya que únicamente tiene como finalidad demostrar el funcionamiento del módulo AD del microcontrolador. En el circuito, se utiliza el canal AN0 (RA0), los niveles analógicos son proporcionados por el potenciómetro de 10K y monitoreados por el voltímetro, el microcontrolador realizará la conversión y mostrará el valor digital (en binario) en el puerto D por medio de un conjunto de leds.

Consideraciones sobre el módulo AD del PIC18F4550

  • El PIC18F4550 posee un modulo AD de 13 canales, esto significa que solamente podemos utilizar un canal por vez (no es posible la conversión simultánea).
  • La resolución de la conversión es de 10 bits (2∧10 = 1024), por lo tanto tendremos 1024 – 1 = 1023 posibles valores.
  • El valor del escalón de la conversión si el voltaje de entrada máximo es de 5V será de: 5V/1023 = 0.004887V. Por ejemplo, si se ajusta el potenciómetro para entregar un voltaje de 3.5V tendremos que: 3.5V /0.004887V = 716 (valor digital), al convertir 716 a binario tenemos:  1011001100 (Este será el resultado que se mostrará en el puerto D).

Librería adc.h de C18

Originalmente creada para C18, puede utilizarse con XC8 y MPLAB X. Las funciones que la librería proporciona son las siguientes:

funciones libreria adc

Las funciones que utilizaremos son:

OpenADC(), se utiliza para configurar el módulo AD, requiere tres parámetros de configuración: origen de la señal de reloj para el módulo AD, la alineación de la conversión, y el tiempo de adquisición de datos (xTAD). El segundo parámetro se utiliza para indicar: el canal a utilizar (AN0 – AN12), estado de la interrupción AD, y los voltajes de referencia positivo y negativo. Finalmente, el tercer parámetro es un valor decimal que representa la configuración del registro ADCON1.

El siguiente es el ejemplo de aplicación:

 OpenADC(ADC_FOSC_64 &
 ADC_RIGHT_JUST &
 ADC_16_TAD,
 ADC_CH0 &
 ADC_INT_OFF &
 ADC_VREFPLUS_VDD &
 ADC_VREFMINUS_VSS,
 14);

**Para información sobre el origen de los valores de configuración se sugiere leer la hoja de especificaciones del PIC18F4550 pág. 259.

Cabe aclarar, que los parámetros de OpenADC() cambian de acuerdo al PIC que se está utilizando.

ConvertADC(), inicia el proceso de conversión.

ReadADC(), devuelve un número entero que representa el valor decimal de la conversión efectuada.

BusyADC(), hace una espera mientras el proceso de conversión AD se lleva a cabo.

Código de aplicación

Creamos entonces, un nuevo proyecto en MPLAB X e incorporamos el siguiente código:

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

//Bits de configuración para Fosc = 48Mhz
#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

int resultado;

void main()
{
 TRISD = 0;
 PORTD = 0;

 /*Configuración del módulo AD
 * Fosc = 64
 * Alineación = derecha
 * 16 TAD
 * Canal AN0
 * Interrupción deshabilitada
 * 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_OFF &
 ADC_VREFPLUS_VDD &
 ADC_VREFMINUS_VSS,
 14);

 //Retardo de 50 Tcy
 Delay10TCYx(5);

 while(1)
 {
 //Iniciar la conversión
 ConvertADC();

 //Espera para que se complete la conversión
 while(BusyADC());

 //Capturando el resultado
 resultado = ReadADC();

 /*Como el resultado es de 10 bits
 * y sólo se dispone de un puerto de 8 bits
 * para mostrarlo. El resultado se convierte
 * en su equivalente de 8 bits dividiendo
 * por cuatro.
 */
 resultado = resultado / 4;

 //Mostrando el resultado
 PORTD = resultado;
 }
}

Bastará con compilar y verificar el funcionamiento en Proteus para ver los resultados, sería interesante comprobar que matemáticamente los valores digitales corresponden al nivel del voltaje entrada analógico (no olvidar que el resultado de 10 bits se está convirtiendo a 8 bits).

El siguiente video muestra la implementación en una placa de desarrollo:

Volveré posteriormente con un ejemplo de conversión A/D utilizando interrupciones.

Saludos a tod@s

02 – Modificando la librería XLCD

En el post anterior, expliqué brevemente como utilizar la librería XLCD para controlar un display LCD 16×2. Sin embargo, si tenemos circuitos en donde debido a requerimientos debemos conectar el display a otro puerto, será necesario realizar cambios en las macro de la librería, este es nuestro objetivo: explicar como realizar cambios en la librería xlcd y salir vivos en el intento.

El circuito

En mi caso particular y que utilizaré como ejemplo, dispongo de una placa de desarrollo en donde el conector para el display LCD tiene la siguiente disposición de pines:

Pines LCD

Lo cual se traduce en el siguiente diagrama esquemático:

lcd pic-ek

La nueva asignación de pines para el display LCD respecto al microcontrolador sería:

  • Puerto de datos: Puerto D.
  • Modo de operación: 8 bits (D0:D7).
  • LCD RS: B5
  • LCD RW: B4
  • LCD E: B3

Muy bien, entonces ¿Qué necesitamos para adecuar la librería a nuestra nueva distribución de pines? En esencia, lo siguiente:

  1. Proporcionar permisos de modificación a la carpeta donde se encuentran los archivos de la librerías C18 o ejecutar MPLAB como administrador.
  2. Crear un proyecto en MPLAB X y editar el código del programa (ver el post anterior).
  3. Copiar los archivos fuente de la librería XLCD a nuestro proyecto.
  4. Modificar las macro en el archivo xlcd.h
  5. Compilar y comprobar el funcionamiento.

Teniendo en cuenta todo lo anterior iremos paso a paso.

1. Proporcionando permisos de modificación a las librerías C18.

Las librerías se copian en el directorio de instalación del compilador XC8, si tienen instalado un Windows de 32 bits será:

C:\Program Files\Microchip\xc8\v1.30\include\plib

Si es de 64 bits:

C:\Program Files(x86)\Microchip\xc8\v1.30\include\plib

NOTA: Las rutas pueden cambiar dependiendo del directorio de instalación, la unidad seleccionada durante el proceso de instalación y la versión del compilador.

Una vez ubicada la carpeta, se procede a proporcionar todos los permisos a los usuarios de la computadora sobre la carpeta plib.

Ahora bien, si se nos complica mucho hacer esto, podemos ejecutar MPLAB X como administradores con lo cual tendremos los permisos necesarios. Para hacer que MPLAB X se ejecute siempre como Administrador, se da clic derecho al icono, se selecciona la pestaña Compatibilidad y se marca la casilla “Ejecutar este programa como administrador“.

2. Creando el proyecto en MPLAB X

Incorporamos el código tal cual publiqué en el post anterior, pero debemos hacer una modificación en el modo de datos ya que ahora debe ser de ocho bits, colocaremos:

//Configurando LCD 8 bits mutilínea
 OpenXLCD(EIGHT_BIT & LINES_5X7);

3. Copiando los archivos fuente de la librería.

Los archivos fuente de la librería xlcd.h (y de todas las demás), se encuentran en la siguiente ruta: C:\Program Files\Microchip\xc8\v1.30\sources\pic18\plib\XLCD

Copiamos todas los archivos y los pegamos en la carpeta de nuestro proyecto.

Archivos de la librería

Ahora en MPLAB X, procedemos a agregar los archivos copiados, para lo cual se damos clic derecho en Source Files, y seleccionamos Add Existing Item …

Agregar archivos

Seleccionamos todos los archivos y damos clic en Select.

4. Modificando las macros de la librería xlcd.h

Es momento de modificar las macros, para ello nos ubicamos en en la sentencia #include “plib/xlcd.h”, damos clic derecho, seleccionamos Navigate y posteriormente Go to Declaration.

Declaracion de xlcd

MPLAB X, abrirá el archivo de código de la librería xlcd.h, en donde haremos la primer modificación ajustando el modo de datos a 8 bits, esto lo hacemos quitando el comentario de la línea 26, dejándola así:

/* Interface type 8-bit or 4-bit
 * For 8-bit operation uncomment the #define BIT8
 */
 #define BIT8

A continuación, comentamos las líneas 34 y 35 que definen el puerto de datos y agregamos la asignación al puerto D:

/* DATA_PORT defines the port to which the LCD data lines are connected */
// #define DATA_PORT PORTB
// #define TRIS_DATA_PORT TRISB
#define DATA_PORT PORTD
#define TRIS_DATA_PORT TRISD

Cambiamos la definición de los pines RW y E del LCD, comentando el código por defecto y agregando las nuevas definiciones, dejando el código de la siguiente manera:

/* CTRL_PORT defines the port where the control lines are connected.
 * These are just samples, change to match your application.
 */
 //#define RW_PIN LATBbits.LATB6 /* PORT for RW */
 //#define TRIS_RW TRISBbits.TRISB6 /* TRIS for RW */

 #define RW_PIN LATBbits.LATB4
 #define TRIS_RW TRISBbits.TRISB4

 #define RS_PIN LATBbits.LATB5
 #define TRIS_RS TRISBbits.TRISB5

 //#define E_PIN LATBbits.LATB4 /* PORT for D */
 //#define TRIS_E TRISBbits.TRISB4 /* TRIS for E */

 #define E_PIN LATBbits.LATB3
 #define TRIS_E TRISBbits.TRISB3

5. Compilar y comprobar el funcionamiento.

Procedemos a compilar el proyecto, esto tomará un poco de tiempo ya que se procesará cada uno de los archivos de código incorporados al proyecto. Si no tenemos errores procedemos a comprobar el funcionamiento en PROTEUS.

Prueba

 

El siguiente video muestra la implementación en el circuito.

 

Conclusiones

Modificando las macros de la librería  podemos utilizarla con casi cualquier variante de conexiones de puerto que se diseñe. Aunque no se haya descrito en este post, la librería se puede configurar para que trabaje en modo de 4 bits con lo que se libera la mitad de los bits del puerto D los cuales podrían ser utilizados para otros fines, dejaré esto como una pequeña tarea a todos aquellos a quienes les haya parecido interesante utilizar la librería.

Volveré posteriormente con otro post en donde explicaré como utilizar la librería ADC.h del módulo convertidor Analógico a Digital para que posteriormente podamos integrar las librerías xlcd.h y adc.h para implementar un sencillo voltímetro digital.

Saludos a tod@s desde El Salvador…

01 – Librería XLCD para el manejo de displays LCD

XLCD.h es una librería del compilador C18 de Microchip, que se utiliza para controlar displays LCD. Esta librería puede utilizarse con XC8 para realizar la misma función.

En la guía MPLAB para las librerías del compilador C18 (pág. 75) se explican detalladamente las funciones disponibles así como los argumentos de cada una de ellas. También, proporciona un pequeño ejemplo de implementación. Recomiendo que se revise dicho documento para comprender con mayor facilidad el código que aquí se muestra. El display que vamos a utilizar es un 16×2 basado en el controlador HITACHI HD44780, el cual es ampliamente utilizado en circuitos de visualización microcontrolados.

El circuito.

La librería viene pre configurada (xc8 ver 1.2) de la siguiente manera:

  • Puerto de datos: Puerto B.
  • Modo de operación: 4 bits, parte baja del puerto B (B0:B3). Esto significa que B0 debe conectarse a D4 del display, B1 a D5, B2 a D6 y B3 a D7.
  • LCD RS: B5
  • LCD RW: B6
  • LCD E: B4

Esta configuración es muy eficiente ya que podemos controlar el display con un solo puerto, el circuito de prueba en Proteus sería similar al siguiente:

CIrcuito LCD

En el circuito no se muestra el cristal de cuarzo a utilizar que en este caso será de 20MHZ, de manera que el oscilador se configurará convenientemente para proporcionar un Fosc = 48MHZ. De acuerdo con el datasheet del microcontrolador (pág. 30) PLLDIV debe ser = 5, para proporcionar 4MHZ al circuito PLL y HSPLL debe ser = 2 (96MHZ /2) para asegurar 48MHZ como Fosc. La definición precisa del Fosc es vital cuando se utiliza la librería XLCD por lo tanto, se debe procurar que la Fosc deseada esté correctamente configurada.

Funciones de retardo requeridas.

La librería requiere que el usuario defina los siguientes retardos para el display:

Funciones de retardo de xlcd

Estas funciones deben calcularse manualmente tomando como base la Fosc anteriormente configurada. Veamos en detalle cada una de ellas:

DelayFor18TCY: requiere 18 ciclos de instrucción, hay dos métodos para conseguir este retardo. Un método consiste en repetir secuencialmente unas doce veces la función Nop(). La otra manera es utilizar la función Delay10TCYx() pasándole como argumento un valor entero que represente el retardo equivalente a 18TCY, haciendo unos cálculos se puede decir que este valor anda por 2.5 por MHZ. Entonces, estamos trabajando con un Fosc = 48MHZ tendríamos que 2.5 x 48 = 120, la función nos quedaría Delay10TCYx(120). Personalmente prefiero este segundo método, la función completa sería así:

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

DelayPORXLCD: se utiliza para implementar un retardo de 15ms. Para este caso se utiliza la ecuación (15ms x 48MHZ) / 4, que nos da como resultado 180000, tomamos 180 como argumento para el método, de manera que el código sería:

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

DelayXLCD: se utiliza para implementar un retardo de 5ms, aplicamos la misma ecuación de manera que: (5ms x 48MHZ) / 4, nos da como resultado 60000, tomamos 60 como argumento para el método:

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

Utilizando las ecuaciones anteriores, podemos adecuarnos perfectamente o cualquier otro Fosc soportado por el microcontrolador.

El código completo es el siguiente:

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

//CON ESTE ORDEN DE CONFIGURACION FUNCIONA RB5.....
#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

//Retardos requeridos por la librería XLCD
void DelayFor18TCY(void);
void DelayPORXLCD(void);
void DelayXLCD(void);

int 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);

 while(1)
 {
 //Primera línea
 SetDDRamAddr(0x00);
 putrsXLCD("HOLA");
 //Segunda línea
 SetDDRamAddr(0x40);
 putrsXLCD("MUNDO");
 }

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

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

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

Al simular en Proteus veríamos lo siguiente:

Simulacion

Consideraciónes sobre la implementación en el circuito.

Como se puede observar en la configuración de los bits del microcontrolador, he colocado un comentario respecto a RB5, esto se debe a que he notado que algunas veces el puerto B funciona erróneamente cuando se coloca la configuración autogenerada por MPLABX mediante la herramienta “Configuration bits”. Por lo general utilizo esta herramienta, pero en este caso particular para evitar estos “extraños errores” prefiero editar los bits de configuración manualmente, dejándolos tal como se observa en el código anteriormente mostrado. Aclaro que este comportamiento anómalo se da únicamente en el circuito real no en la simulación. El siguiente micro video muestra la ejecución en el circuito:

Conclusiones.

Utilizar la librería XLCD es sencillo, pero que pasaría si ya tenemos un circuito previamente diseñado que no utiliza el puerto B como puerto de datos,  en este caso debemos modificar las macro de la librería y compilarla nuevamente, aprovechando que la placa de desarrollo que estoy utilizando tiene conectado el display al puerto D, explicaré posteriormente como cambiar las configuraciones por defecto.

Librerías XC8 (C18) para microcontroladores PIC18

Con esta entrada, voy a iniciar una serie de mini-guías explicando como utilizar las librerías del compilador XC8 (antes C18) para microcontroladores PIC18. Para estas guías se tomará como base el popular microcontrolador PIC18F4550 de bajo costo y muy buenas prestaciones. Aclaro, que con estas guías no pretendo sustituir ninguna otra existente, ni proponer algoritmos y estrategias de programación ya que no soy programador profesional sino un simple hobbista por lo que los circuitos y códigos propuestos son producto de la experimentación (Si, todos funcionan…) de manera que siempre será posible que se puedan encontrar mejores diseños.

Por otra parte, si bien las librerías facilitan enormemente el trabajo del programador, tienen el inconveniente de hacer que los programadores dejen de conocer los registros de control de los periféricos integrados (timers, ADC, PWM, UART, etc.) del microcontrolador. En mi humilde experiencia, es necesario conocer dichos registros para saber o al menos tener una idea de como configurar “manualmente” dichos periféricos para así comprender lo que las librerías hacen por medio de los métodos que proporcionan.

También es indispensable tener conocimiento del lenguaje C, electrónica analógica y digital, y obviamente de microcontroladores PIC. En otras palabras, estas mini-guías están orientadas a aquellos que ya han trabajado con microcontroladores y buscan una manera mas sencilla de realizar sus programas.

¿Qué se necesita?

  • Compilador XC8. En las versiones mas recientes de XC8, ya no se incluyen las librerías periféricas XC8, por lo que es necesario instalarlas por separado, consulte aquí para mayor información.
  • MPLAB X
  • Un software de simulación de circuitos microcontrolados como PROTEUS.
  • Una placa de desarrollo.
  • Un programador.
  • Microcontrolador PIC18F4550 y su datasheet.
  • Guía de referencia de las librerías del lenguaje C para microcontroladores PIC.

Respecto a la placa de desarrollo, puede ser cualquiera para PIC DIP40 o se puede montar en breadboard, en cuanto al programador yo utilizo bastante el PICKIT3 (se puede adquirir un clon en ebay por USD $30) o el CANAKIT PICKIT2.

También hay un programador buenísimo llamado BRENNER 8, el cual recomiendo para todos aquellos que quieran montar su propio programador profesional, mis alumnos han montado en varias ocasiones este programador por lo que el funcionamiento está garantizado. Eso sí, está en alemán, pero nada que no pueda solucionarse con un buen traductor, al igual que los programadores antes mencionados, el brenner 8 permite programar microcontroladores usando el ICSP.

Por el momento, llegaré hasta acá con esta pequeña introducción volveré posteriormente con la primer mini-guía que estará orientada al manejo de la librería XLCD.

Saludos,