Autor Tema: Retardos e Interrupciones  (Leído 176 veces)

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

Desconectado PicMinor

  • PIC16
  • ***
  • Mensajes: 177
Retardos e Interrupciones
« en: 16 de Octubre de 2020, 04:16:14 »
¡ Saludos al Foro !

Tengo una duda muy simple:

Con el compilador CCS, si tenemos un delay_ms muy largo y durante ese tiempo de retardo recibimos datos por la UART ¿Salta la interrupción?

¡Gracias por anticipado!

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 7894
Re:Retardos e Interrupciones
« Respuesta #1 en: 16 de Octubre de 2020, 08:41:03 »
En cualquier lenguaje un delay, es un delay.

Si esta en el loop principal SI salta la interrupcion, sea cual sea.

Si esta dentro de otra interrupcion, depende.
Algunos micros tienen 2 prioridades, y obviamente si la UART es prioridad mayor, entonces SI. Sino NO.
En caso de tener un solo punto de interrupcion, directamente NO.

AL entrar en la interrupcion se deshabilita las mismas, por eso no va a ocurrir otra (a no ser que sea de mayor prioridad), pero si esta en el programa, no hay problema.

Lo que si... si estas trabajando con delays de ese tipo, si procesas el mensaje en el loop principal (main) entonces asegurate de tener un buen buffer de recepcion.
Sino trabajalo en la interrupcion.

Desconectado PicMinor

  • PIC16
  • ***
  • Mensajes: 177
Re:Retardos e Interrupciones
« Respuesta #2 en: 19 de Octubre de 2020, 04:49:38 »
¡ Gracias por la respuesta !
Afortunadamente el delay lo tengo en el bucle principal, no dentro de otra interrupción.
El micro es un pic18 y no manejo mas que la interrupción por UART.
Una duda: Dices que al entrar en una interrupción se deshabilitan todas. Yo tengo la costumbre de deshabilitarlas manualmente al entrar. ¿No es necesario entonces?

Gracias de nuevo!

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 7894
Re:Retardos e Interrupciones
« Respuesta #3 en: 19 de Octubre de 2020, 08:44:34 »
Una duda: Dices que al entrar en una interrupción se deshabilitan todas. Yo tengo la costumbre de deshabilitarlas manualmente al entrar. ¿No es necesario entonces?

No es necesario.... Lo explico para un PIC16, un PIC18 es lo mismo nomas que tiene 2 bits distintos, uno para cada prioridad (a no ser que fue configurado para trabajar con 1 sola prioridad).
Al entrar a una interrupción el bit global que activa las interrupciones supongamos GIE, se pone en 0.
Cuando salís de la interrupción, si usaste ASM alguna vez recordaras que es necesario hacerlo mediante la instrucción RETFIE.

Esta instrucción funciona como un RETURN (vuelve al lugar donde ocurrió la interrupción del programa principal), pero además pone un 1 en GIE, es decir vuelve a habilitar las interrupciones de forma global.

Lo que si uno debe limpiar es el flag de interrupción, pero en la UART se limpia al leer el contenido del registro de entrada, por lo que no es necesario ningún paso extra, solo leer el registro y listo.

Desconectado PicMinor

  • PIC16
  • ***
  • Mensajes: 177
Re:Retardos e Interrupciones
« Respuesta #4 en: 19 de Octubre de 2020, 08:52:50 »
Un poco más complicado:

Tenemos este código en CCS:

Código: C
  1. // ===========================================================================================================
  2. // Test de bucles e Interrupciones
  3. // ===========================================================================================================
  4. #include    <18F2580.h>                                             // Micro utilizado
  5.  
  6. #fuses      INTRC_IO                                                // Oscilador Interno con pines de IO
  7. #fuses      MCLR                                                    // Patilla de Reset habilitada  
  8. #fuses      NOWDT                                                   // No Watch Dog Timer
  9. #fuses      NOPROTECT                                               // Code not protected from reading
  10. #fuses      BROWNOUT                                                // Reset when brownout no detectado
  11. #fuses      NOPUT                                                   // No Power Up Timer
  12. #fuses      NOCPD                                                   // No EE protection
  13. #fuses      DEBUG                                                   // Debug mode for ICD
  14. #fuses      NOWRT                                                   // Program memory not write protected
  15. #fuses      NOLVP                                                   // No Low Voltage Power
  16. #fuses      BORV21                                                  // Reset a 2.1V
  17.  
  18. #use Delay (Clock=32000000)                                         // Usamos 32MHz
  19.  
  20. // -----------------------------------------------------------------------------------------------------------
  21. // Macro de los mensajes
  22. // -----------------------------------------------------------------------------------------------------------
  23. #define     Pon_Mensaje(Cadena,Columna,Fila) \                      
  24.                 strcpy (Buff_Mensaje, Cadena); \
  25.                 lcd_gotoxy(Columna,Fila); \
  26.                 LCD_Print_Cadena();                                 // Macro de impresión de cadenas
  27.  
  28. // -----------------------------------------------------------------------------------------------------------
  29. // Variables Globales
  30. // -----------------------------------------------------------------------------------------------------------
  31. char        Buff_Mensaje[16];                                       // Buffer de Cadenas
  32. short       Hay_Pulsacion;                                          // Test del pulsador
  33.  
  34. // -----------------------------------------------------------------------------------------------------------
  35. // Ficheros de la aplicación
  36. // -----------------------------------------------------------------------------------------------------------
  37. #include    "Flex_Lcd.c"                                            // Rutinas del LCD adaptadas
  38.  
  39. // -----------------------------------------------------------------------------------------------------------
  40. // Imprime la cadena que está en Buff_Mensaje. Debe de acabar en 0x00
  41. // -----------------------------------------------------------------------------------------------------------
  42. void LCD_Print_Cadena() {                                           // Imprime una Cadena
  43.     char i=0;                                                       // Contador
  44.  
  45.     while(Buff_Mensaje[i] != 0) {                                   // Hasta que acaba la cadena
  46.         lcd_putc(Buff_Mensaje[i]);                                  // Lo pasa al Display
  47.         i++;                                                        // Prepara el siguiente
  48.     }
  49. }
  50.  
  51. // -----------------------------------------------------------------------------------------------------------
  52. // Detecta la señal del pulsador en RB0. Es una transición Hi -> Lo
  53. // -----------------------------------------------------------------------------------------------------------
  54. #INT_EXT
  55. void Detecta_Pulsador() {
  56.     if (!input(PIN_B0)){                                            // Si pulsamos
  57.         disable_interrupts(INT_EXT);                                // Desactivamos la Interrupción
  58.         do {} while(!input(PIN_B0));                                // Esperamos a que suelte
  59.         Hay_Pulsacion=true;                                         // Se ha recibido una pulsacion
  60.     }                                                                  
  61. }
  62.  
  63. // ===========================================================================================================
  64. // Programa Principal
  65. // ===========================================================================================================
  66. void main() {
  67.     int     Num_Mensaje;                                            // Numero de mensaje
  68.  
  69.     setup_oscillator(OSC_32MHZ);                                    // Oscilador Interno a 32MHz
  70.     lcd_init();                                                     // Inicializa el LCD
  71.  
  72.     output_float(PIN_B0);                                           // Pone la línea como entrada
  73.     ext_int_edge(H_TO_L);                                           // Configuro captura del flanco de Bajada
  74.     enable_interrupts(INT_EXT);                                     // Activamos la interrupción del PIN B0
  75.     enable_interrupts(GLOBAL);                                      // Activamos las interrupciones
  76.  
  77.     Hay_Pulsacion=false;                                            // De momento es falsa
  78.  
  79.     // ********************************************************************************************************
  80.     // ESTE ES BUCLE QUE NO FUNCIONA COMO QUIERO. SIEMPRE SALE CON EL RESULTADO Nº4
  81.     // ********************************************************************************************************
  82.     while(Hay_Pulsacion==false) {                                   // Hasta que haya pulsación
  83.         Num_Mensaje=0;                                              // Ponemos el mensaje 0
  84.         Pon_Mensaje("  MENSAJE CERO  ",1,1);                        // Mensaje Cero
  85.         delay_ms(2000);                                             // Retardo de temporización
  86.  
  87.         Num_Mensaje=1;                                              // Ponemos el mensaje 1
  88.         Pon_Mensaje("   MENSAJE UNO  ",1,1);                        // Mensaje Uno
  89.         delay_ms(2000);                                             // Retardo de temporización
  90.  
  91.         Num_Mensaje=2;                                              // Ponemos el mensaje 2
  92.         Pon_Mensaje("   MENSAJE DOS  ",1,1);                        // Mensaje Dos
  93.         delay_ms(2000);                                             // Retardo de temporización
  94.  
  95.         Num_Mensaje=3;                                              // Ponemos el mensaje 3
  96.         Pon_Mensaje("  MENSAJE TRES  ",1,1);                        // Mensaje Tres
  97.         delay_ms(2000);                                             // Retardo de temporización
  98.  
  99.         Num_Mensaje=4;                                              // Ponemos el mensaje 4
  100.         Pon_Mensaje(" MENSAJE CUATRO ",1,1);                        // Mensaje Cuatro
  101.         delay_ms(2000);                                             // Retardo de temporización
  102.     }                                                               // Cierra el bucle
  103.  
  104.     Pon_Mensaje("Se ha pulsado en",1,1);                            // Mensaje del resultado
  105.     Pon_Mensaje("el mensaje n:   ",1,2);                            // Resultado
  106.     lcd_gotoxy(14,2);                                               // Se posiciona
  107.     lcd_putc(0x30+Num_Mensaje);                                     // Y escribe el nº de mensaje
  108. }
  109.  
  110.  
  111.  

