¿No encuentro las librerías en XC8?

Un saludo a tod@s, hace ya mucho tiempo que no actualizo este blog, en parte porque estoy ocupado en otras cosas y, porque desde hace algún tiempo Microchip ha ido relegando la utilización de las librerías periféricas hasta el punto que generan problemas de compilación con las últimas versiones de XC8. Es lógico que Microchip quiera “descontinuar” estas viejas librerías y enfocarse en crear nuevas herramientas que faciliten la tarea del diseñador.

Hay una nueva herramienta MCC que se ve interesante, lamentablemente el soporte de microcontroladores es muy reducido y está enfocado a los nuevos modelos. Como no lo he utilizado, no puedo opinar sobre si es bueno o malo, pero definitivamente es algo que haré y publicaré en este mismo blog.

En todo caso, para aquellos que tienen problemas con las nuevas versiones de XC8, les sugiero utilizar la XC8 versión 1.34 que fue la última en incorporar soporte completo  a las librerías periféricas. De igual manera continuaré publicando ejemplos de aplicación de los módulos periféricos del procesador utilizando estas “viejas” librerías, en cuanto me quede un poco de tiempo.

¡Hasta pronto!

Publicado en Uncategorized | Deja un comentario

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,

Publicado en Microcontroladores PIC | Etiquetado , , , , , | 3 comentarios

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,

Publicado en Microcontroladores PIC | Etiquetado , , , | 2 comentarios

Instalación de librerías periféricas XC8

En las versiones mas recientes del compilador XC8, ya no se incluyen las librerías periféricas XC8 ya que el directorio plib no existe. Por lo que es necesario descargarlas e instalarlas por separado para poder seguir utilizándolas. Afortunadamente, Microchip incorpora el enlace de descarga de las librerías (peripheral libraries) en la misma página desde donde descargamos XC8. El siguiente es el proceso general de instalación del entorno de desarrollo (omitir los dos primeros pasos si ya se tienen instalados MPLAB X y XC8):

  1. Descargar e instalar el compilador XC8.
  2. Descargar e instalar MPLAB X.
  3. Descargar las librerías periféricas XC8.
  4. Instalar las librerías periféricas en el directorio donde instalamos XC8 (generalmente el instalador detecta el directorio de instalación de XC8). Esperamos a que finalice el proceso de instalación.
  5. Finalmente, ingresamos al directorio de instalación de XC8, en mi caso la ruta es: C:\Program Files (x86)\Microchip\xc8\v1.37\sources\
  6. Verificamos que aparece el directorio plib, ingresamos en él y podremos observar todas las librerías.

plibAbrimos MPLAB X y creamos un proyecto. Damos click derecho al nombre del proyecto, seleccionamos Properties y en la ventana de propiedades damos click en XC8 linker y  finalmente marcamos la opción Link in Peripheral Library.

linker

Ahora ya podremos utilizar las librerías desde MPLAB X en nuestro proyecto. Espero que esta pequeña guía les sea de utilidad.

Saludos,

Publicado en Uncategorized | 1 Comentario

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.

Publicado en Microcontroladores PIC | Etiquetado , , , , | 3 comentarios

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

Publicado en Microcontroladores PIC | Etiquetado , , , , | Deja un comentario

06 – Interrupción de Cambio en Puerto B del PIC18F4550

Las interrupciones son muy importantes en los sistemas electrónicos microcontrolados ya que proveen un canal a través del cual los dispositivos periféricos solicitan la atención del microprocesador (que se encuentra dentro del encapsulado) ante eventos tales como: finalización de conversión A/D, desbordamiento de un Timer, click o movimiento de un puntero (ej. ratón), presión de teclas de un teclado, etc.

Todos los dispositivos de hardware de una computadora tienen asignada una interrupción (IRQ) para comunicar al microprocesador que deben ser atendidos. De esta manera el CPU puede pausar momentáneamente la ejecución de un proceso para atender otros y así sucesivamente.

El PIC18F4550 cuenta con las siguientes interrupciones:

  • Externas: en este tipo de interrupciones, el microcontrolador utiliza uno de sus pines I/O para recibir la interrupción desde un dispositivo externo. Estas a su vez pueden ser: INT: denominadas “Edge-Triggered” debido a que se pueden programar para activarse en su flanco de subida o flanco de bajada. Son las interrupciones idóneas para controlar dispositivos externos. De cambio en PORTB: “Interrupt-On-Change”, es un tipo de interrupción que utiliza los bits RB4:RB7 del PIC, la interrupción se activará cuando en alguno de estos pines se produzca un cambio de estado de 1 a 0 o viceversa.
  • Periféricas: son interrupciones que se originan en los módulos periféricos internos (dentro del encapsulado) del microcontrolador, tales como: TIMER, ADC, EUSART, CCPx, etc.

 

