TODOPIC

Microcontroladores PIC => Almacén del Assembler => Mensaje iniciado por: Chenao en 12 de Julio de 2009, 14:30:36

Título: Manejo de interrupciones
Publicado por: Chenao en 12 de Julio de 2009, 14:30:36
saludos

El siguiente pretende explicar, programar y simular las causas de interrupcción de un microcntrolador PIC (basicamente el 16f877) por que es con el que más trabajo  :mrgreen:

Sin más preambulos he preparado una introducción que ya más de uno la conoce pero para los que no la conocen les puede servir para entender hacia donde vamos.

IMPOTANCIAS DE LA INTERRUPCIONES

Las llamadas a subrutinas mediante la instrucción CALL son desviaciones del flujo de control del programa originadas por instrucciones, por lo que se consideran síncronas. Se producen cada vez que se ejecuta dicha instrucción.

Las interrupciones son desviaciones del flujo de control del programa originadas asincrónicamente por diversos sucesos que no se hallan bajo la supervisión de la instrucciones. Dichos sucesos pueden ser externos al sistema, como la generación de un flanco o nivel activo en un terminal del microcontrolador, o bien internos, como el desbordamiento de un contador. En general se puede decir que hay dos clases de interrupciones que son por software y hardware.   .

El comportamiento del microcontrolador ante la interrupción es similar al de la instrucción CALL de llamada a subrutinas. En ambos casos se detiene la ejecución del programa en curso, se salva la dirección actual de PC(contador del programa) en la pila y se carga el PC con una dirección, que en el caso de CALL viene acompañado a la propia instrucción, y en caso de la interrupción es una dirección “reservada” de la memoria del código, llamada vector de interrupciones.

En los PIC16X8X el vector de interrupción se halla situado en la dirección 0004H, en donde comienza la rutina la rutina de servicio a la interrupción (RSI). En general, en dicho vector se suele colocar una instrucción de salto incondicional (GOTO), que se traslada el flujo de control a la zona de la memoria de código destinada a contener destinada la rutina a la interrupción

La RSI suele comenzar guardando en la memoria de datos algunos registros específicos del procesador. Concretamente aquellos que la RSI va a emplear y va alterar su contenido. Antes del retorno al programa principal se recuperan los valores guardados y se restauran completamente el estado del procesador. Algunos procesadores salvan estos registro en la Pila, pero los PIC no disponen de instrucciones para meter (push) y sacar (pop) información de la Pila (o no las conozco), utilizando para este fin registros de propósito general de la memoria de datos.

Los PIC16X8X pueden ser interrumpido por varia causas (desborde de un contador, flanco activo en RB0), pero todas desvían es averiguar cuál es la posible causa que ha sido la responsable de la interrupción en curso. Para ello se exploran los señalizadores de la fuente de interrupción.

Otro detalle importante en la RSI de los PIC16XFX es que los microcontroladores poseen un bit GIE (Global Interrupt Enable) que cuando vale 0 prohíbe todas la interrupciones. Pues bien, al comenzar la RSI dicho bit GIE se pone automáticamente a 0, con el objeto de no atender nuevas interrupciones hasta que se termine la que ha comenzado. En el retorno final de la interrupción, GIE pasa automáticamente a valer 1 para volver a tener en cuenta las interrupciones. Dicho retorno de interrupción se realiza mediante la instrucción RETFIE no debe cometer el error de confundir la instrucción RETFIE con la instrucción RETURN la dos tiene funciones diferentes.

Espero que les halla gustado la introducción cualquier sugerencia, preguntas o jalada de orejas son bien resividas  :D

hasta un próxima entrega un saludo desde Pereira - Colombia
Título: Re: Manejo de interrupciones
Publicado por: groundman en 13 de Julio de 2009, 17:43:04
esta muy bien el poner informacion sobre las interrupciones.asi hay mas ayuda. :)
y como tu dices estos pic no tienen las instruciones POP y PUSH.
pero podemos emularlas.y es mas debemos de usarlas para no tener problemas cuando utilicemos interrupciones.abajo pongo el codigo.anque segun que registros estemos usando durante una interrupcion,quizas tengamos que incluir algunas instrucciones mas.y en otros programas habria que quitar otras para optimizar el codigo.

PUSH      MACRO               ;macro para salvar registros antes de una llamada a interrupcion   
         movwf   W_TEMP               
            swapf   STATUS,W
            movwf   STATUS_TEMP         
            clrf      STATUS               
            movfw   PCLATH               
           movwf   PCLATH_TEMP
            clrf      PCLATH
         ENDM

POP         MACRO               ;macro para restaurar registros   despues de una llamada a interrupcion
         movfw   PCLATH_TEMP         
            movwf   PCLATH
            swapf   STATUS_TEMP,W     
            movwf   STATUS
            swapf   W_TEMP,F         
            swapf   W_TEMP,W
         ENDM
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 13 de Julio de 2009, 21:53:36
Saludo

un saludo compañero groudman. Cuando vas hablar de esto de los push y pop mas detenidamente es muy importante como tu dices ojala compartas tus conocimiento con los novatos :D :D.

Bueno hoy les traigo la teoria DEL TIMER0 como hacer demoras y como manejar la interrupcion por desborde sin más chachara ahy le va lo que he preparado.

Registros de control de interrupciones del timer0

Antes que nada es importante aclarar algo

Antes del retorno conviene borrar el señalizador de la causa de la interrupción (es obligatorio) que se ha atendido, porque si bien los señalizadores se ponen automáticamente en 1 cuando se produce la causa que indica, la puesta a 0 se hace por SOFTWARE. 

La mayor parte de los señalizadores y bits de permiso de las fuentes de interrupción en los PIC16X8X están implementados sobre los bits de registro INTCON (También hay otros importantes pero por ahora no concentraremos en este registro), que ocupa la dirección 0Bh del banco 0, hallándose duplicado en el banco 1

Bits del registro INTCON

GIE: permiso global de interrupciones :1 permite la ejecución de todo las interrupciones, cuyos bits de permiso individuales también las permitan.

TOIE : Permiso de interrupción de sobrepasamiento del TMR0

1: Permite la interrupción al desbordase el timer0
0: prohíbe esta interrupción

TOIF: Señalizador de sobrepasamiento del TMR0

1: hubo desborde del TMR0
0: indica que el TMR0 no se ha desbordado

Otro registro importante es el OPTION_REG el cual tiene como dirección 81h y esta ubicado en el banco 1 de la memoria

PSA: Bit de asignación del divisor de frecuencias

1: Divisor de frecuencia es asignado al WDT
0: Divisor de frecuencia es asignado al TMR0

Interrupción por desborde del TMR0

Cuando el TMR0 se desborda y pasa del valor 255 a 0, el señalizador TOIF se pone automáticamente a 1. Si, además, el bit de permiso de la interrupción del TMR0, TOIE = 1 y el bit de permiso global de interrupciones, se produce una interrupción.

Si no se recarga el TMR0 cuando se desborda, sigue contando desde 0 a 255. En cualquier momento se puede leer y escribir este registro, pero cada vez que se escribe se pierden dos ciclos de reloj para la sincronización.

