Autor Tema: Duda con temporizador  (Leído 1726 veces)

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

Desconectado Azteka

  • PIC10
  • *
  • Mensajes: 24
Duda con temporizador
« en: 27 de Marzo de 2023, 03:27:32 »
Hola a todos. Requiero de su sabiduría, por favor. Estoy tratando de entender cómo funcionan los temporizadores. Trabajo con el TMP0 del PIC16F628A en PicBasic Pro v3.0.8. Uso un oscilador de 4MHz, he configurado el preescalador en 64 y cargado en el TMR0 el valor de 100, lo cual, según investigué, me debe dar una interrupción cada 10 mS. He puesto un condicional "IF" para que al acumular 100 interrupciones se cree un evento cada 1 segundo, (10 mS X 100 = 1000 mS). Un led de color rojo me indicaría que se ha cumplido 1 segundo. Otro led, verde, trabaja continuamente y sólo me sirve para ver "físicamente" cómo el PIC interrumpe lo que está haciendo para "atender la interrupción" encendiendo el led rojo por 200 mS. No he logrado que funcioné encendiendo el led rojo cada 1 segundo, en su lugar tarda 100, (no sé qué unidades, no sé si esté tardando 100 segundos) en hacerlo. En un LCD visualizo un Contador de interrupciones. Por favor alguien que me indique en qué estoy equivocado. De antemano mil gracias.
Código: QBasic/QuickBASIC
  1. '****************************************************************
  2. '*  Name    : INT_TMR0.BAS                                      *
  3. '*  Author  :                                                   *
  4. '*  Notice  : Copyright (c) 2023 [select VIEW...EDITOR OPTIONS] *
  5. '*          : All Rights Reserved                               *
  6. '*  Date    : 26/03/2023                                        *
  7. '*  Version : 1.0                                               *
  8. '*  Notes   :                                                   *
  9. '*          :                                                   *
  10. '****************************************************************
  11.  
  12. DEFINE OSC 4
  13.  
  14. CONTADOR VAR BYTE                           ;CONTADOR DE INTERRUPCIONES DE 32,256 uS CADA UNA
  15. CONTADOR = 0                                ;INICIALIZA CONTADOR DE INTERRUPCIONES
  16.  
  17. CONT_PAUSAS VAR BYTE                        ;CONTADOR DE PAUSAS DE LA RUTINA "PROG"
  18.  
  19. LED_VERDE VAR PORTB.1                       ;LED DE LA RUTINA "PROG" PARA VERIFICAR VISUALMENTE
  20.                                             ;LA INTERRUPCIÓN DE DICHA RUTINA CUANDO SE CUMPLE
  21.                                             ;UN SEGUNDO
  22.  
  23. LED_ROJO VAR PORTB.2                        ;EL LED ENCIENDE CADA QUE SE CUMPLE UN SEGUNDO
  24.  
  25. TRISB = 0
  26.  
  27. ON interrupt GOTO RAI                       ;RUTINA DE ATENCIÓN A LA INTERRUPCIÓN
  28.  
  29. OPTION_REG = %01010110                      ;CONFIGURA PREEESCALADOR TMR0 EN 1:128
  30.  
  31. TMR0 = 4                                    ;INICIALIZA TMR0
  32.  
  33. INTCON= %10100000                           ;HABILITA INTERRUPCIÓN TMR0
  34.  
  35. PROG:                                       ;RUTINA PARA DETECTAR VISUALMENTE LA INTERRUPCIÓN
  36.   HIGH LED_verde
  37.   GOSUB pausA
  38.   low LED_verde
  39.   GOSUB pausA
  40.   GOTO prog
  41.  
  42. PAUSA:
  43.   FOR CONT_PAUSAS = 1 TO 20                 ;GENERA PAUSAS DE 10 mS PARA NO PERDER INTERRUPCIONES
  44.       PAUSE 10
  45.       NEXT CONT_PAUSAS
  46.   RETURN
  47.      
  48. DISABLE                                     ;DESHABILITAR INTERRUPCIONES
  49. RAI:
  50.   TMR0 = 4
  51.  
  52.   CONTADOR = CONTADOR + 1
  53.  
  54.   IF CONTADOR = 31 THEN                     ;31 INTERRUPCIONES DE 32,256 mS CADA UNA = 999,936 uS  
  55.      GOSUB UN_SEGUNDO
  56.   ENDIF
  57.  
  58.   INTCON.2 = 0                              ;RESETEA LA BANDERA DE INTERRUPCIÓN DEL TMR0
  59.   RESUME                                    ;REGRESA AL PROGRAMA PRINCIPAL
  60.   ENABLE                                    ;HABILITAR INTERRUPCIONES  
  61.  
  62. UN_SEGUNDO:
  63.   HIGH LED_ROJO
  64.   PAUSE 200
  65.   LOW LED_ROJO
  66.   CONTADOR = 0
  67.   RETURN
  68.  
« Última modificación: 03 de Abril de 2023, 01:49:16 por Azteka »

Desconectado DominusDRR

  • PIC24H
  • ******
  • Mensajes: 1937
    • Sicoy
Re:Duda con temporizador
« Respuesta #1 en: 27 de Marzo de 2023, 11:23:56 »
Parece que estás correcto en lo de generar 1 segundo.

