TODOPIC

Microcontroladores PIC => Primeros pasos - Iniciación a los microcontroladores => Mensaje iniciado por: tito melli en 01 de Abril de 2017, 13:10:05

Título: Problema con contador de eventos TIMER0
Publicado por: tito melli en 01 de Abril de 2017, 13:10:05
Hola!

Estoy programando un contador de eventos por segundo con un 16F54, con la idea de que cada segundo refresque un display de 7 segmentos con 4 dígitos (en este caso de ánodo común).

El problema está en que mientras el TIMER0 está contando los eventos durante este segundo, el display está apagado, pues no puede hacer 2 procesos a la vez.

¿Hay alguna forma de que el display siempre esté ON, y cada segundo actualice los valores que ha obtenido del TIMER0?

Gracias!

Saludos.
Título: Re:Problema con contador de eventos TIMER0
Publicado por: KILLERJC en 01 de Abril de 2017, 13:52:11
No hay formas de que el display este SIEMPRE ON, a no ser que puedas proveer para cada digito el numero y mantenerlo.

Lo que se suele realizar es un multiplexado, y lo mejor es hacerlo con un timer. Suponete un tiempo de 5ms la interrupcion del timer. Cada ves que entra pone otro dato en el puerto y activa el comun del display que corresponde..

Esto al ser tan rapido parece como si todos estuvieran continuamente encendidos. Si quisiera actualizar al segundo, recien a las 1s / 5ms = 200 veces que entre en la interrupcion actualizo los valores que muestro. Si es en C algo asi:


Código: C
  1. uint8_t ContadorInterrupciones = 0;             //Usado para contar cuantas veces entro a la interrupcion
  2. uint8_t Contadordisplay = 0;
  3.  
  4. void interrupcionTimer(void)
  5. {
  6.         TIMER = 0x000;          // Aca recargo el timer con el valor que necesita para mantener el tiempo
  7.         Comunes = 0;            //Apago todos los displays
  8.         switch(contadordisplay)
  9.         {
  10.                 case 0:
  11.                         PORTx = Unidades;
  12.                         ComunUnidades = 1
  13.                         contadordisplay++;
  14.                         break;
  15.                 case 1:
  16.                         PORTx = Decenas;
  17.                         ComunDecenas = 1
  18.                         contadordisplay++;
  19.                         break;
  20.                 case 2:
  21.                         PORTx = Centenas;
  22.                         ComunCentenas = 1
  23.                         contadordisplay++;
  24.                         break;
  25.                 case 3:
  26.                         PORTx = Miles;
  27.                         ComunMiles = 1
  28.                         contadordisplay = 0;
  29.                         break;
  30.         }
  31.         if ( contadorInterrupciones > 199 )
  32.         {
  33.                 // Si entro aca pasaron 200 * Duracion timer. En el ejemplo de que la interrupcion sea
  34.                 // cada 5ms, entonces aca entra cada 1s
  35.  
  36.                 // Calculo el nuevo valor que pongo en Unidades,Decenas, Centenas y Miles
  37.  
  38.                 // Codigo de ejemplo va a sumar 1 cada 1 segundo si es que el timer es de 5ms
  39.                 Unidades++;
  40.                 if(Unidades>9) { Unidades = 0; Decenas++; }
  41.                 if(Decenas>9) { Unidades = 0; Centenas++; }
  42.                 if(Centenas>9) { Unidades = 0; Miles++; }
  43.                 if(Miles>9) { Miles = 0; }
  44.  
  45.                 ContadorInterrupciones = 0;     //Empiezo a contar de vuelta
  46.         }
  47.  
  48. }

Es una especie de pseudocodigo, vas a tener que buscar las instrucciones correctas en algunas dependiendo que compilador uses.
Título: Re:Problema con contador de eventos TIMER0
Publicado por: tito melli en 01 de Abril de 2017, 13:59:40
Hola KillerJC,

Actualmente así es como lo hago: muestro las unidades, 5ms depués las decenas, etc. El problema llega cuando el TIMER0 tiene que contar durante un segundo los pulsos que le llegan, pues todos los displays quedan apagados y sin informacion.

Creo que tendré que hacerlo con un LCD, no?
Título: Re:Problema con contador de eventos TIMER0
Publicado por: KILLERJC en 01 de Abril de 2017, 14:18:23
No, tu problema es que estas ESPERANDO 1s !

Si podes pasar el programa mejor.
Título: Re:Problema con contador de eventos TIMER0
Publicado por: tito melli en 01 de Abril de 2017, 15:19:28
Hola KillerJC,

Estoy modificando el código para usar un LCD, pero tengo el siguiente error al compilar:

Undefined identifier -- set_tris_lcd

La sentencia completa donde está el fallo es:

 #if defined(__PCB__)
   set_tris_lcd(LCD_INPUT_MAP);
 #else

He probado incluyendo, antes de #include <lcd.c>, las siguientes sentencias:

CASO1:

#define LCD_ENABLE_PIN PIN_B0
#define LCD_RS_PIN PIN_B1
#define LCD_RW_PIN PIN_B2
#define LCD_DATA4 PIN_B3
#define LCD_DATA5 PIN_B4
#define LCD_DATA6 PIN_B5
#define LCD_DATA7 PIN_B6

CASO2:

#define LCD_DATA_PORT getenv("SFR:PORTB")


En ningun caso consigo que compile sin error.

He mirado la configuración de la librería lcd.c, y creo que habría que configurar las siguientes opciones:

#if defined(__PCB__)
   // these definitions only need to be modified for baseline PICs.
   // all other PICs use LCD_PIN_MAP or individual LCD_xxx pin definitions.
                               /*                                    EN, RS,   RW,   UNUSED,  DATA  */
 const LCD_PIN_MAP LCD_OUTPUT_MAP =  {0,  0,       0,          0,          0};
 const LCD_PIN_MAP LCD_INPUT_MAP =      {0,  0,       0,         0,           0xF};
#endif


¿Podrías decirme cómo debo configurar esto?

Supongo que habrá que indicar qué pines usar, pero... no sé cuáles son.


Gracias de antemano!

Saludos
Título: Re:Problema con contador de eventos TIMER0
Publicado por: KILLERJC en 01 de Abril de 2017, 15:32:39
Con los #define asi como los tenes indicas que pines vas a usar. Los TRIS los acomoda la funcion lcd_init().

Como este ejemplo que encontre por internet:

Código: C
  1. //LCD Module Connections
  2. #define LCD_RS_PIN PIN_D1
  3. #define LCD_RW_PIN PIN_D2
  4. #define LCD_ENABLE_PIN PIN_D3
  5. #define LCD_DATA4 PIN_D4
  6. #define LCD_DATA5 PIN_D5
  7. #define LCD_DATA6 PIN_D6
  8. #define LCD_DATA7 PIN_D7
  9. //End LCD Module Connections
  10.  
  11. #include <lcd.c>
  12.  
  13. #use delay (clock=8000000)
  14.  
  15. void main()
  16. {
  17.  
  18.   int i=0;
  19.   lcd_init();
  20.  
  21.   while(TRUE)
  22.   {
  23.     lcd_gotoxy(1,1);
  24.     printf(lcd_putc,"Contador = %d", i); //Display Count
  25.     Delay_ms(2000);
  26.     i++;
  27.   }
  28. }
Título: Re:Problema con contador de eventos TIMER0
Publicado por: tito melli en 01 de Abril de 2017, 15:57:22



Esto es justo lo que probé en el CASO1. El problema es que mi PIC es Baseline, y no sirve de nada definirlos así, o al menos eso pone en la configuración:

    // these definitions only need to be modified for baseline PICs.
   // all other PICs use LCD_PIN_MAP or individual LCD_xxx pin definitions.

Tendré que ir probando configuraciones...

Título: Re:Problema con contador de eventos TIMER0
Publicado por: KILLERJC en 01 de Abril de 2017, 17:08:53
Ahora entiendo.... Con ese PIC ni pensaria en usar siquiera LCD.. Pero bueno, vamos a intentarlo.
Despues de leer la libreria..

Hay 2 formas de proceder.... La primera es:

Código: C
  1. #define LCD_DATA_PORT getenv("SFR:PORTB")      //Seteo el puerto
  2. #define set_tris_lcd(x)   set_tris_b(x)                         // Si o si debo hacerlo, ya que la unica definicion es cuando no esta la definicion anterior.
  3.  
  4. // Configurar los pines, los LCD_DATA si o si deben ser del bit 7 a 4, y los demas desde el 0 al 3.
  5. #define LCD_DATA4 PIN_B0
  6. #define LCD_DATA5 PIN_B1
  7. #define LCD_DATA6 PIN_B2
  8. #define LCD_DATA7 PIN_B3
  9. #define LCD_ENABLE_PIN PIN_B0
  10. #define LCD_RS_PIN PIN_B1
  11. #define LCD_RW_PIN PIN_B2

La segunda es depender de la libreria que hace que al no encontrar la definicion de LCD_DATA_PORT y ser de 12bits tu micro directamente elige PORTB entonces lo mas simple que podrias hacer es:

Código: C
  1. // Configurar los pines, los LCD_DATA si o si deben ser del bit 7 a 4, y los demas desde el 0 al 3.
  2. #define LCD_DATA4 PIN_B0
  3. #define LCD_DATA5 PIN_B1
  4. #define LCD_DATA6 PIN_B2
  5. #define LCD_DATA7 PIN_B3
  6. #define LCD_ENABLE_PIN PIN_B0
  7. #define LCD_RS_PIN PIN_B1
  8. #define LCD_RW_PIN PIN_B2

Resumen, la simplicidad es la ultima. Los intercambiables son los LCD_DATAx entre si (solo bits 4 a 7), y los LCD_xx_PIN entre si (Aca podes usar bits de 0 a 3)

La definicion de LCD_PORT_DATA implica que no deberias usar el bit3, pero como esta configurado como salida, no le va a hacer daño.
Título: Re:Problema con contador de eventos TIMER0
Publicado por: tito melli en 03 de Abril de 2017, 02:38:39
Hola KillerJC,

Me sigue saliendo el mismo error.

Sin embargo, he probado con una librería más antigua que he encontrado en el foro de CCS, y sí que funciona! :)

Debe haber algo mal en las definiciones, pues ni si quiera cuando creas un nuevo proyecto con el Wizard, funciona.

Ahora mi problema esta en representar la variable con el printf, que no me deja mostrar un número entero 16bit cuando escribo %u, pero ya indagaré un poco más sobre este tema (quizás debería usar %Lu, pero no hay memoria suficiente).

Alomejor lo que tengo que hacer es cambiar de PIC, jajaja.

Gracias por tu ayuda KillerJC!!

Saludos
Título: Re:Problema con contador de eventos TIMER0
Publicado por: KILLERJC en 03 de Abril de 2017, 14:49:04
Es que si te pones a pensar... tiene 25 bytes de RAM... Ya con escribir el numero ese de 16bits necesitas: 2 bytes por el numero, y luego dependera del numero, pero si es un numero de 4 cifras, estamos hablando de 5 bytes mas..

Pensarias que deberia sobrar, pero C utiliza la RAM como stack, lo cual ahi tenes mucho mas ocupado.
Asi que pienso que realmente deberias cambiar de PIC si es que pensas usar el LCD.
Título: Re:Problema con contador de eventos TIMER0
Publicado por: tito melli en 10 de Abril de 2017, 15:35:06
Hola KillerJC,

Finalmente he optado por cambiar al 16F877 (que lo tenía ya en casa) y usaré el TMR0 para contar los pulsos, y el TMR2 para hacer la interruppción en 1 segundo.

Ahora sin embargo, estoy viendo que, una vez entra en la interrupción, nunca hace las sentencias que están contenidas en el bucle infinito "while(1)".

Creía que la interrupción del TIMER ejecutaba un código, y al terminar volvía donde lo había dejado, pero no como me lo está haciendo. Te dejo el código aquí:

Código: CSS
  1. #device PIC16F877
  2. #include <16F877.h>
  3. #fuses XT, NOWDT
  4. #use delay(clock=4MHZ)
  5.  
  6. #define use_portb_lcd TRUE
  7. #define LCD_ENABLE_PIN  PIN_B0                                    ////
  8. #define LCD_RS_PIN      PIN_B1                                    ////
  9. #define LCD_RW_PIN      PIN_B2                                    ////
  10. #define LCD_DATA4       PIN_B3                                    ////
  11. #define LCD_DATA5       PIN_B4                                    ////
  12. #define LCD_DATA6       PIN_B5                                    ////
  13. #define LCD_DATA7       PIN_B6
  14. #include <lcd.c>
  15.  
  16. #use standard_io(B)
  17. #use standard_io(A)
  18.  
  19. #BYTE TMR0 = 0x01
  20. #BYTE TMR2 = 0x11
  21. #BYTE OPTION_REG = 0x81
  22. #BYTE T2CON = 0x12
  23.  
  24. int16 rpm=0;
  25. int8 tim,cont=0;
  26.  
  27. // *************** FUNCION CONTADOR DE RPM ***************
  28. void cont_rpm() {
  29.    tim,rpm=0;
  30.    tim=get_timer0();                         // Almacena el valor del contador en la variable "tim"
  31.    rpm=tim*60;                               // Cálculo RPM motor a partir de los pulsos almacenados en "tim"
  32.    set_timer0(0);                            // Resetea el contador a 0
  33. }
  34.  
  35.  
  36. // *************** FUNCION MOSTRAR EN LCD ***************
  37.  
  38. void display_rpm() {
  39.    lcd_gotoxy(1,1);
  40.    printf(lcd_putc,"RPM = %Lu",rpm);
  41. }
  42.  
  43. // *************** INTERRUPCION EN TIMER 2 ***************
  44. #int_TIMER2
  45. void TIMER2_isr(void){
  46.    if (cont==15){                            //Contador a 15 para que haga 1s de conteo en TMR0
  47.       set_timer2(255);                       //Carga del TMR2 a 255 para obtener 1s/15
  48.       output_toggle(pin_A0);                 //Simplemente para saber que está funcionando
  49.       cont_rpm();                            //Llamada a función de conteo RPM
  50.       cont=0;}                               //Borra el contador para volver a esperar 1s
  51.  
  52.    else{
  53.       set_timer2(255);                       //Vuelve a cargar el TMR2
  54.       cont++;}                               //Sigue contando hasta hacer 1s
  55. }
  56.  
  57.  
  58. // *************** PROGRAMA PRINCIPAL ***************
  59. void main() {
  60.    lcd_init();                                  // Inicializar LCD
  61.    OPTION_REG=0x28;                             // Registro para habilitar el TIMER0, en flanco ascendente, con un Prescaler de 1:1 al WDT
  62.    setup_timer_2(T2_DIV_BY_16,255,16);          // Prescaler = 1:16 | Carga TMR2 = 255 | Postcaler = 1:16
  63.    enable_interrupts(global);                   // Habilitación interruppciones globales
  64.    enable_interrupts(INT_TIMER2);               // Habilitación interrupciones TMR2
  65.  
  66. while (1);                                      // Bucle de repetición indefinido
  67.    display_rpm();                               //Representar constantemente las RPM, aunque el muestreo de pulsos durante 1 segundo no esté completo
  68. }



El problema es que no llega a realizar la función "display_rpm" y el LCD está en blanco siempre.

¿Es que el TIMER no actúa como yo pienso?

Gracias!

Saludos
Título: Re:Problema con contador de eventos TIMER0
Publicado por: KILLERJC en 10 de Abril de 2017, 15:53:51
Como vos pensas es lo correcto. El problema esta en el programa.

Por 2 cosas:
El timer 2 es de 8 bits, si le cargas 255 es decir 0xFF solo le falta contar 1 para que active de nuevo la interrupcion, en tu interrupcion tenes:

Código: C
  1. set_timer2(255);                       //Carga del TMR2 a 255 para obtener 1s/15

Si vos queres contar 255 pulsos de reloj, tenes que cargar 256 - 255 = 1, es decir el valor 1, pero no 255.
Al no llegar a terminar de ejecutar la interrupcion del timer y ya el flag se pone en 1, hace que reentre a la interrupcion otra ves. Sin llegar a ejecutar nada del main.

Y la otra es que tenes que limpiar el flag de interrupcion. Aunque tengo entendido que CCS lo hace por vos mismo.

Vi que tenias puesto un output_toggle del pin A0, segun tus calculos, esto deberia entrar 15 veces por segundo, es decir que tendrias una señal cuadrada de de unos 7.5Hz a la salida del pin A0, si es mucho mayor es por que esta entrando muchas mas veces
Título: Re:Problema con contador de eventos TIMER0
Publicado por: tito melli en 10 de Abril de 2017, 16:51:06
Hola KillerJC,

He probado con una carga del TIMER_2 = 1, pero ocurre incluso con más frecuencia, y el display sigue sin mostrar nada.

También he probado a insertar "clear_interrupt(TIMER2) antes de acabar las sentencias de la interrupcion, pero tampoco sirve.

Es que hay que añadir algo más relacionado con las interrupciones?

Saludos
Título: Re:Problema con contador de eventos TIMER0
Publicado por: KILLERJC en 10 de Abril de 2017, 17:34:48
Ahi revise.. Yo me equivoque en pensar que el Timer2 era solo ascendente y la interrupcion era producida por el overflow. La interrupcion se produce por comparacion entre el registro del TIMER y del PR2, esto hace que si necesitas contar 255 valores, tenes que poner 255 en el setup_timer_2. Entonces olvida todo lo que dije en la anterior respuesta :P, eso me paso por no fijarme en el datasheet.

El timer 2 NO necesita ser recargado! (La misma interrupcion resetea el valor del registro TMR2 ). Imagino que el setup_timer_2 ya pone el valor correcto en el registro PR2. Entonces tu codigo quedaria asi:

Código: C
  1. #device PIC16F877
  2. #include <16F877.h>
  3. #fuses XT, NOWDT
  4. #use delay(clock=4MHZ)
  5.  
  6. #define use_portb_lcd TRUE
  7. #define LCD_ENABLE_PIN  PIN_B0                                    ////
  8. #define LCD_RS_PIN      PIN_B1                                    ////
  9. #define LCD_RW_PIN      PIN_B2                                    ////
  10. #define LCD_DATA4       PIN_B3                                    ////
  11. #define LCD_DATA5       PIN_B4                                    ////
  12. #define LCD_DATA6       PIN_B5                                    ////
  13. #define LCD_DATA7       PIN_B6
  14. #include <lcd.c>
  15.  
  16. #use standard_io(B)
  17. #use standard_io(A)
  18.  
  19. #BYTE TMR0 = 0x01
  20. #BYTE TMR2 = 0x11
  21. #BYTE OPTION_REG = 0x81
  22. #BYTE T2CON = 0x12
  23.  
  24. int16 rpm=0;
  25. int8 tim,cont=0;
  26.  
  27. // *************** FUNCION CONTADOR DE RPM ***************
  28. void cont_rpm() {
  29.    tim,rpm=0;
  30.    tim=get_timer0();                         // Almacena el valor del contador en la variable "tim"
  31.    rpm=tim*60;                               // Cálculo RPM motor a partir de los pulsos almacenados en "tim"
  32.    set_timer0(0);                            // Resetea el contador a 0
  33. }
  34.  
  35.  
  36. // *************** FUNCION MOSTRAR EN LCD ***************
  37.  
  38. void display_rpm() {
  39.    lcd_gotoxy(1,1);
  40.    printf(lcd_putc,"RPM = %Lu",rpm);
  41. }
  42.  
  43. // *************** INTERRUPCION EN TIMER 2 ***************
  44. #int_TIMER2
  45. void TIMER2_isr(void){
  46.    if (cont==15){                            //Contador a 15 para que haga 1s de conteo en TMR0
  47.       output_toggle(pin_A0);                 //Simplemente para saber que está funcionando
  48.       cont_rpm();                            //Llamada a función de conteo RPM
  49.       cont=0;}                               //Borra el contador para volver a esperar 1s
  50.  
  51.    else{
  52.       cont++;}                               //Sigue contando hasta hacer 1s
  53. }
  54.  
  55.  
  56. // *************** PROGRAMA PRINCIPAL ***************
  57. void main() {
  58.    lcd_init();                                  // Inicializar LCD
  59.    OPTION_REG=0x28;                             // Registro para habilitar el TIMER0, en flanco ascendente, con un Prescaler de 1:1 al WDT
  60.    setup_timer_2(T2_DIV_BY_16,255,16);          // Prescaler = 1:16 | Carga TMR2 = 255 | Postcaler = 1:16
  61.    enable_interrupts(global);                   // Habilitación interruppciones globales
  62.    enable_interrupts(INT_TIMER2);               // Habilitación interrupciones TMR2
  63.  
  64. while (1);                                      // Bucle de repetición indefinido
  65.    display_rpm();                               //Representar constantemente las RPM, aunque el muestreo de pulsos durante 1 segundo no esté completo
  66. }
Título: Re:Problema con contador de eventos TIMER0
Publicado por: tito melli en 11 de Abril de 2017, 03:45:56
Hola KillerJC,

Lo he probado, pero me vuelve a pasar.

Puede que debugueando con el PICkit 3 sepa que está haciendo el programa, no?
Título: Re:Problema con contador de eventos TIMER0
Publicado por: KILLERJC en 11 de Abril de 2017, 08:25:19
Otro error que no me di cuenta xD por concentrarme en la interrupcion

En tu main tenes:

Código: C
  1. while (1);                                      // Bucle de repetición indefinido
  2.    display_rpm();                               //Representar constantemente las RPM, aunque el muestreo de pulsos durante 1 segundo no esté completo

Si observas tenes un punto y coma luego del while, es hace que el display_rpm no este dentro del while, sino que sean 2 instrucciones separadas algo asi:

Código: C
  1. while(1)
  2. {
  3. }
  4. display_rpm();

el cambio que tenes que hacer es por esto;

Código: C
  1. while(1)
  2. {
  3.     display_rpm();
  4. }
Título: Re:Problema con contador de eventos TIMER0
Publicado por: tito melli en 11 de Abril de 2017, 13:43:02
Hola KillerJC,

Ya he dado con el problema!

Está en el propio bucle "while", que no tiene las llaves {}, y por lo tanto se queda en un bucle vacío todo el rato. Menudo error!

Eso sí, me ha servido para aprender mucho sobre los TIMER, jejeje.

Gracias por tu ayuda!