Incremento Contador 0-9 a cada pulsación

Hola,
resulta que estoy haciendo un programa con un display de 7 segmentos Ánodo Común que incrementa en 1 el display de 0 a 9 a cada activación de un pulsador.

La pretensión del programa no es otra que la de aprender, habiendo realizado ya un contador de 0-9 he decidido hacerlo añadiendo el incremento por pulsador en RA0 y experimentando con el comando "for".

El problema está en que no consigo que funcione; el contador se limita a ir aumentando de 0-9 (y vuelta a empezar) cada 150 ms que es el retardo que uso para evitar rebotes de pulsación.

Si activo el pulsador se congela en el número de ese instante y al desactivarlo vuelve a 0 y sigue contando.

Consigo el mismo efecto con el comando "while".

No importa si cambio la condición de activación del pulsador de "0" a "1" lógico, parece que solo atiende si detecta cambio en RBA0.

Yo quiero que sólo se incremente el display a cada pulsación visualizando el número actual hasta que no haya nueva pulsación.

El compilador usado es el XC8.

Adjunto código:

PHP:
/* 
 * File:   main.c
 * Author: nimio
 *
 * 16F84A
 *
 * Display 7 segmentos Ánodo Común.
 * Contador de 0-9.
 * Incrementará a cada activación de un pulsador
 * en RA0
 *
 * Created on 9 de noviembre de 2012, 2:23
 */

#include <xc.h>

#define _XTAL_FREQ 4000000

#pragma config FOSC=XT, WDTE=OFF, PWRTE=ON, CP=OFF


void main (void) {

    int puls; // Variable para contar pulsaciones del pulsador.

    // Definición de tabla en binario para display Ánodo Común.

    int tabla_disp [10]={0b11000000, 0b11111001, 0b10100100, 0b10110000, 0b10011001, 0b10010010, 0b10000010, 0b11111000, 0b10000000, 0b10011000};
    
    
    TRISA=1; // Puerto A como entrada.
    TRISB=0; // Puerto B como salida.
    
    for (puls=0; RA0=0; puls++) { // for (inicializar variable; condición; modificar variable;)
           
           __delay_ms(150); // Antirrebote.
           PORTB=tabla_disp [puls];
           
           if (puls==9)
               puls=-1;
           }
    if (RA0=1)
        PORTB=tabla_disp [puls];
    
            //PORTB=tabla_disp [puls];
           }

Decir que me interesa hacerlo con el comando "for" si es posible.

A ver si alguien puede decirme que tengo mal o como habría que hacerlo.

Saludos.
 
hola,

aunque no es muy comun, puedes cambiar tu instruccion "for" por algo asi:

PHP:
for (puls = 0; ;){
      if (RA0){
          __delay_ms(150);
          if (RA0)
              puls++;
      }
      if (puls > 9) puls = 0;
      PORTB=tabla_disp [puls];
}

saludos
 
Hola carferper,
muchísimas gracias, va bien tal como dices.

Jugaré un poco con el código a ver si consigo alguna otra forma, es que quería aprovechar los 3 espacios que ofrece la instrucción "for" y no había manera.
El código funciona pero si mantengo accionado el pulsador me hace la cuenta continua.

Al menos ahora va porque me había acercado mucho pero sin llegar.

Muy agradecido.

Saludos!
 
El código funciona pero si mantengo accionado el pulsador me hace la cuenta continua.

Puedes eliminar ese efecto modificando un poco tu codigo:
PHP:
#include <xc.h>
#include <stdint.h>          // necesario para variables tipo uint8_t
#define _XTAL_FREQ 4000000 
#pragma config FOSC=XT, WDTE=OFF, PWRTE=ON, CP=OFF 
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))

bit estado_boton = 0b0;      // usado para control de rebotes
uint8_t puls = 0;            // contador que indica digito a visualizar
uint8_t tabla_disp [10] = {  // bits para anodo comun
    //hgfedcba
    0b11000000,   // 0
    0b11111001,   // 1
    0b10100100,   // 2
    0b10110000,   // 3
    0b10011001,   // 4
    0b10010010,   // 5
    0b10000010,   // 6
    0b11111000,   // 7
    0b10000000,   // 8
    0b10011000};  // 9