Cuando se carga inicialmente el TMR0 con el valor N, cuenta 256 – N impulsos, siendo el tiempo que tarda en hacerlo el que expresa la siguiente ecuación

Temporización = 4*Tosc*(256 – TMR0)*Divisor de frecuencias

Con un cristal de 4MHz se puede contar como mínimo 256us y como máximo 65.536ms dato importante. Listo ya estamos preparados (por lo menos yo) para poner a contar nuestro TIMER0.

El PROGRAM

                                                       

;************************************************************
;* Carlos Alberto Henao                                                                      *
;* Interrupción por desborde del timer0 con el pic 16f877                      *
;* Foro técnico de microcontroladores "todopic"              *
;* 13 de julio del 2009 Pereira - Colombia                                            *
;************************************************************


;******************** Declaración de registros especiales *********************************

tmr0      equ      01h
status      equ      03h
porta      equ      05h
portb      equ      06h
portc      equ      07h
portd      equ      08h
porte      equ      09h
intcon      equ      0bh

option_reg   equ      81h
trisa      equ      85h
trisb      equ      86h
trisc      equ      87h
trisd      equ      88h
trise      equ      89h
adcon1      equ      9fh

;******************* registros de usuario *************************************************

cuenta      equ      20h


list p = 16f877

;***************** va a empezar el programa ***********************************************

   org   00
   goto   inicio
   
   org   04
   goto   inter


;**************** Rutina de manejo de interrupción ****************************************

inter   btfsc   intcon,2   ; pregunto si hubo interrupción por timer0
   goto   timer0
   retfie

timer0   incf   portb,1      ; incremento el puerto b en 1
   clrf   timer0      ; inicializo de nuevo el timer para tiempo máximo
   bcf   intcon,2   ; borro el flag
   retfie         ; retorno al pcl+1


;*************** rutina de principal (main) ***********************************************

inicio   bcf   status,6
   bsf   status,5   ; banco 1
   movlw   b'11111111'
   movwf   trisa
   movwf   trisc
   movwf   trisd
   movwf   trise      ; puerto A, C,D,E como entradas digitales
   movlw   b'00000000'
   movwf   trisb      ; puerto B como salidas digitales
   movlw   b'11010111'
   movwf   option_reg   ; configuro el timer a un predivisor de frecuancia de 1:256
   movlw   b'00000110'
   movwf   adcon1      ; configuro puerto A Y E como entradas digitales   
   bcf   status,5   ; banco 0
   clrf   porta
   clrf   portb
   clrf   portc
   clrf   portd
   clrf   porte      ; limpio todo los puertos
   movlw   b'10100000'
   movwf   intcon      ; habilito la interrupcion global y por desborde del timer0
            ; ya estamos listos solo falta cargar el valor correspondiente
            ; en el registro tmro

   clrf   tmr0       ; limpio el timer para que me produsca un máximo valor de
            ; tiempo
nada   goto   nada      ; bucle infinito esperando a que ocurra la interrupción
   end


Bueno cualquier cosa me comentan. no vemos. Un saludo desde Pereira - Colombia
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 13 de Julio de 2009, 22:03:35
Saludos

Acá la simulación utilizo el proteus 7.2 espero que la disfruten y les sean útil

Un saludo desde Pereira - Colombia
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 18 de Julio de 2009, 18:47:54
Saludos

Hoy les traigo un ejemplo muy interesante, para lo que quieren ahorrace el dolor del cabeza de estar llamando una rutina para manejar un display de doble segmento Además a los amigos que le gusta optimizar código esta es una buena opcion sin chachara aquí lo preparado.

Ejemplo Numero  2 “Manejo de interrupción del TMR0.  Multiplexación de un display de cátodo común utilizando el TMR0”

Mucha veces tenemos que hacer cálculos “tediosos en asembler” divisiones, multiplicaciones o en general rutinas que demanda mucho tiempo de ejecución y al mismo tiempo tenemos que manejar un display de ánodo común o cátodo común por ejemplo (midiendo temperatura, un velocímetro etc.) por lo tanto, este ejemplo pretende ilustrar como utilizar la interrupción del TMR0 para refrescar los cátodos del display en paralelo.

En los hilos anteriores dijimos que Tt = 4*Tosc*(256- Vtimer)*Pf (1.1)

Donde Pf = Divisor de frecuencias
Tosc = Tiempo del oscilador
Tt = Tiempo de temporización
Vtimer = Es el valor cargado en el registro TMR0 de la memoria RAM

Para refrescar los displays necesitamos prender uno por un lapso de unos 11ms e ir alternando para lograr préndelos indefinidamente.

Para que el timer0 cuente 11ms vasta con cargar el registro option_reg con un predivisor de 1:64 asignado al timer0.

La ecuación 1.1 se puede escribir de la siguiente manera

Vtimer = 256 – (Tt/(4*Tosc*pf))

Ahora para temporizar 11ms Vtimer aproximadamente es 84d

Con este valor del Vtimer Tt = 11.008ms es un valor muy acertado con este valor podemos darnos cuenta que para realizar rutinas de tiempo utilizando las interrupciones por desborde del TMR0 quedan bastante exactas, otra cosa que podemos decir es que entre mas pequeño se el valor del divisor de frecuencia (Pf) y la frecuencia del oscilador mas resolución tendrá nuestro TMR0, en desventaja a esto es que nuestro TMR0 contará menos, es decir, las rutinas de tiempo por interrupción serán menores.

El código


                                                      

Código: [Seleccionar]
;************************************************************
;* Carlos Alberto Henao                                                                      *
;* Interrupción por desborde del timer0 con el pic 16f877                      *
;* Para multiplización de display de doble segmento                         *
;* Foro técnico de microcontroladores "todopic"         *
;* 16 de julio del 2009 Pereira - Colombia                                            *
;************************************************************


;******************** Declaración de registros especiales *********************************

tmr0 equ 01h
status equ 03h
porta equ 05h
portb equ 06h
portc equ 07h
portd equ 08h
porte equ 09h
intcon equ 0bh

option_reg equ 81h
trisa equ 85h
trisb equ 86h
trisc equ 87h
trisd equ 88h
trise equ 89h
adcon1 equ 9fh

;******************* registros de usuario *************************************************

conta equ 20h ; contador
loop equ 21h
loop1 equ 22h ; registro de para la rutina de tiempo
digito1 equ 23h
digito2 equ 24h ; son los digitos del display


list p = 16f877

;***************** va a empezar el programa ***********************************************

org 00
goto inicio

org 04
goto inter


;**************** Rutina de manejo de interrupción ****************************************

inter btfsc intcon,2               ; pregunto si hubo interrupción por timer0
goto timer0
retfie

timer0 movf conta,0 ; paso lo de conta a w
xorlw d'0'
btfss status,2                  ; pregunto si conta es cero para saber que display activar
goto display2
goto display1

display1
bsf portc,1
bcf portc,2 ; activo el display 1 por 11.008ms
movf digito1,0                 ; paso digito1 a w
movwf portb ; paso el valor a visualizar
bsf portc,1
bcf portc,2 ; activo el display 1 por 11.008ms
movlw d'1'
movwf conta
movlw d'84'
movwf tmr0 ; vuelvo cargar el valor 84d para que el timer se
; desborde de nuevo en 11.008ms