Con el temporizador 0 o TMR0, para conseguir lo que deseas, los pulsos contados (que son los pulsos del oscilador) se incrementen al ritmo que deseas, deben seguir la ruta mostrada por la flecha roja:



El primer bit que se debe verificar/activar a 0 lógico es T0CS (caso contrario estarías usando como fuente de pulsos el pin externo TOCKI)

El segundo bit que se debe verificar o activar a cero lógico es el PSA. (Caso contrario la fuente de pulsos es el perro guardián.



Hasta ese punto parece estar correcto:

Código: QBasic/QuickBASIC
  1. OPTION_REG = %00000101
´

Si la frecuencia del oscilador es 4Mhz, antes del escalamiento, sería 4MHz/4 = 1MHz, luego del escalamiento (que es de 64), la frecuencia sería  de 15.625 kHz, o en periodo 64 us.

Al ser un temporizador de 8 bits, la interrupción se produciría según como calibras tu temporizador cada (256 - 100) pulsos = 64 us x (256 - 100) = 9.984 ms., y con la variable que usas, el tiempo creado sería de 0.998 segundos.

Lo que no entiendo y no me parece correcto es la deshabilitación de las interrupciones. Deshabilitar la interrupción es poner en pausa al temporizador, lo que introduciría jitter e incrementa el error que ya por defecto tiene (no es 1 segundo exacto)

Algo que no comprendo es cuándo se ejecuta la línea 38.
Código: QBasic/QuickBASIC
  1. DISABLE                                     ;DESHABILITAR INTERRUPCIONES

Por que al producirse la interrupción, la línea que se ejecuta es la 39, y según yo, la línea 38 nunca sucedería.

Esta línea de acá:

Código: QBasic/QuickBASIC
  1. INTCON.2 = 0                              ;REHABILITAR INTERRUPCIÓN TMR0

No habilita la interrupción, esa línea limpia la bandera de interrupción y siempre debe ser ejecutada antes de retornar al flujo normal del CPU.

También la línea 52, me parece que nunca se ejecuta:

Código: QBasic/QuickBASIC
  1. ENABLE                                    ;HABILITAR INTERRUPCIONES

ya que antes se ejecuta RESUMe y regresas al flujo normal del CPU.

Algo que podría meter latencia al sistema tal vez es la función que maneja el display (LCDOUT) ya que tu interrupción se ejecuta aproximadamente cada 1ms, y esas rutinas tras bambalinas hacen muchos procesos, no creo que es buena idea que la coloques ahí.

Una interrupción debe ser lo más simple y corta en sus procesos.





« Última modificación: 27 de Marzo de 2023, 13:51:47 por DominusDRR »
Tengo una idea algo difusa sobre MPLAB Harmony, XC32 con PIC32

Desconectado Eduardo2

  • PIC24F
  • *****
  • Mensajes: 965
Re:Duda con temporizador
« Respuesta #2 en: 27 de Marzo de 2023, 16:01:26 »
Errores hay unos cuantos:

El principal
Código: [Seleccionar]
    PROG:                                       ;RUTINA PARA DETECTAR VISUALMENTE LA INTERRUPCIÓN
      HIGH L_verde
      pause 500
      low l_verde
      PAUSE 500
      GOTO prog
En PBP,  si ocurre una interrupción durante pause n  no la atiende hasta que termina.  La "solución" propuesta por sus autores es descomponerla en intervalos mas cortos, por ejemplo:
Código: [Seleccionar]
for k=1 to 1000 : pauseus 500 : NEXT k se pierde exactitud pero por lo menos bloquea durante menos tiempo.

Citar
      LCDOUT $FE, $C4, "CONTEO: ", DEC CONTador ;VISUALIZAR EL CONTEO DE INTERRUPCIONES EN UN LCD
LCDOUT  no es rápido, demora mas de 20ms la primer vez y casi 3ms después.  Usarla dentro de una interrupción que ocurre cada 10ms es mala idea. Más teniendo en cuenta que refrescar un display mas de 5 veces por segundo no tiene sentido porque no se alcanza a distinguir el último dígito (si está cambiando obviamente)

Código: [Seleccionar]
      IF CONTADOR = 100 THEN                    ;SI SUMAN 100 INTERRUPCIONES DE 10 mS (1 SEGUNDO)
         GOTO UN_SEGUNDO
      ENDIF
     
      INTCON.2 = 0                              ;REHABILITAR INTERRUPCIÓN TMR0
      RESUME                                    ;REGRESA AL PROGRAMA PRINCIPAL
      ENABLE                                    ;HABILITAR INTERRUPCIONES   
     
   UN_SEGUNDO:
         HIGH L_ROJO
         PAUSE 200
         LOW L_ROJO
         CONTADOR = 0
         RETURN

Si hacés un GOTO a UN_SEGUNDO no podés terminar con return porque se trata de una interrupción.  En todo caso usá GOSUB

Menos que menos usar un PAUSE 200 dentro de una interrupción de período 10ms --> Hacelo parpadear:
Código: [Seleccionar]
UN_SEGUNDO:
     if L_ROJO = 0 THEN
        HIGH L_ROJO
     ELSE
        LOW L_ROJO
     ENDIF
     CONTADOR = 0
     RETURN

Desconectado Azteka

  • PIC10
  • *
  • Mensajes: 24
Re:Duda con temporizador
« Respuesta #3 en: 28 de Marzo de 2023, 02:28:25 »
DominusDRR, Eduardo2, mil gracias a ambos por responder. Voy a leer con mucho cuidado lo que cada uno señala y a ponerlo en práctica. Más adelante reportaré mis avances. Saludos.

Desconectado dogflu66

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 3510
Re:Duda con temporizador
« Respuesta #4 en: 29 de Marzo de 2023, 06:02:51 »
Hola dejo la función MILLIS, lo mismo la puedes adaptar a tu lenguaje.
En este caso usa el timer1 y cuenta cada 1mSeg.

Código: Visual Basic
  1. '***********************************************************************
  2. 'MILLIS function, long type geneal counter.
  3. 'For replacing absolute pauses generated with WaitMs.
  4. 'The _MILLIS function increments every mSec. so it takes
  5. 'approximately 49.7 days To reinitialize.
  6. 'That is, it will pass through a zero value.
  7. '************************************************************************
  8. 'Pic16F88, OshonSoft Pic Basic Compiler v.8.42
  9. 'By COS, 03/2023
  10. '************************************************************************
  11. #define CONFIG = 0x2f50  'Fuse definition.
  12. #define CONFIG2 = 0x3ffc
  13. Define CLOCK_FREQUENCY = 8  'Clock a 8Mhz
  14. 'Define SIMULATION_WAITMS_VALUE = 1'Waitms is used activate for simulation.
  15. Include "_FuncionesPic16F88.bas"
  16. '************************************************************************
  17. main:
  18.         'Pin configuration
  19.         AllDigital
  20.         ConfigPin PORTA = Output
  21.         ConfigPin PORTB = Output
  22.         'Clock internal 8Mhz.
  23.         Call _setup_oscillator_mode_select_bit(_oscillator_mode_defined_by_fosc)
  24.         Call _setup_oscillator(_osc_8mhz)
  25.         'Initialize Timer1 desborde 1mSec.
  26.         Call _setup_timer1(_tmr1_internal, _tmr1_div1)
  27.         Call _set_timer1(0xf831)  'Interruption every 1mSec.
  28.         Call _timer1(_on)
  29.         Call _enable_interrupts(_int_timer1)
  30.         'Call _enable_interrupts(_global)
  31.         'Assign names to the Leds
  32.         Symbol LedGreen = RB0
  33.         Symbol LedYellow = RA7
  34. '********************************************************************
  35.         Call _SetUp_MILLIS()  'Setup _MILLIS functions, before activate interrupts
  36.         Call _enable_interrupts(_global)
  37.         Dim PreMillis As Long
  38.         Dim PreMillis1 As Long
  39.  
  40.         'Loop
  41.         While True
  42.  
  43.                 Call _MILLIS()  'Repeat As few times As possible, so As Not To interfere with interruptions.
  44.  
  45.                 If (_MILLIS - PreMillis) >= 200 Then  'Wait time in mSec.
  46.                         PreMillis = _MILLIS  'The last value of _MILLIS is stored.
  47.                         Toggle LedYellow  'Invers the state of the led.
  48.                 Endif
  49.  
  50.  
  51.                 If (_MILLIS - PreMillis1) >= 400 Then  'Wait time in mSec.
  52.                         PreMillis1 = _MILLIS  'The last value of _MILLIS is stored.
  53.                         Toggle LedGreen  'Invers the state of the led.
  54.                 Endif
  55.  
  56.         Wend
  57. End                                              
  58. '********************************************************************
  59. 'Function _MILLIS
  60. 'Requires the use of a timer and interrupts.
  61. '**************************************************
  62. 'Call _SetUp_MILLIS()  'Before triggering interrupts
  63. 'Call _MILLIS()  'Update _MILLIS counter
  64. 'Call _CounterMillis()  'Incrementa contador Millis
  65. '**************************************************
  66. 'Inicialize functions
  67. Proc _SetUp_MILLIS()
  68.         _MILLIS = 0
  69.         _CounterMillis = 0
  70. End Proc                                          
  71. 'Return _MILLIS as Long and global variable
  72. Function _MILLIS() As Long
  73.         Disable  'Stop interruption
  74.         _MILLIS = _CounterMillis
  75.         Enable  'Enable interruption
  76. End Function                                      
  77. 'Increment the counter Millis
  78. 'Return _CounterMillis as Long and global variable
  79. Function _CounterMillis() As Long
  80.         _CounterMillis++  'Increment counter millis
  81. End Function                                      
  82. '********************************************************************
  83. 'Interrupt vector
  84. On Interrupt  'Disable interruptions
  85. Save System
  86.         If _tmr1if Then
  87.                 Call _set_timer1(0xf831)  'Reload TMR1 registers to count 1mSec and clear _tmr1if.
  88.                 Call _CounterMillis()  'Increments _CounterMILLIS every mSec.
  89.         Endif
  90. Resume  'Enable interruptions
« Última modificación: 29 de Marzo de 2023, 07:39:25 por dogflu66 »
Saludos desde Granada, España.

Desconectado scrwld

  • PIC12
  • **
  • Mensajes: 56
Re:Duda con temporizador
« Respuesta #5 en: 31 de Marzo de 2023, 03:55:35 »
hola azteka,  aquí tienes un ejemplo tomado del foro de melabs.  espero que te sirva.
sin interrupciones.
*******************************************************************
DEFINE LOADER_USED 1
   DEFINE OSC 4
   
   ' Define LAB-X1 LCD registers and bits
   DEFINE  LCD_DREG        PORTD
   DEFINE  LCD_DBIT        4
   DEFINE  LCD_RSREG       PORTE
   DEFINE  LCD_RSBIT       0
   DEFINE  LCD_EREG        PORTE
   DEFINE  LCD_EBIT        1
   DEFINE  LCD_COMMANDUS   2000
   DEFINE  LCD_DATAUS      50
   
   SYMBOL PROBE = PORTA.0 ' O-scope probe for timing tests
   SEC_COUNT VAR BYTE     ' seconds counter
   OLD_COUNT VAR BYTE     ' copy of count
   PORT_VAL  VAR BYTE     ' holds value read from input port
   O_FLOWS   VAR BYTE     ' holds the number of timer1 overflows
   
   T1LO CON $B0           ' timer1 reload values
   T1HI CON $3C
   
   PORTB = 0              ' RB3 to RB0 = 0
   TRISB = %11110000      ' RB7 to RB4 = inputs, RB3 to RB0 = outputs
   INTCON  = 0            ' interrupts disabled (we're just watching tmr1 int flag)
   OPTION_REG = 0         ' portb pull-ups on for RB7 to RB4
   T1CON = %00100000      ' 1:4 prescale (4uS per tick), timer1 off
   TMR1H = T1HI           ' $3CB0=15,536. 65,536-15,536 = 50,000
   TMR1L = T1LO           ' 50,000 ticks * 4uS = 0.2 seconds. 1 second / 0.2 = 5
                          ' every 5 timer1 overflows = 1 second
   ADCON1 = 7             ' Set PORTA and PORTE to digital
   LOW PORTE.2            ' LCD R/W line low (W)
   PAUSE 1000             ' Wait for LCD to start up
   LCDOUT $FE,1           ' Clear LCD
   