El esquema es simplemente un pulsador en RB0 que lleva la línea a masa al ser pulsado y un display LCD.

El resultado al pulsar es siempre el nº 4 ya que completa el bucle aunque haya saltado la interrupción.

Lo que yo necesito es que en cuanto pulse salga del bucle y no espere hasta el final. ¿Alguien tiene alguna idea?


¡ Gracias por anticipado !
« Última modificación: 19 de Octubre de 2020, 08:57:08 por PicMinor »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 7894
Re:Retardos e Interrupciones
« Respuesta #5 en: 19 de Octubre de 2020, 11:15:13 »
Varios problemas tenes ahí..

- No hay un loop infinito. Ese es el principal problema.
- Nunca uses un posible loop infinito que detenga el programa en una interrupcion. Si queres saber que se solto el pulsador, entonces detecta el otro flanco, el de bajo a alto (L_TO_H), solamente entraria cuando lo soltas.


Si tu intencion es que al soltar el boton sepas que valor es el que se estaba mostrando entonces tenes un error de codigo.
Algo asi mejor:


Código: C
  1. // ===========================================================================================================
  2. // Test de bucles e Interrupciones
  3. // ===========================================================================================================
  4. #include    <18F2580.h>                                             // Micro utilizado
  5.  
  6. #fuses      INTRC_IO                                                // Oscilador Interno con pines de IO
  7. #fuses      MCLR                                                    // Patilla de Reset habilitada  
  8. #fuses      NOWDT                                                   // No Watch Dog Timer
  9. #fuses      NOPROTECT                                               // Code not protected from reading
  10. #fuses      BROWNOUT                                                // Reset when brownout no detectado
  11. #fuses      NOPUT                                                   // No Power Up Timer
  12. #fuses      NOCPD                                                   // No EE protection
  13. #fuses      DEBUG                                                   // Debug mode for ICD
  14. #fuses      NOWRT                                                   // Program memory not write protected
  15. #fuses      NOLVP                                                   // No Low Voltage Power
  16. #fuses      BORV21                                                  // Reset a 2.1V
  17.  
  18. #use Delay (Clock=32000000)                                         // Usamos 32MHz
  19.  
  20. // -----------------------------------------------------------------------------------------------------------
  21. // Macro de los mensajes
  22. // -----------------------------------------------------------------------------------------------------------
  23. #define     Pon_Mensaje(Cadena,Columna,Fila) \                      
  24.                 strcpy (Buff_Mensaje, Cadena); \
  25.                 lcd_gotoxy(Columna,Fila); \
  26.                 LCD_Print_Cadena();                                 // Macro de impresión de cadenas
  27.  
  28. // -----------------------------------------------------------------------------------------------------------
  29. // Variables Globales
  30. // -----------------------------------------------------------------------------------------------------------
  31. char        Buff_Mensaje[16];                                       // Buffer de Cadenas
  32. volatile short       Hay_Pulsacion;                                 // Test del pulsador
  33. const char *textos[] = {"  MENSAJE CERO  ","   MENSAJE UNO  ","   MENSAJE DOS  ","  MENSAJE TRES  "," MENSAJE CUATRO "};
  34. // -----------------------------------------------------------------------------------------------------------
  35. // Ficheros de la aplicación
  36. // -----------------------------------------------------------------------------------------------------------
  37. #include    "Flex_Lcd.c"                                            // Rutinas del LCD adaptadas
  38.  
  39. // -----------------------------------------------------------------------------------------------------------
  40. // Imprime la cadena que está en Buff_Mensaje. Debe de acabar en 0x00
  41. // -----------------------------------------------------------------------------------------------------------
  42. void LCD_Print_Cadena() {                                           // Imprime una Cadena
  43.     char i=0;                                                       // Contador
  44.  
  45.     while(Buff_Mensaje[i] != 0) {                                   // Hasta que acaba la cadena
  46.         lcd_putc(Buff_Mensaje[i]);                                  // Lo pasa al Display
  47.         i++;                                                        // Prepara el siguiente
  48.     }
  49. }
  50.  
  51. // -----------------------------------------------------------------------------------------------------------
  52. // Detecta la señal del pulsador en RB0. Es una transición Lo -> Hi (Se suelta)
  53. // -----------------------------------------------------------------------------------------------------------
  54. #INT_EXT
  55. void Detecta_Pulsador() {
  56.     clear_interrupt(INT_EXT);
  57.     Hay_Pulsacion=true;
  58. }
  59.  
  60. // ===========================================================================================================
  61. // Programa Principal
  62. // ===========================================================================================================
  63. void main() {
  64.     int     Num_Mensaje=UINT_MAX;                                   // Numero de mensaje, UINT_MAX para que cuando se sume 1, quede en 0. En limits.h
  65.  
  66.     setup_oscillator(OSC_32MHZ);                                    // Oscilador Interno a 32MHz
  67.     lcd_init();                                                     // Inicializa el LCD
  68.  
  69.     output_float(PIN_B0);                                           // Pone la línea como entrada
  70.     ext_int_edge(L_TO_H);                                           // Configuro captura del flanco de Bajada
  71.     enable_interrupts(INT_EXT);                                     // Activamos la interrupción del PIN B0
  72.     enable_interrupts(GLOBAL);                                      // Activamos las interrupciones
  73.  
  74.     while(1) {
  75.     Hay_Pulsacion=false;                                            // Iniciamos como falsa
  76.  
  77.         while(Hay_Pulsacion==false) {                                   // Hasta que haya pulsación
  78.             if(++Num_Mensaje > 5) Num_Mensaje = 0;                      // En el caso que sea mayor a 5 (La cantidad de textos) vuelvo a 0
  79.             Pon_Mensaje(&textos[Num_Mensaje],1,1);                      // Mensaje
  80.             delay_ms(2000);                                             // Retardo de temporización
  81.          }                                                              // Cierra el bucle
  82.  
  83.         Pon_Mensaje("Se ha pulsado en",1,1);                            // Mensaje del resultado
  84.         Pon_Mensaje("el mensaje n:   ",1,2);                            // Resultado
  85.         lcd_gotoxy(14,2);                                               // Se posiciona
  86.         lcd_putc(0x30+Num_Mensaje);                                     // Y escribe el nº de mensaje
  87.     }
  88. }