Interrupciones de Cambio de PORTB.

La interrupciones más simples son las de “Cambio en PORTB”, cuando están habilitadas el microcontrolador monitorea constantemente los pines KBI0, KBI1, KBI2 y KBI3 que corresponden a RB4, RB5, RB6 y RB7 respectivamente. Cualquier cambio de estado en estos pines activará una interrupción.

El registro INTCON, se utiliza para configurar y habilitar la interrupción de cambio en PORTB, su estructura es la siguiente:

INTCON

  • El bit RBIE: habilita la interrupción de cambio en PORTB al asignarle un valor de 1.
  • RBIF: es la bandera de interrupción de PORTB, se pondrá en 1 cada vez que se produzca un cambio en PORTB. Después de atender la interrupción debe ser inicializada en 0.
  • GIE: Es el bit de habilitación de interrupciones globales, debe activarse asignándole un valor de 1.

Cada vez que se produzca una interrupción, el microcontrolador ejecutará un método, cuya firma típica es: void interrupt isr(void) {}. Inicialmente, dentro de este método se debe verificar que bandera de interrupción está activada para así poder atenderla por medio del código correspondiente. El siguiente segmento de código muestra la estructura básica del método.


void interrupt isr(void)
{
 if(RBIF) //Si hay cambio de estado en PORTB
 {
 //Código de atención de la interrupción
 RBIF = 0; //Desactivar bandera...
 }
}

Siempre se debe verificar si la bandera de interrupción deseada está activada ya que en una aplicación real se tienen varias interrupciones habilitadas.

Ejemplo de implementación

Para demostrar cómo utilizar la interrupción de cambio en puerto B, utilizaremos el siguiente circuito:

10_INT_PORTB

El funcionamiento es simple, el microcontrolador mostrará constantemente un conteo de 0 a 255 en el puerto B. Si se produce un cambio en el puerto B, se detendrá el conteo y se mostrará en los leds en que bit se ha producido el cambio. Ej. Si se cambió el estado de KBI0, se mostrará 00000011, si el cambio sucedió en KBI1 se mostrará 00001100 y así sucesivamente. Pasado un segundo, se continuará con el conteo. El código 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);
int KBIx;
int old_PORTB = 0B11110000;

void main()
{
 ADCON1 = 0x0F;
 TRISB = 0xFF;
 TRISD = 0;
 PORTD = 0;


 INTCONbits.RBIE = 1; //Habilitar interrupciones de puerto B.
 INTCONbits.RBIF = 0; //Bandera desactivada.
 INTCONbits.GIE = 1; //Interrupciones globales habilitadas.

 while(1)
 {
 for(int i = 0; i <= 255;i++)
 {
 PORTD = i;
 retardo(10);
 }
 }
}

void interrupt isr(void)
{
 if(RBIF) //Si hay cambio de estado en PORTB
 {
 //==============================================
 //Según datasheet debe incorporarse esto...
 if(PORTB)
 {
 asm("nop");
 }
 //==============================================
 KBIx = PORTB ^ old_PORTB; //Hacemos una operación XOR
 old_PORTB = PORTB;
 KBIx = KBIx >> 4;
 switch(KBIx)
 {
 case 1:
 PORTD = 0B00000011;
 break;
 case 2:
 PORTD = 0B00001100;
 break;
 case 4:
 PORTD = 0B00110000;
 break;
 case 8:
 PORTD = 0B11000000;
 break;
 }
 retardo(100);
 RBIF = 0; //Desactivar bandera...
 }
}

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

El microcontrolador detectará cambios en el puerto B cuando se hace una transición de 0 a 1 y viceversa, esto último podemos comprobarlo manteniendo presionado el botón por más de un segundo y posteriormente soltarlo.

En conclusión, este tipo de interrupción es muy simple y nos ha servido como base para que en futuros artículos podamos abordar las interrupciones de los módulos internos del microcontrolador (nuestro principal objetivo). Volveré posteriormente abordando el tema de las interrupciones externas. Saludos a tod@s desde El Salvador.

Publicado en Uncategorized | Etiquetado , , , , | 1 Comentario