Main:
   SEC_COUNT=30           ' load for 30 to 0 countdown
   OLD_COUNT=1            ' mismatch for display (see below)
   O_FLOWS=0              ' clear overflow count
   PIR1.0=0               ' clear timer1 overflow flag
   T1CON.0=1              ' start timer1
   
Test:
   TOGGLE PROBE           ' 77uS from start to return

   ' Note: Timer1 overflows every 0.2 seconds, so we have time to execute
   ' a whopping 200,000 single-cycle instructions before this section is
   ' executed
   IF PIR1.0 THEN           ' if timer1 overflow then
aqui puedes encender led verde como testigo
      O_FLOWS=O_FLOWS+1     ' inc oveflow counter
      IF O_FLOWS=5 THEN     ' if 1 second elapsed then aqui puedes encender/apagar led rojo
         SEC_COUNT=SEC_COUNT-1 ' decrement seconds count
         O_FLOWS=0          ' reset overflow counter
      ENDIF
      PIR1.0=0              ' clear timer1 overflow flag
      T1CON.0 = 0           ' stop timer1
      TMR1L = TMR1L + T1LO  ' add in low byte
      IF TMR1L < T1LO THEN  TMR1H = TMR1H + 1  ' if low byte overflow, increment high byte
      TMR1H = TMR1H + T1HI  ' add in high byte
      T1CON.0=1             ' re-start timer1
      IF SEC_COUNT=255 THEN
         SEC_COUNT=30       ' reset to 30 after 0-1 underflow
         OLD_COUNT=1        ' make sure new value is displayed
      ENDIF
   ENDIF
   
   ' display is updated only when count has changed, so LCDOUT gets executed
   ' only once each second
   IF SEC_COUNT != OLD_COUNT THEN
      LCDOUT $FE,1,"Count = ",DEC SEC_COUNT
      OLD_COUNT=SEC_COUNT  ' match last displayed count for next pass
   ENDIF
   
   ' portb gets read ~every 77uS
   PORT_VAL = (PORTB >> 4) ' read portb
   IF PORT_VAL !=15 THEN ProcessInput ' do something if switch pressed
   GOTO Test
   