Seguramente tenga errores, ya que no poseo en este momento ningun compilador instalado, lo hago todo en el bloc de notas. El limite se puede hacer tambien dependiente de la cantidad de textos que posea el array.

Lo que no recuerdo es si CCS le gusta los punteros a string constantes... Creo que una vez lo pase y tenian problemas. Pero bueno.. te va a dar una mejor idea de como hacerlo.
Observa que Hay_pulsacion, esta como volatile, debido a que se usa en ambos lados (principal e interrupcion)

Desconectado PicMinor

  • PIC16
  • ***
  • Mensajes: 177
Re:Retardos e Interrupciones
« Respuesta #6 en: 20 de Octubre de 2020, 03:22:50 »
¡ Gracias KILLERJC !

Creo que he captado la idea, de la forma que propones saltaría fuera del bucle en la última opción que estaba asignada, lo cual es parte de lo que buscaba. Lo que no evitamos es que se complete el retardo de 2000ms, lo cual no es crítico en la aplicación en la que estoy trabajando pero sí un poco "antiestético". Planteémonos la situación: Estamos leyendo una serie de mensajes y pulsamos el botón. El código tardaría en el caso peor 2sg en reaccionar. Se me ocurren varias soluciones pero todas pasan por una rutina de retardos con detección del flag de la interrupción mediante un bucle "for". Tal vez no haya ninguna solución más "elegante".

