Funcion incremetar/decrementar valor en distintos tiempos para boton con PIC y LCD

Hola
He estado trabajando para el diseño de una interfaz para un equipo medico a traves de un PIC 16F877 y una LCD donde se selecciona la cantidad de concentracion deseada de medicamento y el PIC devuelve el dato de flujo de aire a fijar manualmente en el equipo.
La seleccion de la concentracion deseada se hace a traves de dos botones que tienen como funcion incrementar o decrementar una variable de uno en uno hasta el valor deseado.
El problema surge cuando se desea dejar pulsado un boton por 2 seg por ejemplo y entonces el valor de incremento o decremento sea de 10 en 10 y no de uno en uno como esta sucediendo y cuando este se deje de pulsar vuelva a su estado normal.
Los botones se estan atendiendo por interrupciones y cada vez que se pulsa uno se actualiza la informacion en la pantalla LCD.
He intentado hacer ciclos dentro de la interrupcion y el PICC me compila sin errores el programa pero a la hora de simularlo con el PROTEUS se pierden direcciones dentro del programa y por tanto no funciona. Tambien he probado verificando dentro de la interrupcion el estado del boton pero cuando veo el pantalla solo me sale el resultado final del incremento o decremento y no la subida o bajada paulatina que es lo deseado.
Aqui les dejo el codigo y garcias por la atencion.

Código:
#include <ozomed16f.h>
#include <STDLIB.h>

#define USE_2LINE_LCD TRUE
#define LCD_DATA0         PIN_D4
#define LCD_DATA1         PIN_D5 
#define LCD_DATA2         PIN_D6 
#define LCD_DATA3         PIN_D7 
#define LCD_RS_PIN        PIN_D1 
#define LCD_RW_PIN        PIN_D2 
#define LCD_ENABLE_PIN    PIN_D3

#include <my_lcd.c>

int opcion=0,bandera,cont=20,cont1_mem=100,cont1=9,des_lcd,valor=0,boton;
int16 dato=0,datoM=0,cu_mem=255,cu=72,T1=65285;
#bit op=bandera.0
#bit f=bandera.1
#bit g=bandera.2

#define size_opcion 4

#int_RB
void RB_isr() 
{op=1;
   switch (opcion) {

    case 1:
            if(!input(pin_B4))valor++;if(valor>100)valor=100;
            if(!input(pin_B5))if(valor>0)valor--;
            if(!input(pin_B7))opcion++;
            if(opcion>size_opcion)opcion=0;
           break;

    case 2:
            if(!input(pin_B7))opcion++;
            if(opcion>size_opcion)opcion=0;
           break;
            
     case 3:        
            if(!input(pin_B7))opcion++;
            if(!input(pin_B7)){enable_interrupts(INT_TIMER1);set_tris_c(0xF3);g=1;}
            if(opcion==4)output_bit(pin_E0,0);
            if(opcion>size_opcion)opcion=0;
           break;
     
     case 4:        
            if(!input(pin_B7))opcion++;
            if(!input(pin_B7)){disable_interrupts(INT_TIMER1);set_tris_c(0xF7);g=0;}
            if(opcion==5)output_bit(pin_E0,1);
            if(opcion>size_opcion)opcion=0;
           break;

     default:
            if(!input(pin_B6))opcion++;
            if(opcion>size_opcion)opcion=0;
            break;
   }
clear_interrupt(INT_RB);

}

#int_TIMER1
void TIMER1_sr() 
{
   set_timer1(T1);   //64555
   cont--;
   if(cont>cont1)set_tris_c(0xF7);else set_tris_c(0xF3);
   if(!cont)cont=20;
   clear_interrupt(INT_timer1);
}

#int_AD
void AD_isr() 
{
   if(datoM!=read_adc()){ 
      if(opcion==0)op=1;
      datoM=read_adc();}
   clear_interrupt(INT_AD);
}