ProcessInput:
   ' print switch inputs on 2nd line of LCD when RB7, 6, 5 or 4 are at ground
   LCDOUT $FE,$C0,IBIN4 PORT_VAL ' display input values
   GOTO Test
   
   END
****************************************************************

Desconectado Azteka

  • PIC10
  • *
  • Mensajes: 24
Re:Duda con temporizador
« Respuesta #6 en: 31 de Marzo de 2023, 16:46:59 »
dogflu66, scrwld, mil gracias por responder. Estoy trabajando con los consejos que he recibido aquí. Ya han mejorado los resultados, aunque aún no funciona al 100%. Saludos a todos.

Desconectado Azteka

  • PIC10
  • *
  • Mensajes: 24
Re:Duda con temporizador
« Respuesta #7 en: 03 de Abril de 2023, 01:42:24 »
Hola a todos. Este es el avance que llevo, ya funciona bastante bien. Modifiqué la configuración del preescalador a 1:128 y el valor del TMR0 a 4, con lo que se producen interrupciones cada 32,256 mS, ésto multiplicado por 31 da 999,936 uS, ya bastante más cercano a un segundo.

 Tengo el siguiente comentario: creí que podía eliminar la rutina "PROG", que se encarga de hacer destellar un led de color verde, lo puse para verificar que efectivamente el PIC "dejaba de hacer lo que estaba haciendo" para atender la interrupción. Pero al eliminarla, aparentemente, ya no se producen interrupciones, el led rojo, que se encarga de notificar cuando se ha cumplido un segundo, deja de destellar. ¿Entonces es necesario que el PIC "esté haciendo algo" para que se produzcan las interrupciones? Yo hubiera pensado que simplemente con que el PIC estuviera alimentado, sin estar haciendo una tarea específica habrían interrupciones. :oops: :oops: :oops:
 Ahora mi pregunta es, si estoy haciendo destellar el led rojo, que me notifica que se ha cumplido un segundo, con una pausa de 200 mS y, como bien comentó Eduardo2, "Menos que menos usar un PAUSE 200 dentro de una interrupción de período 10ms...", ¿qué valor sería recomendable para la pausa en lugar de 200?

