¿Cómo usar el Timer 0 correctamente?

No importa los milisegundos de espera o los microsegundos de ejecución de las instrucciones. Eso es muy poco comparado con los mil misegundos que van a pasar hasta la próxima interrupción.

Si la rutina de interrupción ocupa un par de milisegundos, aún quedan 998 milisegundos para el programa principal, antes de que se repita la interrupción.

Bien entonces tienes razón, me queda claro entonces, que los microsegundos de checkeo SI afectan al tiempo del TMR0.
En interrupciones cortas no se llega a notar, pero si quiero hacer tiempos de horas ahi si voy a tener problemas.

Una solución que pensé, ya a extremo, es usar la rutina que hice de 1 minuto ininterrumpida, y la corto con un pulsador al Reset del micro ( No es lo ideal pero creo que no queda otra ).
Es lo mas cerca a lo que quiero y no afectaría el tiempo de trabajo.

Lógico, seria lo mismo que usar una rutina de 1 minuto ( sin usar el TMR0 ) y usar el mismo metodo del reset.

Acabo de hacer un ejemplo basico normal sin TMR0:
Código:
cblock  0x0c
milisegundo
segundo
minuto
endc

boton
         call  10ms
         incrementar  milisegundo
         milisegundo = 100  ?           10ms x 100 = 1 seg
         NO, ir a boton
         SI, resetear milisegundo
        
         incrementar  segundo
         segundo = 60 ?                 1 seg x 60 = 1 minuto
         NO, ir a boton
         SI, resetear segundo
         
         incrementar  minuto
         mostrarlo en PORTB ..... etc ....
         ir a boton 

;============ RUTINA 10MS ==============
10MS
        BLA BLA BLA
        BLA BLA BLA
        .................
        RETURN
En ese ejemplo sin usar el TMR0, he logrado algo bastante aceptable.
Muestro minutos usando un ( display 7 seg ) probe de 0 a 9 minutos.

En 9 minutos conseguí un retraso de 1 segundo.
Probé alterar el 60 por 59 de MINUTO y se alteraba mucho, tambien el 100 de milisegundos por 99, y lo mismo ...
pero a la rutina de 10ms le quité un NOP que tenía y ahora me quedó justo.

No se como un microsegundo cada 10ms en 1 minuto me corrigió el retraso ...
No hice calculos y nada,solo probé.

Pero al final aprendí mucho sobre el TMR0, o por lo menos lo basico de como se usa, pero no lo veo muy probechoso en el fin que quiero hacer de un simple contador de ( segundos, minutos y horas seleccionables de 0 a 9 ).

Mi pensamiento era como dije antes, lo pongo a contar una rutina ininterrumpida de EJ: 1 hora y mientras checkeo el pulsador de parada sin alterar los tiempos de rutina del TMR0.

Pero veo que como tu me explicas, el programa se detiene para la rutina del TMR0,
quedando invalida para este fin que pretendo hacer.

Lo que si se puede como tu me has dicho es tomar fracciones cortas ( maximas 1 segundo ), y de ahi ir decrementando o incrementando n veces con registros auxiliares.

Con lo que, no difiere mucho en hacerlo con la rutina normal sin la necesidad del TMR0.

Yo lo entiendo y veo necesario por ejemplo, para hacer un reloj continuo como tu has mensionado, y por ejemplo, al cambiar la hora no afectaría su tiempo constante ..

O por ejemplo, ( un temporizador en LCD con menu, algo que hice con rutina común ) para manejar el menu se cortaba o alteraba el tiempo ejecutandose.
Con el TMR0 esto no pasaría ....

Pero bueno ....
Es un arma muy potente segun la necesidad de pequeña precisión que uno necesite !!!

Muchas gracias nuevamente Joaquin por estar presente !!!
 
Bien entonces tienes razón, me queda claro entonces, que los microsegundos de checkeo SI afectan al tiempo del TMR0.
No, no es así.

Tú configuras el TIMER0 para que ocurra una interrupción cada 10 ms. Dentro de ella tienes una variable Contador que va de 0 a 100 o de 100 a 0, de tal manera que al final de la cuenta sabes que ya ha pasado un segundo. Entonces, dentro de la misma rutina, incrementas una variable llamada segundero.

Y te olvidas del TIMER0.

A partir de ese momento, para ti, y para tu programa, tienes una variable llamada segundero que sabes que se incrementa una vez cada segundo. A partir de ahí puedes crear los múltiplos de tiempo que quieras (un minuto, una hora). Incluso puedes ponerlo a cero cuando quieras.