void main()
{
   port_b_pullups(TRUE);
   setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_DIV_2);
   setup_psp(PSP_DISABLED);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
   setup_timer_2(T2_DIV_BY_1,166,1);
   setup_ccp1(CCP_PWM);
   set_pwm1_duty(512);
   set_tris_c(0xF7);
   enable_interrupts(INT_RB);
   disable_interrupts(INT_TIMER0);
   disable_interrupts(INT_TIMER1);
   disable_interrupts(INT_TIMER2);
   enable_interrupts(INT_AD);
   enable_interrupts(GLOBAL);
   lcd_init();
   delay_ms(500);
    op=1;g=0;f=0;
    cu=72;  //C.U. = 50%
    set_pwm2_duty(make8(cu,0));
    
    printf(LCD_PUTC, "\f");
         lcd_gotoxy(1,1);
         printf(LCD_PUTC, " O");
         delay_ms(100);
         lcd_gotoxy(3,1);
         printf(LCD_PUTC, "Z");
         delay_ms(100);
         lcd_gotoxy(4,1);
         printf(LCD_PUTC, "O");
         delay_ms(100);
         lcd_gotoxy(5,1);
         printf(LCD_PUTC, "M");
         delay_ms(100);
         lcd_gotoxy(6,1);
         printf(LCD_PUTC, "E");
         delay_ms(100);
         lcd_gotoxy(7,1);
         printf(LCD_PUTC, "D");
         delay_ms(100);
         lcd_gotoxy(8,1);
         printf(LCD_PUTC, "PLUS \n\r");
         delay_ms(800);
         for (des_lcd = 12; des_lcd >= 1; --des_lcd)
    {
         lcd_gotoxy(des_lcd,2);
         printf(LCD_PUTC, " Version 001.1 \n\r");
         delay_ms(200);
    }
         
         while (true)
  {
    
   delay_ms(300);
    while(true){
    //read_adc(ADC_START_ONLY);
    if(op){
      switch (opcion) {

    case 1:
           printf(LCD_PUTC, "\f");
           lcd_gotoxy(1,1);
           printf(LCD_PUTC, " Concentracion \n\r");
           lcd_gotoxy(1,2);
           printf(LCD_PUTC, " mg/L: %u\n\r",valor);
           op=0;
           break;

     case 2:  
           printf(LCD_PUTC, "\f");
           lcd_gotoxy(1,1);
           printf(LCD_PUTC, " Seleccione \n\r");      
           lcd_gotoxy(1,2);
           printf(LCD_PUTC, " xxxx mL/min \n\r");
           op=0;
           break;
            
     case 3:        
           printf(LCD_PUTC, "\f");
           lcd_gotoxy(1,1);
           printf(LCD_PUTC, " Presione OK \n\r");
           lcd_gotoxy(1,2);
           printf(LCD_PUTC, " para generar O3 \n\r");
           op=0;
           break;
           
      case 4:        
           printf(LCD_PUTC, "\f");
           lcd_gotoxy(1,1);
           printf(LCD_PUTC, " Presione OK \n\r");
           lcd_gotoxy(1,2);
           printf(LCD_PUTC, " para detener O3 \n\r");
           op=0;
           break;

     default:
         printf(LCD_PUTC, "\f");
         lcd_gotoxy(1,1);
         printf(LCD_PUTC, " Pulse MENU para \n\r");
         lcd_gotoxy(1,2);
         printf(LCD_PUTC, " selec. Conc. O3 \n\r");
         op=0;
         break;
      }
      op=0;
    }
    
  
  }
   
    } 
 }
 
Última edición por un moderador:
El problema es que mientras se está pulsando el botón, no se producen interrupciones por cambio de flanco, así que solo aparecen interrupciones en la rutina del temporizador.

Se podría colocar allí el control del caso del botón pulsado. Necesitaremos que una variable nos indique que un botón ha sido pulsado, y quizás otra variable (podría ser la misma) que nos indique desde cuándo. Si se cumplen las dos condiciones (pulsado y han pasado dos segundos), in/decrementamos en 10, y lo mostramos en el LCD.

Quizás el timer lo puedas asociar a una variable que haga de segundero.

Otra forma: cuando se pulsa el botón, ponemos una variable de segundero a 0, y otra variable que indique que hay un botón pulsado (dice cuál es). Entonces, si se entra en la rutina del timer y ha pasado un segundo, y hay un botón pulsado, incrementamos el segundero, y si es 2 o más, cambiamos el valor de la variable. Si el usuario suelta el botón, quita la variable de botón pulsado.
 
El problema es que mientras se está pulsando el botón, no se producen interrupciones por cambio de flanco, así que solo aparecen interrupciones en la rutina del temporizador.