Aún tengo pendiente investigar lo que comentó DominusDRR acerca de la instrucción DISABLE, que aparece justo antes de ejecutar la RAI y de la instrucción RESUME, casi al final de la RAI, pero que está una línea antes de la instrucción ENABLE. Aparentemente el RESUME dejaría sin ejecutar el ENABLE. Dejo imagen de donde tomé las instrucciones así.

Gracias a todos por adelantado. Saludos.

Código: QBasic/QuickBASIC
  1. DEFINE OSC 4
  2.  
  3. CONTADOR VAR BYTE                           ;CONTADOR DE INTERRUPCIONES DE 32,256 uS CADA UNA
  4. CONTADOR = 0                                ;INICIALIZA CONTADOR DE INTERRUPCIONES
  5.  
  6. CONT_PAUSAS VAR BYTE                        ;CONTADOR DE PAUSAS DE LA RUTINA "PROG"
  7.  
  8. LED_VERDE VAR PORTB.1                       ;LED DE LA RUTINA "PROG" PARA VERIFICAR VISUALMENTE
  9.                                             ;LA INTERRUPCIÓN DE DICHA RUTINA CUANDO SE CUMPLE
  10.                                             ;UN SEGUNDO
  11.  
  12. LED_ROJO VAR PORTB.2                        ;EL LED ENCIENDE CADA QUE SE CUMPLE UN SEGUNDO
  13.  
  14. TRISB = 0
  15.  
  16. ON interrupt GOTO RAI                       ;RUTINA DE ATENCIÓN A LA INTERRUPCIÓN
  17.  
  18. OPTION_REG = %01010110                      ;CONFIGURA PREEESCALADOR TMR0 EN 1:128
  19.  
  20. TMR0 = 4                                    ;INICIALIZA TMR0
  21.  
  22. INTCON= %10100000                           ;HABILITA INTERRUPCIÓN TMR0
  23.  
  24. PROG:                                       ;RUTINA PARA DETECTAR VISUALMENTE LA INTERRUPCIÓN
  25.   HIGH LED_verde
  26.   GOSUB pausA
  27.   low LED_verde
  28.   GOSUB pausA
  29.   GOTO prog
  30.  
  31. PAUSA:
  32.   FOR CONT_PAUSAS = 1 TO 20                 ;GENERA PAUSAS DE 10 mS PARA NO PERDER INTERRUPCIONES
  33.       PAUSE 10
  34.       NEXT CONT_PAUSAS
  35.   RETURN
  36.      
  37. DISABLE                                     ;DESHABILITAR INTERRUPCIONES
  38. RAI:
  39.   TMR0 = 4
  40.  
  41.   CONTADOR = CONTADOR + 1
  42.  
  43.   IF CONTADOR = 31 THEN                     ;31 INTERRUPCIONES DE 32,256 mS CADA UNA = 999,936 uS  
  44.      GOSUB UN_SEGUNDO
  45.   ENDIF
  46.  
  47.   INTCON.2 = 0                              ;RESETEA LA BANDERA DE INTERRUPCIÓN DEL TMR0
  48.   RESUME                                    ;REGRESA AL PROGRAMA PRINCIPAL
  49.   ENABLE                                    ;HABILITAR INTERRUPCIONES  
  50.  
  51. UN_SEGUNDO:
  52.   HIGH LED_ROJO
  53.   PAUSE 200
  54.   LOW LED_ROJO
  55.   CONTADOR = 0
  56.   RETURN
  57.  

Desconectado Eduardo2

  • PIC24F
  • *****
  • Mensajes: 965