TIMER0 ya no influye en tu programa. Sigue interrumpiendo cada 10 ms, pero su única misión es de hacer de segundero. Es cierto que puedes pararlo, pero... ¿para qué? Si quieres recomenzar la cuenta, pones a cero el segundero. Incluso puedes al mismo tiempo inicializar TMR0 a ~39 por si necesitas un comienzo preciso. Pero no necesitas realmente iniciar/parar la interrupción del TIMER0.

Si quieres contar un minuto, en el bucle principal te vale con mirar si el segundero vale más de 60.

Es más: si el programa requiere un verdadero reloj o contador sexadecimal, puedes hacer que la interrupción se encargue de actualizar no solo la variable segundero, sino también otra llamada minutero. Incluso una tercera llamada horaria. Todo este procesamiento lleva muy pocos microsegundos, así que la interrupción ocupa muy poco, y de esa manera ya le estás dando un reloj completo al resto del programa.

Edito: sí que puede haber un caso en que nos interese parar el reloj, y sería cuando queremos hacer una parada momentánea del reloj.
 
Última edición por un moderador:
Si, gracias Joaquin, ya me doy cuenta que era una burrada lo que quería hacer de poner una rutina de 1 minuto o mas !!!, dentro de los 10ms o del segundo del contador x 100 ... si, veo que la solución es como dices, crear variables de 60 o lo que sea necesario.

Ahora razonando veo porque con la rutina de 1 minuto no me respondían los pulsadores, ( cada 10ms, yo llamaba la rutina de 1 minuto ) quedando trabado dentro de la rutina esperando terminarla y desbordar.
Yo ignorantemente pensando que el TMR0 trabajaba en segundo plano, como un segundo nucleo, o micro paralelo.

Pero bueno muchas gracias por toda la info necesaria, he sacado mucho provecho !!! y de los errores se aprende !!!
 
Hola. Ando con un problemilla en un PIC18F4550, que no logro corregir.
Estoy trabajando con la interrupción por TMR0, así como con comunicación serial con un modulo Bluetooth.
Todo andaba bien con mi comunicación serial, pero al momento de agregarle la interrupción por TMR0, no anda como debería, ya que mi intención es que la comunicación serial funcione al recibir caracteres y cada uno efectúa diferentes tareas, mientras tanto, el timer debe ejecutarse cada 100 mS aproximadamente para mandar un texto por serial.

Con mi código actual, la comunicación se realiza, pero la interrupción no ocurre hasta que no se recibe algún valor por HSERIN.

En resumen, el PIC espera valores con HSERIN, mientras debería interrumpirse cada cierto tiempo y mandar texto por serial.
¿Qué ocurre realmente? El PIC espera un valor y al recibirlo ejecuta las tareas y luego ejecuta la interrupción.
Parece mas una interrupción por cambio de estado, que por timer.

Realmente el tiempo de interrupción no me preocupa, ya que con las fórmulas se puede modificar.
Al momento, sólo quisiera que se ejecutara la interrupción correctamente.

Anexo el código. Gracias por su tiempo.
PHP:
'----------------------FUSIBLES------------------------------
DEFINE OSC 48   'FRECUENCIA PARA LOS CALCULOS DE RETARDO          
DEFINE HSER_RCSTA 90h   
DEFINE HSER_TXSTA 20h   'ASINCRONO, PARA OSC 4 ES HIGH SPEED 24H, PARA OSC 48 ES LOW SPEED 20H 
DEFINE HSER_BAUD 9600 
          
'----------------------REGISTROS----------------------------------
ADCON1 = %00001111  'TODOS LOS PINES DIGITALES
BAUDCON.3 = 0 '8 BITS BAUD RATE GENERATOR
RCON.7 = 1      'SISTEMA DE PRIORIDAD EN LAS INTERRUPCIONES 1 ACTIVADO 0 DESACTIVADO
CMCON = 7
INTCON = %10100000      'CONFIGURANDO LA INTERRUPCION TMR0, BIT 2 FLAG TMR0
INTCON2.7 = 1   'DESACTIVAR LAS RESISTENCIAS EN PULL-UP DEL PUERTO B
INTCON2.1 = 1   'PRIORIDAD DE LA INTERRUPCION POR TMR0
T0CON = %10000111   'TIMER ACTIVO 16 BITS TEMPORIZADOR PRESCALER DE 1:256
TMR0H = $ED '%11101101  'VALORES DE INICIO DE CONTEO TIMER 16BITS
TMR0L = $A9 '%10101001
TRISC = %10000000 'PIN7 RECEPCION PIN 6 TRANSMISION
TRISD = 0 'PUERTO D PARA LOS MOTORES