Se podría colocar allí el control del caso del botón pulsado. Necesitaremos que una variable nos indique que un botón ha sido pulsado, y quizás otra variable (podría ser la misma) que nos indique desde cuándo. Si se cumplen las dos condiciones (pulsado y han pasado dos segundos), in/decrementamos en 10, y lo mostramos en el LCD.

Quizás el timer lo puedas asociar a una variable que haga de segundero.

Otra forma: cuando se pulsa el botón, ponemos una variable de segundero a 0, y otra variable que indique que hay un botón pulsado (dice cuál es). Entonces, si se entra en la rutina del timer y ha pasado un segundo, y hay un botón pulsado, incrementamos el segundero, y si es 2 o más, cambiamos el valor de la variable. Si el usuario suelta el botón, quita la variable de botón pulsado.

He probado unas cuantas variantes y he utilizado timers como comentas pero no actualizo la pantalla hasta que no levante el boton.
Lo que he pensado entonces es no activar las interrupciones del puerto B que es donde estan conectados los botones, sino entonces pregunatr por el estados de esos botones constantemente pero igula no me convence esa idea. Dime como lo ves. Gracias
 
implementa while y un loop que dentro de este sume una variable; Luego, al dejar de presionar el boton, while deja de funcionar y al salir de este loop, comparas ese resultado para sumar de a diez o de a uno ;)
Obviamente, dentro del while tendrias que imprimir el LCD...esta mas que claro esto :)
 
Última edición:
implementa while y un loop que dentro de este sume una variable; Luego, al dejar de presionar el boton, while deja de funcionar y al salir de este loop, comparas ese resultado para sumar de a diez o de a uno ;)
Obviamente, dentro del while tendrias que imprimir el LCD...esta mas que claro esto :)


Eso tendria que probarlo pero me parece que no me va a funcionar como quiero porque cuando entre en la interrupcion entro al while, al loop y realizo mi rutina pero no voy a escribir en pantalla hasta que no modifique la variable op y salga de todo lo antes escrito y para cuando escriba en pantalla voy a ver el incremento final y no el paulatino. Si tienes otra idea revisa mi codigo y ponme tu variante porque de verdad no lo veo. Gracias
 
ahora si vengo renovado

bueno primero vamos a tener un timer, despues hay que hacer una funcion que se llame soltar para mandar a 0 el boton soltado Y en el main preguntar sobre el estado del boton por poleo.

esto es mas o menos asi:

primero que nada hay que hacer un timer que haga una cuenta alrevez
mas o menos asi:



interrupcion()
{
decremento1--; //el contador decrementara
}


el contador inverso contara alrevez

ahora hay que hacer una funcion que limpie nuestro boton pisado mas o menos asi:

void soltar(void)
{
if (input(pin_a5))tecla_1 = 0; ///checamos que la tecla se soltó.
if (input(pin_a6))tecla_2 = 0;
}



y despues en el main hacemos el poleo haciendo preguntas si nuestro boton se piso o no

esto es asi:

void main()
{
while(1)
{

soltar();

if((!input(pin_a5))&&(!tecla_1) )//
{
if(decremento1<=1)
{
tecla_1 = 1;//no borrar





decremento1=valor;//no borrar
}

}
else{ decremento1=valor; }




}
}




cual es la idea de este raro algoritmo aun no lo vaz a entender pero con una brebe explicacion y varias leidas y probarlo veraz que es muy efectivo.

idea principal.

no podemos meter un while dentro del boton para evitar un rebote
¿por que?

por que atoramos el micro y si estamos leyendo el ADC o haciendo una matriz de displays este while dentro del antirrebote nos causara problemas.

este algoritmo hace lo siguiente

1.- en el timer hacemos una variable llamada drecremento1 que esta se decrementara TODO el tiempo
cada boton debera tener su propio decremento para un segundo boton se llamara la variable drecremento2 y el tercer boton decremento3 y asi sucesivamente.

2.-la funcion soltar() lo que hace es comportase como una bandera, que cuando se pisa el boton manda a 0. lo que hace es que una vez que pisas el boton se manda a 0 y como que lo bloquea momentaneamente para evitar el rebote, pero no es muy efectivo sin la ayuda del timer que acontinuación explicare.

