TUTORIAL OBJETIVOS • Hacer uso de las interrupciones. • Conocer
Transcripción
TUTORIAL OBJETIVOS • Hacer uso de las interrupciones. • Conocer
Curso de Microcontroladores [email protected] 1 TUTORIAL OBJETIVOS • • Hacer uso de las interrupciones. Conocer el problema de los rebotes en los pulsadores y plantear algunas formas de solucionarlo. INTRODUCCIÓN: Utilizaremos dos importantes funciones que nos ofrecen los microcontroladores, los temporizadores y las interrupciones externas, por medio de ellas incrementaremos o decrementaremos un contador el cual será mostrado por medio de displays de siete segmentos. A continuación dividiremos el problema en dos, primero abordaremos el problema del conteo utilizando el timer0 y las banderas de interrupción externa, luego abordaremos la parte de la visualización complementando lo visto en la práctica anterior. FUNDAMENTOS TEÓRICOS: Del funcionamiento del microcontrolador necesitamos conocer dos módulos muy importantes: El timer0 y la interrupción externa (int). • El timer0: Ya se ha trabajado tanto en el tutorial anterior como en la parte teórica, en este caso lo usaremos como contador de eventos internos. Refiérase a la guía anterior para los detalles referentes a este. • La interrupción externa: Estas se manejan en el pin RB0, este pin puede configurarse para que cuando se perciba un cambio de señal de alto a bajo o de bajo a alto (flanco de bajada o de subida) se encenderá una bandera (INTF del registro INTCON) y se disparará la interrupción si esta estaba habilitada. Solo se puede configurar si la interrupción se disparará en el flanco de subida o de bajada y se hace por medio del registro OPTION_REG bit INTEDG en el cual con un 1 indicamos flanco de subida y con un cero flanco de bajada. REALIZACIÓN DEL CONTEO: Recuérdese que tenemos un pulsador en el pin RA4 (TOCKI) el cual nos permitirá incrementar la cuenta y otro pulsador en RB0 (INT) el cual nos permitirá Curso de Microcontroladores [email protected] 1 Curso de Microcontroladores [email protected] 2 decrementarla, vamos por lo tanto a esperar interrupciones dadas por alguno de estos eventos. La estrategia que usaremos será la siguiente: Mantener el TMR0 en un valor de 255 de modo que al presionar el usuario el pulsador de incrementar se disparará la interrupción de TMR0, en la interrupción por TMR0 se procederá a incrementar la cuenta (en una variable contador), volver a cargar el TMR0 con 255 y a limpiar la bandera TMR0IF. Para el decremento simplemente al presionar el pulsador en RB0 se disparará una interrupción externa INT y en esta interrupción decrementamos la variable y colocamos de nuevo la bandera en cero. INTERRUPCIONES: Cuando usamos las interrupciones el microcontrolador responde automáticamente a ciertos eventos haciendo saltar la ejecución del código del punto donde se encuentre a un lugar conocido como vector de interrupción; en dicho punto se debe encontrar el código necesario para atender el evento que genero la interrupción, una vez terminado este código el microcontrolador automáticamente devuelve la ejecución al lugar donde se encontraba cuando fue interrumpido. En los micros de la familia 16 existe un solo vector de interrupción, es decir, independiente de cual bandera de interrupción sea activada (TMR0IF, INTIF, RBIF, ADIF, RCIF, etc) siempre el código será dirigido a la misma ubicación. En las familias 18 y los DSPIC existen varios vectores de interrupción. Cuando sucede una interrupción algunos registros (STATUS y W) deben ser guardados para evitar interferencia con el código en ejecución en el momento que sucede la interrupción, el guardado de dichos registros es realizado por el programador en ensamblador, en C el compilador se encarga de agregar este proceso. Mientras el microcontrolador se encuentra atendiendo una interrupción no puede atender otra, cualquier interrupción generada durante este tiempo encenderá la bandera y esperará a que la interrupción anterior sea procesada antes de procesar la actual. Control de interrupciones: Las interrupciones son controladas por medio de registros, en dichos registros se encuentran dos bits por cada interrupción, uno de ellos es la bandera de interrupción (XXIF) el otro es el habilitador de interrupción (XXIE) el cual sirve para que dicha interrupción efectivamente se dispare si esta activo, si esta inactivo solo se encenderá la bandera. Curso de Microcontroladores [email protected] 2 Curso de Microcontroladores [email protected] 3 Aparte de estos bits existe el bit GIE y el PEIE, el GIE (Habilitador global de interrupción) debe estar en uno para habilitar las interrupciones, si esta en cero no permite la interrupción sin importar el estado de los habilitadores individuales. El PEIE habilita las interrupciones por periféricos. En los micros de la familia 16F88X las interrupciones se dividen en varios registros, en el INTCON encontramos los bits GIE, PEIE y los habilitadores y banderas de las interrupciones por TMR0, interrupción externa y por cambio en puerto B. El resto de las 14 interrupciones son interrupciones por periféricos (A/D, CCP, USART, etc) y se manejan en los registros PIE1, PIE2 (Habilitadores) PIR1 y PIR2 (Banderas), El bit PEIE habilita las interrupciones controladas por los registros PIE1 y PIE2. En esta practica trabajaremos con las interrupciones de internas y las del TMR0 por lo tanto trabajaremos el registro INTCON, más adelante se podra trabajar algunos periféricos con sus respectivas interrupciones. Registro INTCON: GIE PEIE T0IE INTE RBIE T0IF INTF RBIF Habilitador global de interrupciones. Habilitador de interrupciones periféricas Habilitador interrupción Timer 0 Habilitador interrupción externa Habilitador interrupción por cambio Bandera de desbordamiento de TMR0 Bandera de interrupción externa Bandera de interrupción por cambio en RB Para todos los bits del registro anterior la lógica es positiva es decir, un uno habilita y un cero deshabilita. Rutina de interrupciones en C.: En el compilador PICC la interrupción se trabaja como una subrutina que hace las veces de un vector de interrupción, dicha subrutina se codifica de la siguiente manera: void interrupt nombre(void) { //Código de la rutina //de servicio a interrupción } Normalmente dentro de esta rutina primero se preguntará que tipo de interrupción fue la causante observando las banderas de interrupción, luego se ejecutará el código correspondiente y luego se limpiara la bandera. Curso de Microcontroladores [email protected] 3 Curso de Microcontroladores [email protected] 4 VISUALIZACIÓN DE LOS DATOS: El hardware a implementar seria como sigue: Obsérvese en dicho diagrama que cada línea de selección maneja un display, cuando dicha línea se encuentra en cero se produce el encendido de dicho display según los segmentos indicados en las líneas de datos, si dicha línea se encuentra en uno se apagarán todos los segmentos del display correspondiente. Es necesario entonces realizar un refrescamiento continuo de dichos displays, se enciende el primer display con el dato de las decenas, luego este se apaga y se enciende el segundo display con el dato de las unidades y se repite este proceso a una frecuencia suficientemente alta de modo que el ojo humano no pueda detectar los continuos prende apaga de los display. Paso previo a la visualización: Como puede notarse, para poder visualizar un dato numérico con displays de siete segmentos, es necesario primero dividirlo en dígitos (en nuestro caso solo unidades y decenas) para luego enviar cada dígito al display correspondiente usando los códigos correspondientes. Necesitamos entonces una función que nos permita realizar dicha separación, el compilador nos presenta una función que permite realizar divisiones y obtener en Curso de Microcontroladores [email protected] 4 Curso de Microcontroladores [email protected] 5 distintos registros el cociente y el residuo. Dicha función se denomina div y se explicará a continuación. La función div recibe dos argumentos tipo entero (int aunque también funciona para char) y realiza su división computando su cociente y su residuo, el resultado se obtiene de una variable tipo estructura que contiene tanto el residuo como el cociente y debe ser leído de manera especial. 1. Definimos la variable tipo estructura que va a recibir el resultado, el tipo de esta estructura (div_t) está definida al igual que la función div en la librería stdlib.h la cual debemos incluir. Para definir la variable recurrimos a la siguiente sintaxis: div_t nombre_variable; donde estamos diciendo que la variable nombre_variable es tipo div_t. 2. Ahora procedemos invocar la función, lo realizamos mediante la siguiente sintaxis: nombre_variable=div(dividendo,divisor); Donde estamos diciendo que realice la función div con los argumentos dividendo y divisor, y que el resultado de dicha función lo reciba en la variable nombre_variable. 3. En nombre_variable tenemos entonces tanto el cociente como el residuo en forma de estructura, para poder leer dicha estructura lo hacemos de la siguiente forma: nombre_variable.quot; Nos devuelve el cociente. nombre_variable.rem; Nos devuelve el residuo. Los valores devueltos pueden ser guardados en variables para ser usados más adelante en el código. Curso de Microcontroladores [email protected] 5 GND VCC R2 10k R3 10k R1 10k 1 2 3 4 5 6 7 14 13 33 34 35 36 37 38 39 40 U1 RD0 RD1 RD2 RD3 RD4 RD5/P1B RD6/P1C RD7/P1D RE0/AN5 RE1/AN6 RE2/AN7 RE3/MCLR/VPP RC0/T1OSO/T1CKI RC1/T1OSI/CCP2 RA0/AN0/ULPWU/C12IN0RC2/P1A/CCP1 RA1/AN1/C12IN1RC3/SCK/SCL RA2/AN2/VREF-/CVREF/C2IN+ RC4/SDI/SDA RA3/AN3/VREF+/C1IN+ RC5/SDO RA4/T0CKI/C1OUT RC6/TX/CK RA5/AN4/SS/C2OUT RC7/RX/DT RA6/OSC2/CLKOUT RA7/OSC1/CLKIN RB0/AN12/INT RB1/AN10/C12IN3RB2/AN8 RB3/AN9/PGM/C12IN2RB4/AN11 RB5/AN13/T1G RB6/ICSPCLK RB7/ICSPDAT PIC16F887 15 16 17 18 23 24 25 26 19 20 21 22 27 28 29 30 8 9 10 1 2 3 4 5 6 7 8 RN1 330 16 15 14 13 12 11 10 9 6 [email protected] Curso de Microcontroladores 6 [email protected] Curso de Microcontroladores ESQUEMA DE CONEXIONES: Curso de Microcontroladores [email protected] 7 CODIFICACIÓN: A continuación se ilustra un código posible que ilustra lo dicho anteriormente, dicho código puede realizarse de varias formas, al código mostrado falta agregarle validaciones para mantener el contador en dos dígitos (0-99) se propone que cada uno diseñe y agregue esta parte y lo implemente. #include <pic.h> #include <stdlib.h> #include "delay.h" #include "delay.c" unsigned char contador=0, unidades=0, decenas=0, bandera=0; //unsigned char display[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x98}; unsigned char display[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; div_t cocienteresiduo; void main(void) { ADCON1=6; //Desactivo entradas analogas ANSEL=0x00; ANSELH=0x00; TRISC=0b10000000; //Puerto C como salidas (7 segmentos) TRISB=0b11111001; //Puerto B como entrada(RB7,RB6) y salida(RB1 y 2 7seg) INTCON=0b10110000; //Habilito iterrupciones OPTION=0b11100000; //Configuración del TMR0 y la interrupción externa. TMR0=255; while(1) { if(bandera==1) { cocienteresiduo=div(contador,10); //Actualizo los valores de decenas y unidades unidades=cocienteresiduo.rem; //solo si la bandera es 1. decenas=cocienteresiduo.quot; bandera=0; //Devuelvo la bandera a su valor por defecto. } PORTC=display[unidades]; RB1=0; RB2=1; DelayMs(50); PORTC=display[decenas]; RB2=0; RB1=1; DelayMs(50); //Envio unidades. //Activo display de unidades. //Retardo de visualización. //Envio las decenas. //Activo display de decenas. //Retardo de visualización. } } Curso de Microcontroladores [email protected] 7 Curso de Microcontroladores [email protected] 8 void interrupt RSI(void) { if(T0IF==1) //Me aseguro que sea interrupción por cambio de TMR0 { T0IF=0; if(contador==99) contador=0; contador++; bandera=1; TMR0=255; } if(INTF==1) { INTF=0; if(contador==0) contador=99; contador--; bandera=1; } } Curso de Microcontroladores [email protected] 8