void main (void) { 
    TRISA = 0x01;        // RA0 como entrada
    PORTB = 0b11000000;  // inicia con contador en cero
    TRISB = 0x00;        // PORTB como salida
    while (1){
        if (RA0){             // detecta si el pulsador ha sido presionado
            __delay_ms(50);                  // espera por rebotes
            if (RA0) estado_boton = 0b1;     // cambia estado del boton
        }
        if (!RA0){            // detecta si el boton ha sido soltado
            __delay_ms(50);                  // espera por rebotes
            if (!RA0 & estado_boton){        // comprueba estado del boton
                puls++;                      // incrementa el contador
                estado_boton = 0b0;          // reinicia estado del boton
            }
        }
        if (puls > 9) puls = 0;    // reinicializa contador cuando es > 9
        PORTB=tabla_disp [puls];   // muestra digito en display
    }
}

Saludos
 
La sugerencia de tu código me ha ayudado a conseguirlo, por lo que veo no iba muy desencaminado.

Unas dudas:

Podrías por favor explicarme esta instrucción?

PHP:
bit estado_boton = 0b0;

al igual que #byte no tengo muy claro para que se suele usar. Yo entiendo que lo defines como bit para indicar que el valor en esta variable será de 1 bit y así ahorrar memoria, en ese caso sería lo mismo que definirlo como "short"?.

Cuando se pone "RA0" sin más... es equivalente a "RA0=1"? y "!RA0" que significa?

Gracias por el interés.

Saludos.
 
He estado intentando añadir un segundo pulsador para poder tanto incrementar como decrementar los dígitos del display pero no lo consigo.

Adjunto código por si alguien pudiera ver donde me equivoco.


PHP:
/*
 * File:   main.c
 * Author: nimio
 *
 * 16F84A
 *
 * Display 7 segmentos Ánodo Común.
 * Contador de 0-9 y 9-0.
 * Incrementará a cada activación de un pulsador en RA0.
 * Decrementará a cada activación de otro pulsador en RA1.
 *
 *Aunque se mantenga activo el pulsador no aumentará ni disminuirá el dígito mostrado.
 *
 *
 *
 * Created on 9 de noviembre de 2012, 2:23
 */

#include <xc.h>

#define _XTAL_FREQ 4000000

#pragma config FOSC=XT, WDTE=OFF, PWRTE=ON, CP=OFF


void main (void) {

                 
    int puls=0; // Variable para contar pulsaciones del pulsador e incrementar display.

    //int flpuls=0; // Flag indicador de que el pulsador se ha activado. Como int ocupa 8 bits.
    short flpuls=0; // Flag indicador de que el pulsador se ha activado.
                     // no se si es lo mismo que poner "bit flpuls=0b0;"
                     // para usar una variable de sólo 1 bit.
    short flpuls2=0; // Flag pulsador decremento.

    // Definición de tabla en binario en lugar de Hexadecimal.

    int tabla_disp [10]={0b11000000, 0b11111001, 0b10100100, 0b10110000, 0b10011001, 0b10010010, 0b10000010, 0b11111000, 0b10000000, 0b10011000};


    TRISA=1; // Puerto A como entrada.
    TRISB=0; // Puerto B como salida.

    while (1) {

        if (RA0=0) {

            __delay_ms(150);

            if (RA0=0)
                flpuls=1; // Si se ha pulsado el botón el indicador de pulsador se pone a "1".

            if (!RA0 & flpuls==1) { // Si el botón pulsado se ha soltado y el flag indicador está a "1"...
                PORTB=tabla_disp [puls];
                flpuls=0;
                puls++;
            }
       

        if (puls>9)
                puls=0; // Se inicializa puls a "0";
        }
        //PORTB=tabla_disp [puls];

        if (RA1=0) {

            __delay_ms(150);

            if (RA1=0)
                flpuls2=1;

            if (!RA1 & flpuls2==1) {
                puls--;
                PORTB=tabla_disp [puls];
                flpuls2=0;
                
            }
       
        if (puls<0)
                puls=9; // Se inicializa puls a "9";
        }
        PORTB=tabla_disp [puls];

    }

Saludos
 
Última edición:
hola, modifique el programa que puse antes para dos botones, asi puedes tener una guia.

PHP:
#include <xc.h>
#include <stdint.h>          // necesario para variables tipo uint8_t
#define _XTAL_FREQ 4000000 
#pragma config FOSC=XT, WDTE=OFF, PWRTE=ON, CP=OFF 
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))

bit estado_boton_0 = 0b0;    // usado para control de rebotes
bit estado_boton_1 = 0b0;    // usado para control de rebotes
int8_t puls = 0;            // contador que indica digito a visualizar
uint8_t tabla_disp [10] = {  // bits para anodo comun
    //hgfedcba
    0b11000000,   // 0
    0b11111001,   // 1
    0b10100100,   // 2
    0b10110000,   // 3
    0b10011001,   // 4
    0b10010010,   // 5
    0b10000010,   // 6
    0b11111000,   // 7
    0b10000000,   // 8
    0b10011000};  // 9