Re:Duda con temporizador
« Respuesta #8 en: 03 de Abril de 2023, 09:59:51 »
...
 Tengo el siguiente comentario: creí que podía eliminar la rutina "PROG", que se encarga de hacer destellar un led de color verde, lo puse para verificar que efectivamente el PIC "dejaba de hacer lo que estaba haciendo" para atender la interrupción. Pero al eliminarla, aparentemente, ya no se producen interrupciones, el led rojo, que se encarga de notificar cuando se ha cumplido un segundo, deja de destellar. ¿Entonces es necesario que el PIC "esté haciendo algo" para que se produzcan las interrupciones? Yo hubiera pensado que simplemente con que el PIC estuviera alimentado, sin estar haciendo una tarea específica habrían interrupciones. :oops: :oops: :oops:
Si sacás "PROG" el programa ejecuta lo que sigue (la interrupción) y retorna a cualquier parte.
Son cosas de éste Basic, en C por ejemplo si sacás main() te da error al compilar.

Citar
Ahora mi pregunta es, si estoy haciendo destellar el led rojo, que me notifica que se ha cumplido un segundo, con una pausa de 200 mS y, como bien comentó Eduardo2, "Menos que menos usar un PAUSE 200 dentro de una interrupción de período 10ms...", ¿qué valor sería recomendable para la pausa en lugar de 200?
Dentro de la interrupción es mala palabra usar PAUSE, hasta un PAUSEUS de pocos us se puede aceptar, pero nunca de milisegundos. 
El problema es que querés llevar dos temporizaciones simultáneamente con instrucciones que no fueron pensadas para trabajar así.  Como se trata de pruebas, te di un ejemplo para hacerlo parpadear pues si no tendrías que escribir una interrupción que maneje varios contadores y en el bucle principal se decide cuando se ejecuta la acción.

Desconectado DominusDRR

  • PIC24H
  • ******
  • Mensajes: 1937
    • Sicoy
Re:Duda con temporizador
« Respuesta #9 en: 03 de Abril de 2023, 10:26:11 »
Hola a todos. Este es el avance que llevo, ya funciona bastante bien. Modifiqué la configuración del preescalador a 1:128 y el valor del TMR0 a 4, con lo que se producen interrupciones cada 32,256 mS, ésto multiplicado por 31 da 999,936 uS, ya bastante más cercano a un segundo.

 Tengo el siguiente comentario: creí que podía eliminar la rutina "PROG", que se encarga de hacer destellar un led de color verde, lo puse para verificar que efectivamente el PIC "dejaba de hacer lo que estaba haciendo" para atender la interrupción. Pero al eliminarla, aparentemente, ya no se producen interrupciones, el led rojo, que se encarga de notificar cuando se ha cumplido un segundo, deja de destellar. ¿Entonces es necesario que el PIC "esté haciendo algo" para que se produzcan las interrupciones? Yo hubiera pensado que simplemente con que el PIC estuviera alimentado, sin estar haciendo una tarea específica habrían interrupciones. :oops: :oops: :oops:
 Ahora mi pregunta es, si estoy haciendo destellar el led rojo, que me notifica que se ha cumplido un segundo, con una pausa de 200 mS y, como bien comentó Eduardo2, "Menos que menos usar un PAUSE 200 dentro de una interrupción de período 10ms...", ¿qué valor sería recomendable para la pausa en lugar de 200?

Aún tengo pendiente investigar lo que comentó DominusDRR acerca de la instrucción DISABLE, que aparece justo antes de ejecutar la RAI y de la instrucción RESUME, casi al final de la RAI, pero que está una línea antes de la instrucción ENABLE. Aparentemente el RESUME dejaría sin ejecutar el ENABLE. Dejo imagen de donde tomé las instrucciones así.

Gracias a todos por adelantado. Saludos.

Código: QBasic/QuickBASIC
  1. DEFINE OSC 4
  2.  
  3. CONTADOR VAR BYTE                           ;CONTADOR DE INTERRUPCIONES DE 32,256 uS CADA UNA
  4. CONTADOR = 0                                ;INICIALIZA CONTADOR DE INTERRUPCIONES
  5.  
  6. CONT_PAUSAS VAR BYTE                        ;CONTADOR DE PAUSAS DE LA RUTINA "PROG"
  7.  
  8. LED_VERDE VAR PORTB.1                       ;LED DE LA RUTINA "PROG" PARA VERIFICAR VISUALMENTE
  9.                                             ;LA INTERRUPCIÓN DE DICHA RUTINA CUANDO SE CUMPLE
  10.                                             ;UN SEGUNDO
  11.  
  12. LED_ROJO VAR PORTB.2                        ;EL LED ENCIENDE CADA QUE SE CUMPLE UN SEGUNDO
  13.  
  14. TRISB = 0
  15.  
  16. ON interrupt GOTO RAI                       ;RUTINA DE ATENCIÓN A LA INTERRUPCIÓN
  17.  
  18. OPTION_REG = %01010110                      ;CONFIGURA PREEESCALADOR TMR0 EN 1:128
  19.  
  20. TMR0 = 4                                    ;INICIALIZA TMR0
  21.  
  22. INTCON= %10100000                           ;HABILITA INTERRUPCIÓN TMR0
  23.  
  24. PROG:                                       ;RUTINA PARA DETECTAR VISUALMENTE LA INTERRUPCIÓN
  25.   HIGH LED_verde
  26.   GOSUB pausA
  27.   low LED_verde
  28.   GOSUB pausA
  29.   GOTO prog
  30.  
  31. PAUSA:
  32.   FOR CONT_PAUSAS = 1 TO 20                 ;GENERA PAUSAS DE 10 mS PARA NO PERDER INTERRUPCIONES
  33.       PAUSE 10
  34.       NEXT CONT_PAUSAS
  35.   RETURN
  36.      
  37. DISABLE                                     ;DESHABILITAR INTERRUPCIONES
  38. RAI:
  39.   TMR0 = 4
  40.  
  41.   CONTADOR = CONTADOR + 1
  42.  
  43.   IF CONTADOR = 31 THEN                     ;31 INTERRUPCIONES DE 32,256 mS CADA UNA = 999,936 uS  
  44.      GOSUB UN_SEGUNDO
  45.   ENDIF
  46.  
  47.   INTCON.2 = 0                              ;RESETEA LA BANDERA DE INTERRUPCIÓN DEL TMR0
  48.   RESUME                                    ;REGRESA AL PROGRAMA PRINCIPAL
  49.   ENABLE                                    ;HABILITAR INTERRUPCIONES  
  50.  
  51. UN_SEGUNDO:
  52.   HIGH LED_ROJO
  53.   PAUSE 200
  54.   LOW LED_ROJO
  55.   CONTADOR = 0
  56.   RETURN
  57.  