3.-el decremento:
lo que hace esta variable llamada decremento sera decrementarse dentro del timer pero dentro del while hara un poleo en una pregunta IF con su respectivo ELSE.
que cuando se pisa el boton un determinado tiempo el y el drecremento sea 0 o menor a 0 el if sera valido y asi evitamos el rebote es algo parecido lo que se haria con un while anidado PERO sin atorar nuestro micro.

la idea es que cuando se pise el boton se decrementa llega a 0 o menos que 0 se cumple lo que haremos dentro de nuestro boton , si no pisamos el boton se carga con un valor muchisimo mayor a 0 y el decremento en realidad no decrementara mucho en el timer hasta que se se pise el boton.

asi se hace un anti rebote completamente funcional, efectivo, sin atorar microcontroladores , casi tan bueno como una interrupcion no es como una interrupcion por ser poleo y sin rebotes de ningun tipo cosa que otros algoritmos si llegan a fallar.

no es invento mio solo uni 2 algoritmos famosos el del decremento del timer y el del metodo de las banderas
 
Última edición:
Al final ya lo resolví.
Trabajé utilizando un timer y su interrupción y mandaba a escribir en pantalla dentro de esa misma rutina y como era la misma información que tenía cuando se empieza a pulsar los botones, pues no se nota diferencia alguna y se logra el objetivo.

El código me quedó de esta forma.
Código:
#include <ozomed16f.h>
#include <STDLIB.h>

#define USE_2LINE_LCD TRUE
#define LCD_DATA0         PIN_D4
#define LCD_DATA1         PIN_D5 
#define LCD_DATA2         PIN_D6 
#define LCD_DATA3         PIN_D7 
#define LCD_RS_PIN        PIN_D1 
#define LCD_RW_PIN        PIN_D2 
#define LCD_ENABLE_PIN    PIN_D3

#include <my_lcd.c>

int opcion=0,bandera,cont=20,cont1=9,des_lcd,valor=0;
int16 cu=72,T1=65285,botup=0,botdo=0;
#bit op=bandera.0
#bit f=bandera.1
#bit g=bandera.2

#define size_opcion 4