void main (void) { 
    TRISA = 0x01;        // RA0 como entrada
    PORTB = 0b11000000;  // inicia con contador en cero
    TRISB = 0x00;        // PORTB como salida
    while (1){
        PORTB=tabla_disp [puls];   // muestra digito en display
        if (RA0 | RA1){             // detecta si el pulsador ha sido presionado
            __delay_ms(50);                  // espera por rebotes
            if (RA0) estado_boton_0 = 0b1;   // cambia estado del boton
            if (RA1) estado_boton_1 = 0b1;   // cambia estado del boton
        }
        if (!RA0){            // detecta si el boton ha sido soltado
            __delay_ms(50);                  // espera por rebotes
            if (!RA0 & estado_boton_0){      // comprueba estado del boton
                puls++;                      // incrementa el contador
                estado_boton_0 = 0b0;        // reinicia estado del boton
            }
        }
        if (RA1 == 0){            // detecta si el boton ha sido soltado
            __delay_ms(50);                  // espera por rebotes
            if (!RA1 & estado_boton_1){      // comprueba estado del boton
                puls--;                      // decrementa el contador
                estado_boton_1 = 0b0;        // reinicia estado del boton
            }
        }
        if (puls > 9) puls = 0;
        if (puls < 0) puls = 9;
    }
}

Con respecto a tu pregunta, efectivamente !RA0 equivale a RA0 == 0.
"bit" es un tipo de variable que generalmente se usa para "flags", por ejemplo estados TRUE, FALSE o ENCENDIDO, APAGADO, etc.

Saludos
 
Aún no me he puesto con tu código, en breve lo implementaré. Pero permíteme una vez más unas consultas:

Podrías decirme en que casos se puede usar por ejemplo "!RA1" en lugar de "RA1=0"?
Probando con mi código he visto que no puedo hacer la sustitución en todos los casos y me ha extrañado porque en teoría son equivalentes.

Y en que casos se usa "==" en lugar de "="?
En el manual del compilador XC8 explica que "==" compara y "=" puede asignar equivalencia entre variables por así decirlo. Pero he visto que no puedo sustituir en los "if" todos los "=" por "==".

Si hago dichas modificiaciones al compilar me sale OK pero el circuito no reacciona correctamente.

Una vez más gracias por tu paciencia.

Saludos.
 
Como mencionas "=" y "==" son diferentes y no se pueden intercambiar.
El primero asigna un valor (lado derecho) a una variable (lado izquierdo). El segundo compara dos cantidads y genera unicamente dos posibles resutados: "1" logico (o TRUE) si estos son iguales o "0" (o FALSE) si son diferentes.

"!" es el operador NOT lo que significa que "!RA0" sera verdardero o "1" si RA0 es igual a cero por lo que esta sentencia es equivalente a escribir "RA0 == 0". En general no es conveniente usar codigo de esta manera excepto en ejemplos sensillos. Es mejor usar etiquetas, por ejemplo se puede definir:

#define Boton PORTAbits.RA0
#define PRESIONADO 0b0
#define NO_PRESIONADO (!PRESIONADO)

y despues en el programa se puede usar por ejemplo:

if (Boton == PRESIONADO)

o similarmente:

if (Boton == NO_PRESIONADO)

Asi, si por algun motivo se requiere cambiar el hardware (como conectar el pulsador a otro pin o cambiar de resistencias pull-up a pull-down), lo unico que se debe cambiar son las dos primeras definiciones. Adicionalmente, el codigo resultante es mucho mas facil portar a otros micros.

Saludos
 
Hola de nuevo.

Me estoy peleando con el código y me estoy volviendo loco.

Adjunto el programa inicial de contador de 0-9 con incremento con pulsador como ejemplo:

Código:
/*
 * File:   main.c
 * Author: nimio
 *
 * 16F84A
 *
 * Display 7 segmentos Ánodo Común.
 * Contador de 0-9. 
 * Incrementará a cada activación de un pulsador
 * en RA0.
 *Aunque se mantenga activo el pulsador no aumentará el dígito mostrado.
 *
 *
 *
 * Created on 9 de noviembre de 2012, 2:23
 */

#include <xc.h>

#define _XTAL_FREQ 4000000

#pragma config FOSC=XT, WDTE=OFF, PWRTE=ON, CP=OFF