bcf intcon,2                 ; borro el flag
retfie ; retorno al pcl+1

display2
bsf portc,2
bcf portc,1 ; activo el display 2 por 11.008ms
movf digito2,0                 ; paso digito1 a w
movwf portb ; paso el valor a visualizar
bsf portc,2
bcf portc,1 ; activo el display 2 por 11.008ms

clrf conta ; limpio a conta para que en el otro ciclo refresque al
; display1
movlw d'84'
movwf tmr0 ; vuelvo cargar el valor 84d para que el timer se
; desborde de nuevo en 11.008ms
bcf intcon,2                 ; borro el flag
retfie ; retorno al pcl+1


;*************** Rutina de tiempo antirebote **********************************************

retar movlw d'100'
movwf loop
movlw d'100'
movwf loop1
decfsz loop1,1
goto $-1
decfsz loop,1
goto $-5
return

;*************** rutina de principal (main) ***********************************************

inicio bcf status,6
bsf status,5                 ; banco 1
movlw b'11111111'
movwf trisa
movwf trisd
movwf trise ; puerto  C,D,E como entradas digitales
movlw b'11111001'
movwf trisc ; configuro el pueto A los primeros 2 terminales como salidas
; digitales las otras 4 como entradas digitales
movlw b'11110000'
movwf trisb ; puerto B como salidas digitales
movlw b'11010101'
movwf option_reg                 ; configuro el timer a un predivisor de frecuancia de 1:64
movlw b'00000110'
movwf adcon1 ; configuro puerto A Y E como entradas digitales
bcf status,5                 ; banco 0
clrf porta
clrf portb
clrf portc
clrf portd
clrf porte ; limpio todo los puertos
movlw b'10100000'
movwf intcon ; habilito la interrupcion global y por desborde del timer0
; ya estamos listos solo falta cargar el valor correspondiente
; en el registro tmro

clrf conta ; limpio el contador
clrf digito1 ; pongo el display en ceros
clrf digito2
movlw d'84'
movwf tmr0 ; pongo 84 en el timer para que cuente 11.008 ms y se
; desborde
clrf portb
pulsa btfsc portc,0
goto $-1
call retar
btfss portc,0 ; ha pulsado el botom
goto $-1
incf digito1,1                 ; incremento digito1
movf digito1,0
xorlw d'10'
btfss status,2
goto pulsa
clrf digito1 ; si llego a 10 pongo el digito2 en 1 y el otro en 0 para escribir 20
; 30.....99
incf digito2,1
movf digito2,0                 ; paso digito2 a w
xorlw d'10'
btfss status,2
goto pulsa ; si no a llegado a 99 sigo incrementado
clrf digito2 ; vuelvo a inicializar el ciclo desde 00 hasta 99
goto pulsa

nada goto nada ; bucle infinito esperando a que ocurra la interrupción
end

Bueno espero que les halla gustando el post. Un saludo desde Pereira - Colombia
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 18 de Julio de 2009, 18:50:39
Saludos

Acá la simulación en el proteus 7.2 espero que la disfruten.

Un saludo desde Pereira - Colombia
Título: Re: Manejo de interrupciones
Publicado por: Chrisbaf en 19 de Julio de 2009, 19:57:58
Gracias Chenao por compartir tus conocimientos y tu tiempo con el foro ..sigue adelante  :)
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 20 de Julio de 2009, 13:47:48
Saludos Chrisbaf

La verdad es que se vienen ejemplos muy interesantes (al menos a mi me parecen) estes pendiente por que si te gusto este ejemplo el otro también te va a gustar.

Un saludo desde Pereira - Colombia
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 25 de Julio de 2009, 18:09:54
Saludos

Una de la técnicas más "aburridoras" :x :x (si que saco canas cuando estuve haciendo mi proyecto con el acelerómetro ADXL202E) si seño hoy vamos a medir un pulso utilizando interrupción por desborde del TIMER0 sin mas chachara aquí lo preparado.

Midiendo el ancho de un pulso (Duty Cicle)

En muchas aplicaciones es importante medir un pulso (salida de un acelerómetro, un sensor de un velocímetro etc). Para medir un pulso con un microcontrolador existen varias técnica (Por interrupción externa, Señalizadores, Módulo captura, Utilizando los timer del micro) como todavía no hemos hablado de la interrupción externa por RBO, nos figuró utilizar señalizadores (intrusiones BTFSC, BTFSS) que son poco eficientes pero (comparada con la otra técnica) pero también funciona.

Bueno hablemos de un pulso (consultar definición de la función escalón unitario aplicando la transformada de laplace). Básicamente existen dos clases de pulsos los síncronos y asíncronos. El pulso síncrono es cuando TI = T2 = To (ver figura) por lo tanto el Periodo T = (2To) y los asíncronos donde T = (T1 + T2), en los asíncronos se puede decir que T1 es diferente de T2 (tenemos una señal modulada por ancho de pulso PWM) por lo que TI será proporcional a T entonces T1 = KT por lo tanto T2 = (1-k)T.