#int_RB
void RB_isr() 
{op=1;
   switch (opcion) {

    case 1:
            if(!input(pin_B4))valor++;
            if(!input(pin_B5))if(valor>0)valor--;
            if(!input(pin_B7))opcion++;
            delay_ms(200);
            if(valor>100)valor=100;
            if(opcion>size_opcion)opcion=0;
            if(!input(pin_B4)){enable_interrupts(INT_TIMER0);}
            if(!input(pin_B5)){enable_interrupts(INT_TIMER0);}
            break;

    case 2:
            if(!input(pin_B7))opcion++;
            if(opcion>size_opcion)opcion=0;
           break;
            
     case 3:        
            set_pwm2_duty(make8(cu,0));
            if(!input(pin_B7))opcion++;
            if(!input(pin_B7)){enable_interrupts(INT_TIMER1);set_tris_c(0xF3);g=1;}
            if(opcion==4)output_bit(pin_E0,0);
            if(opcion>size_opcion)opcion=0;
           break;
     
     case 4:        
            if(!input(pin_B7))opcion++;
            if(!input(pin_B7)){disable_interrupts(INT_TIMER1);set_tris_c(0xF7);g=0;}
            if(opcion==5)output_bit(pin_E0,1);
            if(opcion>size_opcion)opcion=0;
           break;

     default:
            if(!input(pin_B6))opcion++;
            if(opcion>size_opcion)opcion=0;
            break;
   }
clear_interrupt(INT_RB);

}
#int_TIMER0
void TIMER0_sr() 
{
   if(!input(pin_B4))botup++;
   if(!input(pin_B4))botup++;
   if(!input(pin_B4))botup++;else botup=0;
   if(botup>8000)valor=valor+3;
   if(valor>100)valor=100;
   if(!input(pin_B4))if(botup>8000)lcd_gotoxy(1,2);
   if(!input(pin_B4))if(botup>8000)printf(LCD_PUTC, " mg/L: %u\n\r",valor);
   if(botup>8000)botup=0;
   if(!input(pin_B5))botdo++;else botdo=0;
   if(botdo>1400)if(valor>0)valor--;
   if(!input(pin_B5))if(botdo>1400)printf(LCD_PUTC, "\f");
   if(!input(pin_B5))if(botdo>1400)lcd_gotoxy(1,1);
   if(!input(pin_B5))if(botdo>1400)printf(LCD_PUTC, " Concentracion \n\r");
   if(!input(pin_B5))if(botdo>1400)lcd_gotoxy(1,2);
   if(!input(pin_B5))if(botdo>1400)printf(LCD_PUTC, " mg/L: %u\n\r",valor);
   if(botdo>1400)botdo=0;
   clear_interrupt(INT_timer0);
}
#int_TIMER1
void TIMER1_sr() 
{
   set_timer1(T1);  
   cont--;
   if(cont>cont1)set_tris_c(0xF7);else set_tris_c(0xF3);
   if(!cont)cont=20;
   clear_interrupt(INT_timer1);
}

 void main()
{
   port_b_pullups(TRUE);
   setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_DIV_2);
   setup_psp(PSP_DISABLED);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
   setup_timer_2(T2_DIV_BY_1,166,1);
   setup_ccp1(CCP_PWM);
   set_pwm1_duty(512);
   set_tris_c(0xF7);
   enable_interrupts(INT_RB);
   disable_interrupts(INT_TIMER0);
   disable_interrupts(INT_TIMER1);
   disable_interrupts(INT_TIMER2);
   enable_interrupts(INT_AD);
   enable_interrupts(GLOBAL);
   lcd_init();
   delay_ms(500);
    op=1;g=0;f=0;
    cu=72;  //C.U. = 50%
    set_pwm2_duty(make8(cu,0));
    
    printf(LCD_PUTC, "\f");
         lcd_gotoxy(1,1);
         printf(LCD_PUTC, " O");
         delay_ms(100);
         lcd_gotoxy(3,1);
         printf(LCD_PUTC, "Z");
         delay_ms(100);
         lcd_gotoxy(4,1);
         printf(LCD_PUTC, "O");
         delay_ms(100);
         lcd_gotoxy(5,1);
         printf(LCD_PUTC, "M");
         delay_ms(100);
         lcd_gotoxy(6,1);
         printf(LCD_PUTC, "E");
         delay_ms(100);
         lcd_gotoxy(7,1);
         printf(LCD_PUTC, "D");
         delay_ms(100);
         lcd_gotoxy(8,1);
         printf(LCD_PUTC, "PLUS \n\r");
         delay_ms(800);
         for (des_lcd = 12; des_lcd >= 1; --des_lcd)
    {
         lcd_gotoxy(des_lcd,2);
         printf(LCD_PUTC, " Version 1.0 \n\r");
         delay_ms(200);
    }
         
         while (true)
  {
    
   delay_ms(300);
    while(true){
    if(op){
      switch (opcion) {

    case 1:
           printf(LCD_PUTC, "\f");
           lcd_gotoxy(1,1);
           printf(LCD_PUTC, " Concentracion \n\r");
           lcd_gotoxy(1,2);
           printf(LCD_PUTC, " mg/L: %u\n\r",valor);
           op=0;
           break;

     case 2:  
           printf(LCD_PUTC, "\f");
           lcd_gotoxy(1,1);
           printf(LCD_PUTC, " Seleccione \n\r");      
           lcd_gotoxy(1,2);
           printf(LCD_PUTC, " xxxx mL/min \n\r");
           op=0;
           break;
            
     case 3:        
           printf(LCD_PUTC, "\f");
           lcd_gotoxy(1,1);
           printf(LCD_PUTC, " Presione OK \n\r");
           lcd_gotoxy(1,2);
           printf(LCD_PUTC, " para generar O3 \n\r");
           op=0;
           break;
           
      case 4:        
           printf(LCD_PUTC, "\f");
           lcd_gotoxy(1,1);
           printf(LCD_PUTC, " Presione OK \n\r");
           lcd_gotoxy(1,2);
           printf(LCD_PUTC, " para detener O3 \n\r");
           op=0;
           break;

     default:
         printf(LCD_PUTC, "\f");
         lcd_gotoxy(1,1);
         printf(LCD_PUTC, " Pulse MENU para \n\r");
         lcd_gotoxy(1,2);
         printf(LCD_PUTC, " selec. Conc. O3 \n\r");
         op=0;
         break;
      }
      op=0;
    }
   }
  } 
 }
Gracias a todos por los aportes.
 
Última edición por un moderador:
Atrás
Arriba