void main (void) {

                  // Inicializada a 0.
    int puls=0; // Variable para contar pulsaciones del pulsador e incrementar display.

    int flpuls=0; // Flag indicador de que el pulsador se ha activado.
    
                     

    int tabla_disp [10]={0b11000000, 0b11111001, 0b10100100, 0b10110000, 0b10011001, 0b10010010, 0b10000010, 0b11111000, 0b10000000, 0b10011000};

    TRISA=1; // Puerto A como entrada.
    TRISB=0; // Puerto B como salida.

    while (1) {
        
        if (RA0=0) {

            __delay_ms(150);

            if (RA0=0)
                flpuls=1; // Si se ha pulsado el botón el indicador de pulsador se pone a "1".
                
            if (!RA0 & flpuls==1) { // Si el botón pulsado se ha soltado y el flag indicador está a "1"...
                //PORTB=tabla_disp [puls];
                flpuls=0;
                puls++;
            }
        }
            if (puls>9)
                puls=0; // Se inicializa puls a "0";

        PORTB=tabla_disp [puls];

    }

}
Resulta que, como comentaba más arriba, si sustituyo los "RA0==0" por "!RA0" o si sustituyo los "RA0=0" por "RA0==0" en los "if" de los programas de este post, el programa no se comporta de igual manera y no lo entiendo de verdad, en teoría "!RA0" sería equivalente y "RA0==0" sería lo correcto.

El código adjunto es un mero ejemplo, tal como está funciona, el display va incrementando a cada pulsación pero si modifico el código según lo que sería correcto... ya no hace lo mismo.

Me desconcierta enormemente y no se donde está mi equivocación.

Me da apuro ser tan pesado pero espero que me puedas ayudar.

Tan sólo quiero entenderlo.

Saludos
 
Última edición por un moderador:
Hola.

"if (RA0 == 0)" es equivalente a "if (!RA0)", y seria igual a "if (1)" cuando en el pin RA0 esta un "0" logico. El compilador en ambos casos genera el mismo código:
...
BCF STATUS, 0x5
BTFSC PORTA, 0x0
GOTO ...

Esto es completamente diferente a escribir "if (RA0 = 0)", en este caso el compilador va a asignar al registro correspondiente a RA0 el valor de "0" lógico y realizar la instrucción "if" con cualquier cosa que este presente en los registros. Es decir es impredecible lo que va a ocurrir durante la ejecución del programa. En este caso el compilador genera:

...
BCF STATUS, 0x5
BCF PORTA, 0x0
BTFSS PORTA, 0x0
GOTO ...

Si observas en este caso existe la instruccion "BCF PORTA, 0x00" que efectivamente significa "RA0 = 0". La diferencia radica en que en el primer caso se genera la instruccion "BTFSC", mientras que en este ultimo "BTFSS".

Similarmente "if (RA0)" es equivalente a escribir "if (RA0 == 1)" lo que significa que seria "if (1)" cuando en el pin RA0 existe un "1" logico. El compilador en este caso produce:

...
BCF STATUS, 0x5
BTFSS PORTA, 0x0
GOTO ...

Si te fijas la instrucción "BTFSS" es igual a la del caso anterior, lo que significa que en tu ejemplo cuando usas "if (RA0=0)" el programa trabaje de manera similar a la forma correcta "if(RA0)" o "if (RA0 == 1)". Esta coincidencia es la que probablemente este causando tu confusión.

Lo importante es recordar que usando (==), el código generado es siempre predecible y robusto; y por tanto garantiza que el programa haga lo que queramos. En el caso (=) los programas pueden o no funcionar como se quiere, lo mas probable es que produzca código con errores o "bugs".

Puedes comparar el código en ensamblador generado por el compilador si revisas el archivo *.lst o si vas a Window -> Output -> Disassembly Listing File. También ayuda utilizar el simulador y ejecutar el programa paso a paso.

Este seria el modo "correcto" de tu ejemplo:

Código:
while (1) {                           
    // RA0 es normalmente "0" -> por la resistencia "pull-down"
    // este segmento comprueba si se a presionado el botón
    // lo que significa que RA0 a cambiado a "1"
    if (RA0 == 1){                    // o if(RA0), pero nunca if(RA0=0)
        __delay_ms(150);              //     espera por rebotes
        if (RA0 == 1)     // comprueba si todavía el botón esta presionado
           flpuls = 1;    // cambia el estado de la "flag" correspondiente
    }

    // este segmento se ejecuta unicamente cuando ha habido un cambio
    // en "flpuls" y el boton ya este levantado => "0" lógico en RA0
    if ((RA0 == 0) & (flpuls == 1)){
        flpuls = 0;        // resetea la bandera "flplus"
        puls++;            // incrementa el contador
    }

    if (puls > 9) puls = 0;
    PORTB = tabla_disp [puls];
}
Espero no haberte confundido aun mas