'----------------------VARIABLES----------------------------------------
CR CON 13  'VALOR ASCII DEL 13 DECIMAL ES CR "RETORNO DE CARRO"
TIEMPO VAR BYTE
DISTANCIA VAR BYTE
CHAR VAR BYTE 

'-----------------------ALIAS--------------------------------------------

CLEAR 'BORRA TODAS LAS VARIABLES

PORTC = 0
PORTD = 0

ON INTERRUPT GOTO SENSOR 'LECTURA CADA 100mS

HSEROUT ["   INICIE",CR]
INICIO:

HSERIN [Char] 'ESPERA DE UN VALOR

SELECT CASE  CHAR
    CASE "A"
        PORTD = %10011001
        
    CASE "B"
        PORTD = %01100110
        
    CASE "C"
        PORTD = %10010110    
        
    CASE "D"
        PORTD = %01101001
        
    CASE "E"
        PORTD = 0
                                        
END SELECT

GOTO INICIO

DISABLE
SENSOR:

    HSEROUT [" ME INTERRUMPI", CR]

TMR0H = $ED '%11101101
TMR0L = $A9 '%10101001
INTCON = %10100000      'BORRANDO BANDERA DE INTERRUPCION

RESUME
ENABLE
END
 
Olvidaste configurar el registro T0CON y los valores para que desborde cada 100 mS @ 48 MHz, no son correctos.

Configuración básica para interrupción por desborde del Timer 0. (100 mS. @ 48 MHz.)
PHP:
; Microcontrolador: 18F4550
; Compilador: PB3X (PICBasic Pro X versión 3.0.8.1
; IDE: MicroCode Studio versión 5.0.0.5

#Config
    Config FOSC = XTPLL_XT, PLLDIV = 1, USBDIV = 2, CPUDIV = OSC1_PLL2
    Config PWRT = ON, WDT = OFF, PBADEN = OFF, LVP = OFF, BOR = ON
    Config FCMEN = OFF, IESO = ON, ICPRT = ON, VREGEN = OFF
#EndConfig

Inicio:
    INTCON.7 = 1            ; Habilitar interrupciones globales.
    INTCON.6 = 1            ; Habilitar interrupción de periféricos.
    INTCON.5 = 1            ; Habilitar interrupción por desborde del Timer 0.
    INTCON2.2 = 1           ; Alta prioridad por desborde del Timer 0.
    
    ; Configuración del Timer 0 para que desborde cada 100 mS @ 48 MHz.
    T0CON = %10000100
    TMR0H = $6D
    TMR0L = $84
    
    On Interrupt GoTo Servicio_Interrupciones
    
Programa:
    While 1 = 1
    ; Código principal.
    Wend
    
Servicio_Interrupciones:
    Disable
    
    If INTCON.2 = 1 Then    ; ¿Es interrupción por desborde del Timer 0?
        INTCON.2 = 0        ; Sí, limpiar Flag TMR0IF
        ; Código
        ; Recargar el Timer 0.
        TMR0H = $6D
        TMR0L = $84
    EndIf
    
    Resume
    Enable
    
    End
 
Última edición:
Muchas gracias por tu apoyo, te comento, estoy de acuerdo con los cálculos para los 100mS, estaba aplicando mal las formulas pero insisto el tiempo por ahora no es de alta prioridad pues primero quiero q funcione, el T0CON si lo tenia configurado pero tenia el PLL en 1:256 veo que tu lo configuras de 1:32 pero eso solo afecta a los cálculos de retardo de la interrupción, otra diferencia que veo es que, asumo que trabajas con RCON.7 = 0 (IPEN) por eso tenemos diferencias en el INTCON, de cualquier manera corregí mi código con tus observaciones y sigue sin funcionar como debería.

El problema estoy seguro que es con la instrucción HSERIN al inicio de mi programa ya que mientras no se reciba ningún valor el programa se queda en espera......en proteus tengo conectado motores que funcionan con caracteres recibidos, mientras no se reciba ningún caracter la interrupción no entra, lo que deseo es que se interrumpa el PIC con el TMR independientemente de que se reciba o no un caracter,

adicional a esto comento que si borro mi instruccion de HSERIN, la interrupcion por TMR se ejecuta sin problema me manda el mensaje al monitor serial "ME INTERRUMPI" cada 100ms.

muchas gracias por tu paciencia
 
Última edición:
El código que adjunto contiene el programa que quieres ejecutar.
Está funcionando perfectamente físicamente.

Se ejecuta la interrupción por desborde del Timer 0 y también realiza la comunicación bilateral RS-232.
Incluyo la simulación que usé, antes de probar el circuito en físico.
 

Adjuntos

  • 18F4550 HSerIn-Out & Timer0 Int.rar
    49.7 KB · Visitas: 9
Atrás
Arriba