(http://img139.imageshack.us/img139/1948/pulso.jpg)

Donde T = Periodo del pulso
          T1 = nivel alto del pulso
          T2 = nivel bajo del pulso

Ya que se acabo la parte teórica “interesante” pasemos a la parte del micro. Según la figura para medir un pulso se puede seguir el siguiente algoritmo.

1.   Pregunta por estado 0 lógico
2.   Pregunta cuando si se puso en 1 lógico
3.   SÍ se puso en 1 lógico inicializar el timer0
4.   Poner a contar el timer0 hasta que vuelva a ponerse en 0 lógico
5.   Sí el timer se desborda atender la interrupción y incrementar una variable
6.   Por último pasar los valores del timer y de la veces que se ha desbordado a otros registro
7.   Volver a iniciar el ciclo

Bueno como ya dijimos anteriormente el timer0 a 4MHz y con divisor de frecuencias de 1:256 puede medir 65.536 ms es decir una señal de (15.25Hz), por lo que para tiempo pequeños se me queda colgado, por lo tanto necesitamos otro registro (cuenta_h) que tendrá como función la siguiente cada vez que se desborda el time0 esté se incrementara en 1, es decir, se incrementará cada 65.536 ms por lo que ahora podremos medir hasta (65.536*255)ms =16.71168s = 0.06 Hz.

Un dato importante para poder muestrear un señal tendremos que hacer caso del teorema de muestreo que dice que la frecuencia de muestreo debé se dos veces mayor o igual a la natural es decir, Fm > = 2Fn. Como nuestro micro estará configurado con un divisor de frecuencias de 1: 256 quiere decir que podrá medir pulsos de aproximadamente 512 us.

Prueba

Un ejemplo supongamos que nuestro micro necesita medir un pulso con periodo de 5 Hz (con periodo síncrono). Calcule el valor con que se cargara el timer0 y la bandera de interrupción para medir el ancho del pulso si la frecuencia del oscilador es 4MHz y divisor de frecuencias es 1:256.

Solución

Primero Ancho del pulso = 1/5 = 0.2s por lo tanto T1 = (0.2/2) =  s = 100ms

Por lo que nuestro timer0 no podrá medirlo completo por lo necesitamos un variable bandera o de ayuda.

Primero #desbordes = (100/65.536) = 1.525878906

Por lo que el timer0 se desbordara 1 vez para medir este ancho del pulso por lo tanto el valor del timer0 será = (100 –65.536) = 34.464ms

Y utilizando la formula T t= 4*Tosc*(256 - Vtimer)*Pf

Despejando y remplazado Vtimer = 121
               
La conclusión la parte alta (cuenta_h) = 1 , la parte baja será (cuenta_l) = 121  aproximadamente

La conexiones A RA0 conectar un clock o un generador de señales y visualiazar los valores utilizando la tabla de registro del proteus.

El programa

Código: [Seleccionar]
;************************************************************
;* Carlos Alberto Henao                                     *
;* Interrupción por desborde del timer0 con el pic 16f877   *
;* Para medir el ciclo útil de un  pulso                    *
;* Foro técnico de microcontroladores "todopic"     *
;* 13 de julio del 2009 Pereira - Colombia                  *
;************************************************************


;******************** Declaración de registros especiales *********************************

tmr0 equ 01h
status equ 03h
porta equ 05h
portb equ 06h
portc equ 07h
portd equ 08h
porte equ 09h
intcon equ 0bh

option_reg equ 81h
trisa equ 85h
trisb equ 86h
trisc equ 87h
trisd equ 88h
trise equ 89h
adcon1 equ 9fh

;******************* registros de usuario *************************************************

cuenta_l equ 20h ; parete baja de pulso
cuenta_h equ 21h ; parte alta del pulso
bandera equ 23h


list p = 16f877

;***************** va a empezar el programa ***********************************************

org 00
goto inicio

org 04
goto inter


;**************** Rutina de manejo de interrupción ****************************************

inter btfsc intcon,2 ; pregunto si hubo interrupción por timer0
goto timer0
retfie

timer0 incf bandera,1 ; incremento el puerto b en 1
clrf tmr0 ; inicializo de nuevo el timer para tiempo máximo
bcf intcon,2 ; borro el flag
retfie ; retorno al pcl+1


;*************** rutina de principal (main) ***********************************************

inicio bcf status,6
bsf status,5 ; banco 1
movlw b'11111111'
movwf trisa
movwf trisd
movwf trise ; puerto A,D y E como entradas digitales
movlw b'00000000'
movwf trisb ; puerto B Y C como salidas digitales
movlw b'11010111'
movwf option_reg ; configuro el timer a un predivisor de frecuancia de 1:256
movlw b'00000110'
movwf adcon1 ; configuro puerto A Y E como entradas digitales
bcf status,5 ; banco 0
clrf porta
clrf portb
clrf portc
clrf portd
clrf porte ; limpio todo los puertos
movlw b'10100000'
movwf intcon ; habilito la interrupcion global y por desborde del timer0
; ya estamos listos solo falta cargar el valor correspondiente
; en el registro tmro

clrf tmr0 ; limpio el timer para que me produsca un máximo valor de
; tiempo
clrf cuenta_l
clrf cuenta_h
clrf bandera
btfsc porta,0 ; pregunto si esta en cero
goto $-1 ; si no está en cero espero a que se ponga en cero
pulso btfss porta,0 ; si está en uno
goto $-1 ; espero a que a que se ponga en uno
clrf tmr0
clrf bandera
btfsc porta,0 ; si se puso de nuevo en cero ya midio el pulso
goto $-1
movf tmr0,0
movwf cuenta_l
movf bandera,0
movwf cuenta_h
goto pulso ; vuelva y inicia el ciclo
end

Casi se me olvida gracias al compañero Suki por sus sugerencias la verdad que me calleron como anillo al dedo.

Un abrazo desde Pereira - Colombia 
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 25 de Julio de 2009, 18:19:22
Saludos

Acá la simulación en Proteus que la disfruten

Un saludo desde Pereira - Colombia
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 05 de Agosto de 2009, 23:00:25
Saludos

Después de unos dias de descanso hoy voy habla de la interrupción externa o conocida como RB0/INT si más chachara aqui va

Interrupción externa INT

Esta fuente de interrupción es sumamente importante para entender acontecimientos externos en tiempo real. Cuando ocurre alguno de ellos, se activo el terminal RB0/INT y se hace una petición de interrupción. Entonces, de forma automática, el bit INTF = 1 y, si el bit de permiso INT = 1, se autoriza el desarrollo de la interrupción.

Mediante el bit, llamado INTDEG, del registro OPTION se puede seleccionar cuál será el flanco activo en RB0/INT. Si se desea que sea el ascendente se escribe un 1 en dicho bit, y si se desea que sea descendente se escribe un 0.

El procesador explora el señalizador al final del primer ciclo de reloj (de cada ciclo de instrucción). Recuérdese de cada ciclo de instrucción constaba de 4 ciclos de reloj: Q1, Q2, Q3, Q4. Al terminal Q1 se explora los señalizadores produciéndose un estado de latencia de 3 o 4 ciclos de instrucción desde el momento que hay un señalizador activado hasta que se inicializa la interrupción.

En la siguiente figura se muestra un esquema práctico una interrupción por activación del terminal RB0/INT

Si INTE = 1 y se aplica un flanco activo en RB0/INT, el señalizador INTF se pone a 1 automáticamente y se solicita una interrupción, que es aceptada GIE = . Antes de regresar al programa principal hay que borrar INTF, puesto que en caso contrario al ejecutar la instrucción de retorno RETFIE se volvería a desarrollar el mismo proceso de interrupción. 

(http://img195.imageshack.us/img195/3941/activo.jpg)

Ejemplo

Con lo anterior estamos listo para programar una interrupción por RB0 / INT

el algoritmo es el siguiente

1:       configurar RB0 como entrada
2:       configuro el OPTION_REG
3:       habilito la interrupción global y la externa (RB0/INT)
4:       Cuando detecto la interrupcion (señalizador INTF) atiendo la interrupción
5:       Cuando termine de atender la interrupción limpio el bit INTF y eso es todo

El ejemplo de hoy es muy sencillo. vamos a conectar un señal cuadrada de baja frecuencia<= 0.5Hz y se la vamos a conectar al terminal RB0 de nuestro PIC, antes de nada tenemos que configurar a RB0 como entrada digital y después que vamos a prender y apagar un led (que esta conectado en RB1 ) secuencialmente, es decir, en el primer pulso prendemos el led en segundo flanco activo lo apagamos y asi sucesivamente.

Acá el código

Código: [Seleccionar]
;*******************************************
;* Carlos Alberto Henao Baena              *
;* Foro de microcontroladores TODOPIC      *
;* Manejo de interrupción extenta RB0/INT  *
;* 5 de agosto del 2009                    *
;* Pereira - Colombia                      *
;*******************************************


;******************************* Registro especiales *************************************

status equ 03h
porta equ 05h
portb equ 06h
portc equ 07h
portd equ 08h
porte equ 09h
intcon equ 0bh

option_reg equ 81h
trisa equ 85h
trisb equ 86h
trisc equ 87h
trisd equ 88h
trise equ 89h
adcon1 equ 9fh
;******************************* Registro de usuario *************************************

bandera equ 20h

;******************************* Empieza el programa *************************************

list p = 16f877
org 00
goto inicio

org 04
goto inter




;****************************** Rutina de manejo de interrupciones ***********************

inter btfsc intcon,1
goto flanco_activo1 ; si ha ocurrido la interrupción
; voy a una rutina de atención
retfie ; si no retorno

flanco_activo1 movf bandera,0 ; paso bandera a W
xorlw d'0'
btfss status,2 ; pregunto por la comparación
goto flanco_activo2
bsf portb,1 ; si es el primer flanco activo el led
incf bandera,1 ; incremento bandera para que en el
; otro flanco activo apague el led
bcf intcon,1 ; limpio el señalizador de interrupción
retfie ; salgo de la interrupción

flanco_activo2 movf bandera,0 ; paso bandera a W
xorlw d'1'
btfss status,2 ; pregunto por la comparación
retfie
bcf portb,1 ; si es el segundo flanco desactivo el led
clrf bandera ; limpio bandera para que en el
; otro flanco activo prenda el led
bcf intcon,1 ; limpio el señalizador de interrupción
retfie

;****************************** Rutina principal de programa MAIN() **********************

inicio bcf status,6
bsf status,5 ; banco 1
movlw b'11111111'
movwf trisa
movwf trisc
movwf trisd
movwf trise ; puerto a,c,d y e como entradas
; digitales
movlw b'11111101'
movwf trisb ; configuro tal que RB0 sea entrada
; digital
movlw b'01000000' ; configuro el flanco de interrupción
movwf option_reg ; ascendente
movlw b'00000110'
movwf adcon1 ; configuro el adcon1 para los puertos
; A y E sean entradas o salidas digitales
bcf status,5 ; banco 0
movlw b'10010000' ; habilito la interrupción global
; y la de RB0/INT y desactivo las
movwf intcon ; demás por timer0 etc
clrf portb
clrf porta
clrf portc
clrf portd
clrf porte
clrf bandera ; inicializo todas la variable y los
; puertos
nada goto nada ; bucle infinito para esperar la
end ; la interrupción

Hata una próxima entraga un abrazo.

Un saludo desde Pereira - Colombia
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 05 de Agosto de 2009, 23:04:14


Acá la simulación en proteus

Un saludo desde Pereira - Colombia
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 05 de Febrero de 2010, 20:34:26
Saludos

Después de 120 días sin seguir con el hilo por diferentes motivos entre ellos algo de descuido si se puede llamar así sigo con el tema, pido disculpas a la comunidad del foro por abandonar el hilo sin previo aviso, espero que no vuelva a ocurrir.

Hoy trago un ejemplo con el timer0 pero en modo contador (se me olvido hacerlo antes) que es una configuración muy importante y necesaria para los que apenas están aprediendo.

bueno acá algo de teoría:

Los PIC16X8X poseen un temporizador/contador de 8 bits, llamado TMR0, que actúa de dos maneras diferentes:

1.Como contador de sucesos, que están representados por los impulsos que se aplican al terminal RA4/TOCKI. Al llegar al valor 255 o 0xFF se desborda el contador y, con el siguiente impulso, pasa a 0x00, advirtiendo esta circunstancia un señalizador y provocando una interrupción.

2. En modo timer que ya conocemos muy bien.

Para que el TMR0 funcione como contador de impulsos aplicados al terminal TOCKI hay que poner el bit TOCS en 1, que es el que ocupa la posición 5 del registro OPTION_REG. En esta situación, el registro TMR0, que es el ubicado en la dirección 1 del banco 0 de la memoria de datos, se incrementa con cada flanco activo aplicado en el terminal TOCKI (depende del bit TOSE el OPTION_REG). El tipo de flanco activo se elige programando el bit TOSE, que es que ocupa la posición 4 del registro OPTION_REG. Si TOSE = 1, el flanco activo es el descendente, y si TOSE = 0, es el ascendente. Cuando se desea que TMR0 funcione como temporizador el bit TOCS = 0 .

En realidad, los PIC16X8X y los de la gama baja disponen de dos temporizadores, el TMR0 y el perro guardián (Watchdog). El primero actúa como principal y sobre él recae el control de tiempos y el contaje de impulsos. El otro vigila que el programa no se “cuelgue”, y para ellos cierto tiempo comprueba si el programa se está ejecutando normalmente. En caso contrario, si el control está detenido en un bucle infinito a la espera de algún acontecimiento que no se produce, el perro guardián “ladra”, lo que se traduce en un Reset que reinicialaza todo el sistema “Programa”.

Ejemplo: El ejemplo de hoy es un programa que cada vez que timer0 se desborda prende un led y cuando vuelve y se desborda apaga el led es un ejemplo básico pero concreto. acá el esquemático.

(http://img16.imageshack.us/img16/4286/contax.jpg)

Es importante aclarar algo, como el timer está configurado como contador los pulsos los recibe por RA4 por lo tanto el timer se incrementa por pulsos en RA4 y no cada ciclo del reloj, así si no hay pulso por RA4 el timer no se incrementa hay una fórmula empírica que es Volor_timer = 255 - (Numéro_de_pulsos / predivisor).


Buano acá el código

Código: [Seleccionar]

;***********************************************************************************
;* Carlos Alberto Henao                                                            *
;* Interrupción por desborde del timer0 con el pic 16f877 en modo contador TOCK1   *
;* Foro técnico de microcontroladores "todopic"                            *
;* 5 de febrero del 2010 Pereira - Colombia                                         *
;***********************************************************************************

;******************** Declaración de registros especiales *********************************

tmr0 equ 01h
status equ 03h
porta equ 05h
portb equ 06h
portc equ 07h
portd equ 08h
porte equ 09h
intcon equ 0bh

option_reg equ 81h
trisa equ 85h
trisb equ 86h
trisc equ 87h
trisd equ 88h
trise equ 89h
adcon1 equ 9fh

;********************* Registro de proposito especial ***************************************


contador equ 20h


list p = 16f877

;***************** va a empezar el programa ***********************************************

org 00
goto inicio

org 04
goto inter

;***************** Manejo de interrupción **************************************************

inter btfsc intcon,2
goto conta ; si hay desborde atiende la interrupción
retfie ; si no retorna

conta incf contador,1 ; incremento contador
movf contador,0 ; paso contador a w
xorlw d'1' ; comparo con 1
btfss status,2 ; pregunta por el resultado lógico
goto apaga ; so no es cero va a apaga
bsf portb,0 ; prende el primer led si el ciclo es impar
movlw d'250'
movwf tmr0 ; cargo otra vez el timer con 250
bcf intcon,2 ; limpio la bandera de interrupción
retfie ; salgo de la interrupción

apaga bcf portb,0 ; apago el led en el ciclo par
movlw d'250'
movwf tmr0 ; cargo otra vez el timer con 250
bcf intcon,2 ; limpio la bandera de interrupción
clrf contador ; vuelve e inicia el ciclo
retfie ; salgo de la interrupción

;************* Inicia el programa **********************************************************

inicio bcf status,6
bsf status,5 ; Banco 1 de memoria
movlw b'11111111'
movwf trisa
movwf trisc
movwf trisd
movwf trise ; configuro puertos A,C,D,E como entrada
movlw b'11111110'
movwf trisb ; configuro el puerto restante
movlw b'11110000'
movwf option_reg ; configuro el timer a un predivisor de frecuancia de 1:2
; y contador cada flanco ascendente por TOCK1
movlw b'00000110'
movwf adcon1 ; configuro puerto A Y E como entradas digitales
bcf status,5 ; me paso al banco 0
movlw b'10100000'
movwf intcon ; configuro el desborde por timer0
clrf contador
clrf porta
clrf portb
clrf portc
clrf portd
clrf porte ; limpio todos los puertos
movlw d'250'
movwf tmr0 ; cargo al timer0 con 250 para que se me desborde cada
; 12 pulsos de reloj

nada goto nada ; bucle infinito esperando desborde
end

Bueno espero que le haya gustado el ejemplo.
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 05 de Febrero de 2010, 20:37:20

Saludos

Acá la simulación en proteus 7.2 espero les guste.
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 06 de Febrero de 2010, 12:29:53
Saludos

El ejemplo de hoy es una mezcla de dos interrupciones que ya hemos manejado la primera interrupción por desborde del timer 0 y la segunda interrupcción por flanco en RB0, la idea de mezclar las dos intterpciones es medir el ancho de un pulso (Duty Cicle) y vamos a utilizar las interrupciones para medirlo.

Primero según la figura para medir un pulso primero tenemos que esperar un flanco ascendente en RB0, en ese momento nuestro timer debe empezar a contar, hasta cuando? la respuesta es sencilla hasta que detecte un flanco ascendente en RB0.

(http://img96.imageshack.us/img96/5083/dutycicle.jpg)

Bueno el algoritmo es el siguiente:

1. Configurar el términal RB0 como entrada
2. Configurar el prescaler y el timer con el registro OPTION_REG
3. Activar la interrupcióngloba y por RB0
4. Cuando halla flanco ascendente activar interrupción por desborde del timer0
5. Después cuando halla flanco descendente en RB0 pasar el valor del timer0 a una variable y el numéro de desbordes a otra
6. Limpiar todas las banderas y renicializar todos los registro de conteo (tmr0, desbordes) y desactivar la intterupción del timer0 hasta que ocurra otro flanco ascendente

Por ejemplo si queremos medir un ciclo útil de 0.5s, entonces nuestro timer lo máximo que puede contar con prescaler de 1:256 es 65,536ms lo que no quiere decir que no lo alcanza a medir por lo tanto tenemos que utilizar otro registro cuya función será que cada que halla un derborde se incremente el cálculo será el siguiente.

0.5/65.536ms = 7,629394531ms entonce de aquí no damos cuenta que para 0.5s el timer se desborda 7 veces

ahora por la 7 desbordadas cuenta = 7*65.356ms = 458.752ms por lo tanto si queremos medir 0.5s lo que resta por medir es:
0.5-0.458752 = 41.248ms.

Ahora según la fórmula del timer0  41.248ms= (4*Tosc)*(256 - Vtimer)*256

Con una frecuencia de 4MHz el valor del timer es (despejando Vtimer) = 160.869 aproximadamente 161

Entonces el valor del ciclo útil estara contenido en dos registro de 8 bit por lo tanto, con este ejemplo y según el ejemplo

Contador_l = 161
contador_h = 7

Acá el código y el esquemático para el ejemplo de hoy

(http://img693.imageshack.us/img693/5639/duty.jpg)

Código: [Seleccionar]
;*******************************************************************************
;* Carlos Alberto Henao Baena                                                  *
;* Foro de microcontroladores TODOPIC                                          *
;* Manejo de interrupción extenta RB0/INT más timero para medir el ciclo útil  *
;* 6 de febrero del 2010                                                       *
;* Pereira - Colombia                                                          *
;*******************************************************************************


;******************************* Registro especiales *************************************

tmr0 equ 01h
status equ 03h
porta equ 05h
portb equ 06h
portc equ 07h
portd equ 08h
porte equ 09h
intcon equ 0bh

option_reg equ 81h
trisa equ 85h
trisb equ 86h
trisc equ 87h
trisd equ 88h
trise equ 89h
adcon1 equ 9fh
;******************************* Registro de usuario *************************************

bandera equ 20h
contador_l equ 21h
contador_h equ 22h
desbordes equ 23h

;******************************* Empieza el programa *************************************

list p = 16f877
org 00
goto inicio

org 04
goto inter




;****************************** Rutina de manejo de interrupciones ***********************

inter btfsc intcon,1
goto externa_rb0 ; si ha ocurrido la interrupción
; voy a una rutina de atención
btfsc intcon,2 ; pregunto por interrupcción del timer0
goto timer0 ; voy a una rutina de atencción
retfie ; si no retorno

;**************************** Rutina de atención de la interrupción por flanco activo en RB0*******

externa_rb0 incf bandera,1 ; incremento la bandera para saber si
; el flanco es descendente o ascendente
movf bandera,0 ; paso a w bandera
xorlw d'1' ; comparo con 1
btfss status,2 ; pregunto por el bit z
goto flanco_ascendente ; si no voy a flanco ascendente
movlw b'10110000' ; habilito la interrupción global
; y la de RB0/INT y activo la del timer0
movwf intcon
bsf status,5 ; me paso al banco 1
movlw b'10000111'
movwf option_reg ; cuando pasa la interrupción configuro
; el flanco en descendente
bcf status,5 ; me paso al banco 0
bcf intcon,1 ; limpio la bandera
clrf tmr0 ; empiezo a contar
retfie ; salgo de la interrupción

flanco_ascendente

movlw b'10010000' ; habilito la interrupción global
; y la de RB0/INT y activo la del timer0
movwf intcon
movf tmr0,0 ; paso el contenido del timer0 a w
movwf contador_l ; paso el contenido del timer 0 a contador_l
movf desbordes,0 ; paso desbordes al w
movwf contador_h ; paso el valor de desbordes a la parte alta.
clrf bandera
bcf intcon,1 ; limpio la bandera de interrupción
bsf status,5 ; me paso al banco 1
movlw b'11000111'
movwf option_reg ; configuro ahora como flanco ascendente
bcf status,5 ; me paso al banco 0
clrf tmr0 ; limpio el timer por que ya término de
; medir el ciclo útil

clrf desbordes ; inicializo la variable para el otro pulso
retfie

;************************* Atención por desborde del timer0 *********************************

timer0 incf desbordes,1 ; Incremento el contador en 1
bcf intcon,2 ; limpio la bandera de interrupción
retfie ; salgo de la interrupción        

;****************************** Rutina principal de programa MAIN() **********************

inicio bcf status,6
bsf status,5 ; banco 1
movlw b'11111111'
movwf trisa
movwf trisc
movwf trisd
movwf trise ; puerto a,c,d y e como entradas
; digitales
movwf trisb ; configuro tal que RB0 sea entrada
; digital
movlw b'11000111' ; configuro el flanco de interrupción
movwf option_reg ; ascendente y prescaler 1:256
movlw b'00000110'
movwf adcon1 ; configuro el adcon1 para los puertos
; A y E sean entradas o salidas digitales
bcf status,5 ; banco 0
movlw b'10010000' ; habilito la interrupción global
; y la de RB0/INT y activo la del timer0
movwf intcon
clrf portb
clrf porta
clrf portc
clrf portd
clrf porte
clrf bandera ; inicializo todas la variable y los
; puertos
clrf tmr0
clrf contador_l
clrf contador_h ; limpio todas las variables
clrf desbordes
nada goto nada ; bucle infinito para esperar la
end ; la interrupción

Para concluir podemos decir que este método es mucho más preciso que el primer método pra medir el ciclo útil, más adelante examinaremos otras técnicas para medir el ciclo útil de un pulso, espero que le halla gustado el ejemplo de hoy.

Nos vemos.



 
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 06 de Febrero de 2010, 12:54:31

Saludos

Acá la simulación en proteus 7.2 espero que le guste.

Nota si alguién sabe cual es el código en html ([xxx] Texto [/xxxx]) para poner el código más organizado ya que mi computador está algo loco por estos días.
Título: Re: Manejo de interrupciones
Publicado por: migsantiago en 06 de Febrero de 2010, 13:26:58

Nota si alguién sabe cual es el código en html ([xxx] Texto [/xxxx]) para poner el código más organizado ya que mi computador está algo loco por estos días.

Hola, no es código HTML es BBC. Puedes usar este iconito en el editor del mensaje para encerrar el código fuente: (http://img683.imageshack.us/img683/3525/56117111.png)
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 06 de Febrero de 2010, 14:26:54
Hola Santiago

El problema es que el navegador no me deja utilizar los iconos del foro y no tengo ni idea porqué, haber si alguién sabe cual es el código en BBC para poner el código más organizado ya que no me acuerdo cual es.

Saludos.
Título: Re: Manejo de interrupciones
Publicado por: migsantiago en 06 de Febrero de 2010, 14:30:06
En ese caso úsalo así...

http://img51.imageshack.us/img51/4494/codei.png

(http://img51.imageshack.us/img51/4494/codei.png)
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 06 de Febrero de 2010, 14:44:09

Gracias Santiago, pero ya no me deja modificar el hilo no se porqué.
Título: Re: Manejo de interrupciones
Publicado por: migsantiago en 06 de Febrero de 2010, 16:23:12
Si no es una cosa es otra  :mrgreen:

No te preocupes que ya lo arreglé. Después de una hora ya no es posible modificar los temas.
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 06 de Febrero de 2010, 16:30:51

Gracias Santiago, ahora el hilo quedo un poco más organizado y más agradable para leer. Para la próxima hay que ser cuidadoso para no pasarce de la hora jeje. Saludos
Título: Re: Manejo de interrupciones
Publicado por: Suky en 06 de Febrero de 2010, 19:53:40
Felicitaciones Carlos por el aporte! Es complicado hacer tutoriales con la restricción de modificación, pero hay que protejer la información compartida, porque algunos despues de un tiempo la borran  :? Cualquier inconveniente notificalo al moderador.  ;-)


Saludos!
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 06 de Febrero de 2010, 21:55:07

Hola Suky gracias por las felecitaciones, y voy a seguir trabajando para que el hilo quede lo mejor que pueda.

Saludos.
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 13 de Febrero de 2010, 13:22:19
Saludos.

Bueno el ejemplo de hoy es el manejo de la interrupciòn por escritura de la EEPROM del PIC16F877, vamos a indicar cuales son los pasos para realizar nuestro programa pero primero una pequeña introducciòn de la memoria EEPROM del pic.

El PIC16F84 tiene solamente 64 posiciones de memoria EEPROM que se puede direcionar solamente con un registro que indica la posiciòn a guardar que va desde la 0 hasta la 63 en décimal con este micro teniamos dos registros el EEDATA y EEADR el primero registro es con el direcionamos el bloque donde queremos guardar el dato de 8 bits y el segundo se carga el valor que queremos guardar en memoria. Por otro lado, el PIC16F877 tiene un direcionamiento de 13 bits y se pueden guardar datos de 14 bits debido a que tiene 8 K de memoria, para poder lograr esto el micro contiene 2 registros màs que el pic16f84 para direcionar el lugar donde se desea guardar como el tamaño del dato a guardar en la EEPROM. Los registros son EEADRH, EEADR, EEDATAH, EEDATA.

Con los dos primero se seleciona el lugar donde se desea escribir pero a que tener en cuenta que el registro EEADRH contiene los 5 bits de mayor peso y el registro EEADR contiene lo 8 bits de menor peso. Para los demás registro pasa lo mismo sólo que en EEDATAH iran los 6 màs significativos y en EEDATA los menos significativos.

Para configurar la interrupción por fin de escritura hay que habilitar la interrupción global, la de perifericos y en el registro PIE2 habilitar el bit EEIF para habilitar la interrupciòn por fin de escritura de la EEPROM. los pasos para iniciar poder escribir son los siguientes.

1. Especificar la direción donde se desea guardar (EEADRH,EEADR)
2. Carga el registro que se desea guardar en el buffer (EEDATAH,EEDATA)
3. Habilitar el bit WREN del registro EECON1 en 1
4. Mandar la secuencia de escritura al registro EENCON2 (0x55 , 0xAA)
5. Inicio de escritura bir WR del registro EECON1
6. Esperar interrupción.

Hay que tener en cuenta que los registro antes mencionados se encuentran en el banco 2 y en el banco 3 de la memoria RAM asì que hay que tener cuidado con el direcionamiento de las páginas.

Como la escritura de la EEPROM dura unos cuantos ms que es una valor muy grande comparado con el ciclo de instrucción por eso es imporatante utilizar la interrupción por fin de escritura en la EEPROM para indicar si todo estuvo bieny cuando termino la escritura el señalizador de interrupciòn se encuentra en el registro PIR2.

Bueno el ejemplo de hoy consiste de programita sencillo tenemos dos pulsadores conectados al puerto B (b1,b2) un pulsador me incrmenta una variable y cuando presionamos el otro pulsador activamos el seceso para guardar en la EEPROM de nuestro pic.

Aca el esquemático

(http://img175.imageshack.us/img175/6953/eeprom.jpg)

En el simulador de proteus hay una ayuda para mirar como esta la EEPROM y que tiene guardada para utilizarla (al menos en mi simulador funciona así) tiene que esta corriendo el programa, además, hay que seleccionala por la ventana del simulador una vez activada hay que undir pause en el simulador y aparece el mapa de la memoria EEPROM del microcontrolador.

acá una imagén de la ayuda.

(http://img502.imageshack.us/img502/3029/eeprom2.jpg)

Bueno por último el código en el esamblador obviamente.

Código: [Seleccionar]
;**************************************************************
;* Carlos Alberto Henao                                       *
;* Foro de microcontroladores TODOPIC                         *
;* Manejo de interupción por fin de escritura de la EEPROM    *
;* Febrero 13 del 2010                                        *
;* Pereira - Colombia                                         *
;**************************************************************

;************************** Registro especiales *******************************************

status equ 03h
porta equ 05h
portb equ 06h
portc equ 07h
portd equ 08h
porte equ 09h
intcon equ 0bh
pir2 equ 0dh

trisa equ 85h
trisb equ 86h
trisc equ 87h
trisd equ 88h
trise equ 89h
pie2 equ 8dh

eedata equ 10ch
eeadr equ 10dh

eecon1 equ 18ch
eecon2 equ 18dh

;************************ Registros de usuario ********************************************

conta equ 20h
loop equ 21h
loop1 equ 22h
respuesta equ 23h

;*********************** Va a empezar el programa *****************************************

list p = 16f877

org 00
goto inicio

org 04
goto inter

;************************* Rutina de atención de interrupciones **************************

inter bcf status,6
bcf status,5 ; banco 0
btfsc pir2,4 ; pregunto si ya hubo interrupción por finalización
; de escritura en la EEPROM
goto guarda_s
bcf status,6
bcf status,5 ; me paso al banco 0
retfie ; de lo contrario retorno al programa

guarda_s
bcf pir2,4 ; limpio la bandera de intterupción
movlw b'1'
movwf respuesta ; cargo
retfie

;************************* Rutina de tiempo antirebote **************************

retar movlw d'180'
movwf loop
movlw d'200'
movwf loop1
decfsz loop1,1
goto $-1
decfsz loop,1
goto $-5
return

;*********************** Rurina de configuración para la escritura en la EEMPROM ***********

guardar bsf status,6 ; me paso al banco de memoria 2
bcf status,5
movlw d'0'
movwf eeadr ; especifico en parte de la memoria voy a guardar
; en este caso en posición 0x00H
bcf status,6
bcf status,5 ; banco 0
movf conta,0 ; paso conta a w ya que conta es el valor a guardar
bsf status,6 ; me paso al banco de memoria 2
bcf status,5
movwf eedata ; paso el dato a eedata
bsf status,5 ; me paso al banco 3
  bsf eecon1,2 ; permito la escritura en la eemprom

movlw 55h
movwf eecon2
movlw 0xAA
movwf eecon2 ; mando el comando de escritura

bsf eecon1,1 ; empieza la escritura
bcf status,6
bcf status,5
movf respuesta,1 ; paso el valor de escritura a w
xorlw d'1' ; lo comparo con 1
btfss status,2 ; pregunto por el resultado lógico de la operación
goto $-3 ; vuelvo y repito esperando interrupción
clrf respuesta ; limpio la bandera
return


;********************** Empieza el programa ************************************************

inicio bcf status,6
bsf status,5 ; banco 1
movlw b'11111111'
movwf trisa
movwf trisb
movwf trisd
movwf trise ; puertos A,B,D,E como entradas digitales
movlw b'11110000'
movwf trisc ; configuro el puerto C
movlw b'00010000'
movwf pie2
bcf status,5 ; banco 0
movlw b'11000000'
movwf intcon ; activo la interrupción por escritura de la EEPROM
clrf portb
clrf conta
clrf portc
clrf respuesta

vuelve btfss portb,1
goto pulsa_2
call retar
btfsc portb,1
goto pulsa_2
call retar
btfss portb,1 ; rutina que pregunta por los dos pulsadores
goto $-1
incf conta,1
goto vuelve

pulsa_2
btfss portb,2
goto vuelve
call retar
btfsc portb,2
goto vuelve
call retar
call guardar
goto vuelve
end


Bueno esto fue todo por hoy saludos y no vemos.

 
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 13 de Febrero de 2010, 13:28:23

Acá la simulación en el proteus 7.2 espero le guste saludos ;-).
Título: Re: Manejo de interrupciones
Publicado por: jhozate en 13 de Febrero de 2010, 14:01:33
excelente Chenao, un buen trabajo te has hecho con este hilo, animo :-/
Título: Re: Manejo de interrupciones
Publicado por: Chenao en 20 de Febrero de 2010, 12:48:01

Saludos

Antes de  entrar a manejar las interrupciones por perifèricos del PIC16F877 vamos a hablar de los registros que gobiernan la configuración de dichas interrupciones entonces empecemos por el registro más general el registro PIE1

Registro de interrupción PIE1

Este registro contiene los bits que activan o prohibe las interrupción provocadas por los periféricos internos del microcontrolador PIC16F87 y que no estaban en el registro INTCON, es importante aclarar que para que estas interrupciones puedan activarse, primero se debe poner en “1” lógico el bit GIE y segundo se debe poner en “1” lógico bit PEIE que están contenidos en el registro INTCON. Este registro se encuentra en el página o banco 1 de la memoria RAM y ocupa la dirección 0x8C. En la siguiente figura se muestra el registro PIE1 con todos sus bits y una descripción de cada bit.

(http://img96.imageshack.us/img96/9118/pie1m.jpg)

PSPIEF : Permiso de interrupción por la puerta paralela esclava al realizar una operación de escritura/ lectura.

ADIE: Permiso de interrupción al finalizar la conversión A/D

RCIE: Permiso de interrupción para receptor cuando el buffer del USART se llena.

TXIE:    Permiso de interrupción para el transmisor cuando el buffer del USART se llena

SSPIE: Permiso de interrupción para la módulo serie síncrono

CCP1IE: Permiso de interrupción para el módulo CCP1

TMR2IE: Permiso de interrupción por desborde del TMR2

TMR1IE: Permiso de interrupción por desborde del TMR1


Registro de interrupción PIE2

Este registro posee las demás causa que no están en el registro INTCON y en el registro PIE1, cabe señalar que si se quiere utilizar las interrupciones por periféricos contenidas en el registro PIE2 se debe activar la interrupción global (GIE) y la interrupción por periféricos PEIE que se encuentra en el registro INTCON. Esta ubicado en la página 1 de la memoria RAM y se ubica en la dirección 0x8D. A continuación se muestra el registro PIE2 con cada uno de sus bits y sus respectivos significados.


(http://img4.imageshack.us/img4/4386/pie2cc.jpg)

EEIE: Permiso de interrupción por fin de escritura de la EEPROM

BCLIE: Permiso de interrupción cuando se produce una colisión o error en el módulo SSP entre el maestro y el esclavo.

CCP2IE: Permiso de interrupción de módulo CCP2

Registro señalizadores de interrupción de periféricos (PIR1 y PIR2)

Estos registro contienen los bits señalizadores de interrupción por periféricos, indican cuando ha ocurrido una interrupción por periféricos independientemente si la interrupción ocurrida este habilitada o deshabilitada. Dichos registro se encuentran en la página 0 y sus direcciones son 0x0C y 0x0D respectivamente. En la siguiente figura se muestran cada uno de los de los registro con sus respectivos bits, se puede observar como los nombre de los bits de los registro PIR1 y PIR2 son similares a los de los registros PIE1 y PIE2 respectivamente, lo cual concluye que el bit TMR1F indica desborde del TMR1, que el bit ADIF indica fin de conversión A/D y así sucesivamente.

(http://img246.imageshack.us/img246/2415/pir1.jpg)

(http://img246.imageshack.us/img246/2415/pir1.jpg)

Bueno esto es todo por hoy ya para la próxima entrega vamos a programar una interrupción por periféricos espero haber sido claro con la teoria de la interrupciones por periféricos. Saludos ;-).