Autor Tema: Manejo de interrupciones  (Leído 14490 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Manejo de interrupciones
« 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
Un saludo desde Pereira - Colombia

Desconectado groundman

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1870
    • www.ingeniopic.com
Re: Manejo de interrupciones
« Respuesta #1 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
Montando mi primera impresora 3D (Raprep Prusa i3)

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #2 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
Un saludo desde Pereira - Colombia

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #3 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
Un saludo desde Pereira - Colombia

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #4 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
« Última modificación: 27 de Julio de 2009, 21:41:06 por Chenao »
Un saludo desde Pereira - Colombia

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #5 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
Un saludo desde Pereira - Colombia

Desconectado Chrisbaf

  • PIC16
  • ***
  • Mensajes: 178
Re: Manejo de interrupciones
« Respuesta #6 en: 19 de Julio de 2009, 19:57:58 »
Gracias Chenao por compartir tus conocimientos y tu tiempo con el foro ..sigue adelante  :)

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #7 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
Un saludo desde Pereira - Colombia

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #8 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.




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 
« Última modificación: 25 de Julio de 2009, 18:16:46 por Chenao »
Un saludo desde Pereira - Colombia

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #9 en: 25 de Julio de 2009, 18:19:22 »
Saludos

Acá la simulación en Proteus que la disfruten

Un saludo desde Pereira - Colombia
Un saludo desde Pereira - Colombia

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #10 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. 



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
Un saludo desde Pereira - Colombia

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #11 en: 05 de Agosto de 2009, 23:04:14 »


Acá la simulación en proteus

Un saludo desde Pereira - Colombia
Un saludo desde Pereira - Colombia

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #12 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.



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.
Un saludo desde Pereira - Colombia

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #13 en: 05 de Febrero de 2010, 20:37:20 »

Saludos

Acá la simulación en proteus 7.2 espero les guste.
Un saludo desde Pereira - Colombia

Desconectado Chenao

  • PIC18
  • ****
  • Mensajes: 414
Re: Manejo de interrupciones
« Respuesta #14 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.



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



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.



 
« Última modificación: 06 de Febrero de 2010, 16:21:38 por un Moderador, Razón: Se agrega code. »
Un saludo desde Pereira - Colombia