Saludos
 
Última edición por un moderador:
Hola carferper, ya está completamente solucionado, tenía algunos desbarajustes en el código jeje. Problemas de enfoque por así decirlo, supongo que apreciarás los cambios.

Lo adjunto con comentarios tal y como yo lo entiendo, y espero acertar esta vez. Es mi propia versión por así decirlo porque veo que tu usaste un "OR" en un "if".

Comentar que me desconcertaba el que dieras por sentado que al pulsar los botones se enviara un "1" a sus pins.
Los RA0 y RA1 están a 5vcc mediante resistencias de 10k (siempre a "1") y al pulsar se conectan a masa. Así deduzco que son pull-up y en tu "diseño" serían pull-down al estar el pulsador a 5v y los pins conectados a masa mediante resistencia.

Código:
/*
 * File:   main.c
 * Author: nimio
 *
 * 16F84A
 *
 * Display 7 segmentos Ánodo Común.
 * Contador de 0-9.
 * Incrementará a cada activación de un pulsador en RA0.
 * Decrementará a cada activación de otro pulsador en RA1.
 *
 *Aunque se mantenga activo el pulsador no aumentará ni disminuirá el dígito mostrado.
 *
 *
 *
 * Created on 9 de noviembre de 2012, 2:23
 */

#include <xc.h>

#define _XTAL_FREQ 4000000

#define bot1 PORTAbits.RA0  // Por lo general es mejor crear alias (etiquetas)
#define bot2 PORTAbits.RA1  // para hacer más portable el código entre plataformas
#define PUERTOB PORTB       // o por si hay que hacer cambios en el hardware. Tan sólo
                            // habría que cambiar las etiquetas y sería mucho más fácil.

#pragma config FOSC=XT, WDTE=OFF, PWRTE=ON, CP=OFF

bit flpuls=0b0;  // Creo que sería equivalente hacer #define flpuls 0b0
bit flpuls2=0b0; // teniendo en cuenta que su valor sería invariable de esta forma.

int puls=0; // Variable para contar pulsaciones del pulsador e incrementar o decrementar display.

int tabla_disp [10]={0b11000000, 0b11111001, 0b10100100, 0b10110000, 0b10011001, 0b10010010, 0b10000010, 0b11111000, 0b10000000, 0b10011000};

void main (void) {

    TRISA=1; // Puerto A como entrada.
    TRISB=0; // Puerto B como salida.

    while (1) {

        // BOTÓN 1:

            if (bot1) // Si bot1 es "1". Mientras no se pulse ("0") botón1 siempre será "1".
                flpuls=1; // Si no se ha pulsado el botón1 el indicador de botón1 (flpuls) se pone a "1".

            if (!bot1)
                __delay_ms(50);  // antirrebotes

            if (!bot1 & flpuls==1) { // Si bot1 se ha pulsado ("0") y flpuls se ha activado ("1")...
               
                flpuls=0;   // Se pone flag flpuls a "0".
                puls++;     // Se incrementa puntero puls.
            }
            
            if (puls>9) // Si puls es mayor de 9...
                puls=0; // Se inicializa puls a "0";

         //BOTÓN 2: LO MISMO QUE EL BOTÓN 1 PERO PARA EL BOTÓN 2 Y DECREMENTANDO.

            if (bot2)
                flpuls2=1;

            if (!bot2)
                __delay_ms(50);

            if (!bot2 & flpuls2==1) {
                
                flpuls2=0;
                puls--;
            }

            if (puls<0) // Si puls es menor de 0...
                puls=9; // Se inicializa puls a "9";
        
        PUERTOB=tabla_disp [puls]; // Muestra en el 7seg. el número según incremento o decremento.

    }

}
Interesante tu explicación y lo de Disassembling listing file, simulador no puedo porque uso mac y no encontré ninguno. El lenguaje ensamblador lo conozco muy vagamente de hace años.

Verás que he tratado de seguir tus consejos de crear "Alias" de los registros (espero hablar con propiedad) y usar "bit" para flags.

Si no entiendo mal... si hay que manipular/asignar valores no es conveniente usar los "nombres oficiales" de los registros por portabilidad/practicidad pero... supone algún problema desde la programación en si?

Entraría dentro de los buenos hábitos el declarar las variables globalmente como veo que haces, usar #include <stdint.h> para usar el tipo de definición de variables que usas y usar doble paréntesis si hay más de una condición?

No me queda más que estarte muy agradecido una vez más por tu tiempo y conocimientos.

Saludos.
 
Última edición por un moderador:
Atrás
Arriba