Tal vez deberías pensar en el uso de retardos asincrónicos. Ese concepto casi nunca lo usan, es decir la gran mayoría usa retardos sincrónicos como en tu caso. Obviamente son más fáciles de entender, pero en cambio surgen estos problemas como lo del CPU se  "congela" hasta que el retardo finalice, mientras que un retardo asincrónico puedes hacer otros procesos.

Con un retardo asincrónico, puede hacer incluso otros retardos al mismo tiempo, la desventaja de los asincrónicos es que no son tan precisos, por ejemplo, si haces un retardo de 1ms, que es 1000 us, en realidad puede ser un poco más largo, es decir puede ser de 1015 us. Pero para procesos como encender y apagar leds, u otros retardos que no es necesaria tanta precisión, son más que suficientes.
« Última modificación: 03 de Abril de 2023, 13:35:34 por DominusDRR »
Tengo una idea algo difusa sobre MPLAB Harmony, XC32 con PIC32

Desconectado Azteka

  • PIC10
  • *
  • Mensajes: 24
Re:Duda con temporizador
« Respuesta #10 en: 05 de Abril de 2023, 00:27:29 »
Hola Eduardo2 y DominusDRR, nuevamente, mil gracias por contestar. Voy a poner en práctica lo que comentan... aunque eso de "retardo asincrónico" me suena a "no le voy a entender"  :shock: :shock: :shock:
 
A seguir experimentando para aprender. Gracias a todos.  ((:-))

Desconectado DominusDRR

  • PIC24H
  • ******
  • Mensajes: 1937
    • Sicoy
Re:Duda con temporizador
« Respuesta #11 en: 05 de Abril de 2023, 10:45:54 »
Hola Eduardo2 y DominusDRR, nuevamente, mil gracias por contestar. Voy a poner en práctica lo que comentan... aunque eso de "retardo asincrónico" me suena a "no le voy a entender"  :shock: :shock: :shock:
 
A seguir experimentando para aprender. Gracias a todos.  ((:-))

Se denomina que un proceso es sincrónico, cuando el CPU permanece en un bucle esperando a que se complete o finalice. Estos procesos también se los denomina también tareas apropiativas ya que se apoderan del CPU y no lo liberan hasta no terminar su proceso.

El tipo caso es los retardos que existen en todos los compiladores, por ejemplo, en tu caso tienes la instrucción PAUSE, que me imagino que es un lazo que realiza varios nops hasta alcanzar el retardo deseado.

Un proceso asincrónico o también llamado cooperativo no se apodera 100% del CPU, lo que hace es que un proceso se divide en varias subpartes de tal manera que hace pequeñas porciones de código para ceder el CPU a otros procesos de igual manera cooperativos. A la velocidad que se ejecuta el CPU, da la sensación al usuario que el microcontrolador hace varios procesos a al vez.

Claro, todo esta palabrería que he escrito, necesita ejemplos para entenderlos, y lo hago con diagramas de flujo.

Imagina que necesitas encender 2 diodos les de manera periódica, el primero cada segundo y el otro cada dos segundos.

Un método sincrónico sería algo similar a lo siguiente:



Para crear temporizadores, asincrónicos, necesitas un temporizador que este siempre funcionando, es decir que una vez que se desborde, nuevamente continúe contado ciclos de máquina. Supongamos que el temporizador que cada incremento se produce por cada 10 us.

Para este caso necesitas dos variables por cada led que permitan capturar el valor del temporizador antes mencionado.



Puedes ver que para crear el retardo, lo que se hace es una resta entre lo que tiene el temporizador y el valor capturado. Si dicha resta ha sobre pasado los 100 000 conteos (o 200 000 para el otro led) se puede asumir que ha transcurrido el tiempo deseado.

También puedes observar que nunca permanece en flujo del programa en un lazo esperando algo. Se hace una velicación (que puede ser una instrucción if), y si no se ha cumplido el tiempo deseado automáticamente se pasa a otro proceso (en este caso la otra comprobación para el otro LED), de esa manera nunca te apropias del CPU y por lo tanto es un proceso asincrónico.