Por ejemplo se me ocurre sustituir los delay_ms por la siguiente función:

Código: C
  1. // -----------------------------------------------------------------------------------------------------------
  2. // Retardo que sale con 'true' al pasar a 'true' el flag 'Hay_Pulsacion'
  3. // -----------------------------------------------------------------------------------------------------------
  4. short Retardo_ms(unsigned long Num_Milis) {
  5.     unsigned long i;                                                // Contador genérico
  6.  
  7.     for (i=0;i<Num_Milis;i++) {                                     // Bucle de conteo
  8.         delay_ms(1);                                                // Un milisegundo de retardo
  9.         if (Hay_Pulsacion==true) return (true);                     // Salimos con pulsación    
  10.     }
  11.     return (false);                                                 // Salimos sin pulsación
  12. }
  13.  
  14.  

Y llamar a la función de la forma: if (Retardo(2000)) break;

Funciona pero no es demasiado elegante.

Respecto a los punteros a string constantes sí que se puede hacer porque yo lo he hecho. No he analizado si hay errores en tu código porque no es ese el problema, lo que busco es captar la idea general de cómo salir del bucle al pulsar el botón. De momento ya he avanzado mucho.

Una curiosidad: ¿Qué aporta el prefijo "volatile" a la variable Hay_Pulsacion?
« Última modificación: 20 de Octubre de 2020, 03:53:21 por PicMinor »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 7894
Re:Retardos e Interrupciones
« Respuesta #7 en: 20 de Octubre de 2020, 08:02:20 »
Otra soluciones a lo que planteas:

- Usando un Timer, en el cual cuentes interrupciones hasta llegar a los 2s y activar una flag, Haria que se apenas se detecte salga (necesitas poner algo que lo detenga al final tambien, sino es tan rapido que se va a repetir constamente. Esta es la solucion ideal, ya que si haces un timer de 0.1s de interrupcion, podes crear "demoras" con mutiplos de este.

Código: C
  1.         while(Hay_Pulsacion==false) {                                   // Hasta que haya pulsación
  2.             if(flag_timer) {
  3.               if(++Num_Mensaje > 5) Num_Mensaje = 0;                      // En el caso que sea mayor a 5 (La cantidad de textos) vuelvo a 0
  4.               Pon_Mensaje(&textos[Num_Mensaje],1,1);                      // Mensaje
  5.               flag_timer = 0;
  6.             }
  7.          }    

- Usando un delay mas pequeño. Pero actualizando el LCD cada X pasadas, logrando una demora de 100ms maximo por pasada de while. Se puede achicar mas, ejemplo contador en 200 y delay en 10ms

Código: C
  1.         contador = 0;
  2.  
  3.         while(Hay_Pulsacion==false) {                                   // Hasta que haya pulsación
  4.             if(contador >= 20) {
  5.               if(++Num_Mensaje > 5) Num_Mensaje = 0;                      // En el caso que sea mayor a 5 (La cantidad de textos) vuelvo a 0
  6.               Pon_Mensaje(&textos[Num_Mensaje],1,1);                      // Mensaje
  7.               contador = 0;
  8.             }
  9.             contador++;
  10.             delay_ms(100);
  11.          }    


Citar
Una curiosidad: ¿Qué aporta el prefijo "volatile" a la variable Hay_Pulsacion?

Uno de los grandes problemas para el compilador es que la "interrupcion" es tratada como una funcion mas. Lo que puede que tengas que en el main pongas a 0 una variable y en la interrupcion el 1, pero nunca llamas desde el main a la interrupcion, asi que el compilador al tratar de optimizar el codigo, piense que esa variable que esta en el main, al no tener acceso a la funcion de interrupcion, considere que jamas va a cambiar de 0, y por lo tanto lo elimine del código, dejandolo en 0 para siempre. ( O eso es lo que cree el compilador)

Como ves es un problema de optimizacion. Entonces uno puede usar "volatile", esto le dice al compilador que en realidad puede ser algo que puede cambiar sin interaccion del codigo, por ejemplo los puertos son tratado asi. Entonces con ese volatile, forzamos al compilador que siempre lo lea. Y que no tome por sentado que no va a cambiar.

Como regla general: Si lo usas en la interrupcion y en el programa principal necesitas tener en cuenta algunos puntos:

- Usar volatile debido a las optimizaciones del compilador
- Intentar no hacerlo, Pero sino, tener cuidado con variables que requieran mas de 1 instrucción su cambio, ya que puede ocurrir que el cambio de una variable quede a la mitad, y luego en la interrupción se cambia por otra, al volver al main vuelve al valor anterior.. Especialmente con variables que ocupan mas de 1 byte, en donde la parte baja se suele realizar primero y luego la parte alta, imaginate que pasaria si a la mitad de eso ocurre la interrupcion y en la interrupcion lo cambias, pensa como quedaria cuando haga la otra parte en el main.
« Última modificación: 20 de Octubre de 2020, 08:26:48 por KILLERJC »

Desconectado PicMinor

  • PIC16
  • ***
  • Mensajes: 177
Re:Retardos e Interrupciones
« Respuesta #8 en: 21 de Octubre de 2020, 06:04:46 »
Gracias KILLERJC. Entendido lo del "volatile"

Lo del TIMER lo estudiaré con más calma ya que le veo muchas posibilidades, aunque me da un poco de "reparo" el usar simultáneamente interrupciones por INT_EXT, por RDA y ahora además el TIMER. (Sin contar con el ADC que posiblemente me haga falta también).

De momento voy a utilizar la función Retardo_ms, que aunque no sea universal, de momento sirve para cubrir mis necesidades. De todas formas he incorporado a mi código alguna de tus técnicas, así que gracias de nuevo.