« Última modificación: 05 de Abril de 2023, 11:50:50 por DominusDRR »
Tengo una idea algo difusa sobre MPLAB Harmony, XC32 con PIC32

Desconectado dogflu66

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 3510
Re:Duda con temporizador
« Respuesta #12 en: 05 de Abril de 2023, 12:06:12 »
Hola dejo la función MILLIS, lo mismo la puedes adaptar a tu lenguaje.
En este caso usa el timer1 y cuenta cada 1mSeg.

Código: Visual Basic
  1. '***********************************************************************
  2. 'MILLIS function, long type geneal counter.
  3. 'For replacing absolute pauses generated with WaitMs.
  4. 'The _MILLIS function increments every mSec. so it takes
  5. 'approximately 49.7 days To reinitialize.
  6. 'That is, it will pass through a zero value.
  7. '************************************************************************
  8. 'Pic16F88, OshonSoft Pic Basic Compiler v.8.42
  9. 'By COS, 03/2023
  10. '************************************************************************
  11. #define CONFIG = 0x2f50  'Fuse definition.
  12. #define CONFIG2 = 0x3ffc
  13. Define CLOCK_FREQUENCY = 8  'Clock a 8Mhz
  14. 'Define SIMULATION_WAITMS_VALUE = 1'Waitms is used activate for simulation.
  15. Include "_FuncionesPic16F88.bas"
  16. '************************************************************************
  17. main:
  18.         'Pin configuration
  19.         AllDigital
  20.         ConfigPin PORTA = Output
  21.         ConfigPin PORTB = Output
  22.         'Clock internal 8Mhz.
  23.         Call _setup_oscillator_mode_select_bit(_oscillator_mode_defined_by_fosc)
  24.         Call _setup_oscillator(_osc_8mhz)
  25.         'Initialize Timer1 desborde 1mSec.
  26.         Call _setup_timer1(_tmr1_internal, _tmr1_div1)
  27.         Call _set_timer1(0xf831)  'Interruption every 1mSec.
  28.         Call _timer1(_on)
  29.         Call _enable_interrupts(_int_timer1)
  30.         'Call _enable_interrupts(_global)
  31.         'Assign names to the Leds
  32.         Symbol LedGreen = RB0
  33.         Symbol LedYellow = RA7
  34. '********************************************************************
  35.         Call _SetUp_MILLIS()  'Setup _MILLIS functions, before activate interrupts
  36.         Call _enable_interrupts(_global)
  37.         Dim PreMillis As Long
  38.         Dim PreMillis1 As Long
  39.  
  40.         'Loop
  41.         While True
  42.  
  43.                 Call _MILLIS()  'Repeat As few times As possible, so As Not To interfere with interruptions.
  44.  
  45.                 If (_MILLIS - PreMillis) >= 200 Then  'Wait time in mSec.
  46.                         PreMillis = _MILLIS  'The last value of _MILLIS is stored.
  47.                         Toggle LedYellow  'Invers the state of the led.
  48.                 Endif
  49.  
  50.  
  51.                 If (_MILLIS - PreMillis1) >= 400 Then  'Wait time in mSec.
  52.                         PreMillis1 = _MILLIS  'The last value of _MILLIS is stored.
  53.                         Toggle LedGreen  'Invers the state of the led.
  54.                 Endif
  55.  
  56.         Wend
  57. End                                              
  58. '********************************************************************
  59. 'Function _MILLIS
  60. 'Requires the use of a timer and interrupts.
  61. '**************************************************
  62. 'Call _SetUp_MILLIS()  'Before triggering interrupts
  63. 'Call _MILLIS()  'Update _MILLIS counter
  64. 'Call _CounterMillis()  'Incrementa contador Millis
  65. '**************************************************
  66. 'Inicialize functions
  67. Proc _SetUp_MILLIS()
  68.         _MILLIS = 0
  69.         _CounterMillis = 0
  70. End Proc                                          
  71. 'Return _MILLIS as Long and global variable
  72. Function _MILLIS() As Long
  73.         Disable  'Stop interruption
  74.         _MILLIS = _CounterMillis
  75.         Enable  'Enable interruption
  76. End Function                                      
  77. 'Increment the counter Millis
  78. 'Return _CounterMillis as Long and global variable
  79. Function _CounterMillis() As Long
  80.         _CounterMillis++  'Increment counter millis
  81. End Function                                      
  82. '********************************************************************
  83. 'Interrupt vector
  84. On Interrupt  'Disable interruptions
  85. Save System
  86.         If _tmr1if Then
  87.                 Call _set_timer1(0xf831)  'Reload TMR1 registers to count 1mSec and clear _tmr1if.
  88.                 Call _CounterMillis()  'Increments _CounterMILLIS every mSec.
  89.         Endif
  90. Resume  'Enable interruptions

Me auto-cito: exactamente es lo que hace la función Millis.
Saludos desde Granada, España.

Desconectado Azteka

  • PIC10
  • *
  • Mensajes: 24
Re:Duda con temporizador
« Respuesta #13 en: 03 de Mayo de 2023, 01:17:37 »
DominusDRR, excelente tu explicación. Mil gracias.


 

anything