¿Contador en ensamblaje, usando interrupción para evitar múltiples conteos con solo presionar?

1

Soy completamente nuevo en el ensamblaje, y debo desarrollar un contador utilizando PIC16F628A, un botón y una pantalla. Además, habrá un oscilador externo (555).

Hice algunos progresos en esto, pero creo que necesito ayuda de ustedes. Al principio hice una demora basada en decrementos para poder ver los números en la pantalla.

Mi problema ahora es que una vez que presiono el botón, necesito contar solo un número, independientemente de cuánto tiempo lo mantenga presionado. Algo así como, si cambia de estado, se incrementará 1. Creo que esto debe hacerse con interrupciones, supongo.

Ahora, ¿cuál es la mejor solución para mi problema? ¿Interrupción externa, por interrupción de estado? ¿Alguna otra cosa?

    
pregunta ERS

3 respuestas

1

Dado que no puede usar un temporizador (recopilado de los comentarios que ha realizado), necesita una rutina de demora adecuada para proporcionar un período de tiempo específico. Me gusta el período de \ $ 8 \: \ textrm {ms} \ $, de la experiencia anterior. Pero puedes usar cualquier período que consideres apropiado. Suponiendo que su procesador utiliza la tasa \ $ 4 \: \ textrm {MHz} \ $ calibrada de fábrica, el ciclo de instrucción será \ $ 1 \: \ mu \ textrm {s} \ $ y tomará \ $ 8,000 \ $ ciclos para inventa un \ $ 8 \: \ textrm {ms} \ $ period.

El código de retardo probablemente debería convertirse en una subrutina, para evitar tener que replicarlo una y otra vez.

DELAY8MS    MOVLW   0x3E
            MOVWF   DLO
            MOVLW   0x07
            MOVWF   DHI
            DECFSZ  DLO, F
            GOTO    $+2
            DECFSZ  DHI, F
            GOTO    $-3
            NOP
            GOTO    $+1
            RETURN

El tiempo total ocupado por la rutina anterior se puede calcular de la siguiente manera:

$$ t = 5 \ cdot \ left [D_ {LO} + 2 + 256 \ cdot \ left (D_ {HI} -1 \ right) \ right] $$

donde \ $ 1 \ le D_ {LO} \ le 256 \ $ y \ $ 1 \ le D_ {HI} \ le 256 \ $, con 0 interpretado como 256. Las instrucciones CALL y RETURN toman 2 ciclos cada una y El código anterior tiene todo eso en cuenta. Llamarlo debe tomar exactamente \ $ 8,000 \ $ ciclos y, en \ $ 4 \: \ textrm {MHz} \ $ esto significa \ $ 8 \: \ textrm {ms} \ $.

Tendrá que crear esas dos variables, \ $ D_ {LO} \ $ y \ $ D_ {HI} \ $ en algún lugar. Eso se puede hacer así, creo:

            CBLOCK
DLO
DHI
            ENDC

Hay, por supuesto, otras formas. Y puede agregar una dirección absoluta a la línea CBLOCK si desea colocar el bloque en un lugar específico.

Ahora que tiene una rutina de demora, puede continuar con el siguiente paso. Necesitas dos nuevas rutinas. Una que se retrasa repetidamente hasta que el botón se active y otra que se retrasa hasta que el botón se desactiva. El anuncio se incluye aquí:

ACTIVE      CALL    DELAY8MS
            BTFSC   PORTx, PINy
            GOTO    ACTIVE
            CALL    DELAY8MS
            BTFSC   PORTx, PINy
            GOTO    ACTIVE
            RETURN

INACTIVE    CALL    DELAY8MS
            BTFSS   PORTx, PINy
            GOTO    INACTIVE
            CALL    DELAY8MS
            BTFSS   PORTx, PINy
            GOTO    INACTIVE
            RETURN

No sé su puerto o número de pin, así que simplemente puse los valores "ficticios" allí. Necesitas reemplazarlos, correctamente. Las dos rutinas anteriores suponen que 0 está activo y 1 está inactivo.

Ahora puedes escribir tu código principal:

MAIN        ; <code to reset your counter value>
            GOTO    LOOP_NXT
LOOP        CALL    ACTIVE
            ; <code to increment your counter value>
LOOP_NXT    ; <code to display your counter value>
            CALL    INACTIVE
            GOTO    LOOP

El código anterior restablece el valor de su contador a lo que quiera comenzar y luego salta al bucle donde muestra el valor y espera a que el botón se vuelva inactivo. El efecto aquí es que si inicia su código con el botón presionado (no debería ser, pero ¿qué pasa si lo es?), Entonces el código aún reiniciará el contador y lo mostrará ... pero esperará hasta que suelte antes de continuar. Así que tienes que dejar el interruptor.

Luego, una vez que esto ha sucedido, el bucle básico solo espera un estado activo de rebote del conmutador. Cuando ve eso, incrementa el contador inmediatamente (en la prensa, no en el lanzamiento) pero luego espera a que se suelte el botón antes de continuar, nuevamente.

Eso es todo. Aún necesita escribir el código apropiado para el contador y la pantalla. Pero eso da a entender la idea del resto.

    
respondido por el jonk
2

Necesitas "rebotar" tu interruptor. Esto se puede hacer en el hardware agregando un pequeño capacitor a través de la entrada o en el software al verificar que el interruptor mantiene su estado durante el tiempo suficiente.

  

Ahora el otro problema es que necesito agregar el valor de 1 una vez y solo una vez mientras presiono el botón. Imagínese el botón presionado por 1 minuto y no debe agregarse más que el incremento inicial de 1.

Ahora necesitas un software "one-shot". Debe recordar si se presionó el botón la última vez que miró.

// Pseudo code
if(button) {               // Button was pressed
  if(!buttonMemory) {      // Button was off last time we looked.
    counter ++;            // Increment the counter
    buttonMemory = true;   // Remember the button was pressed.
  }
} else {
  buttonMemory = false;    // Cancel the memory.
}
    
respondido por el Transistor
1

Hay varios problemas. Según su vaga descripción, parece que desea medir el tiempo que se presiona un botón. Tiene dos problemas: rebotar el botón y medir el tiempo que el botón de rebote está inactivo.

Haría ambas cosas en una interrupción periódica de 1 ms. Acostúmbrate a hacer eso. Un reloj de 1 ms es útil para muchas cosas.

En este procesador en particular, el temporizador 2 con su registro de período incorporado es adecuado para esto. Configúrelo para causar una interrupción cada 1 ms (velocidad de 1 kHz).

En la rutina de interrupción, primero debes rebotar el botón. Me gusta usar 50 ms como el periodo de rebote. La mayoría de los botones rebotan durante unos 10 ms, pero he visto algunos que rebotan cerca de 50 ms. Además, ese es aproximadamente el tiempo máximo que los humanos no notan como un retraso, por lo que no hay daño a los 50 ms en la experiencia del usuario.

Mantenga una bandera que sea el estado oficial de rebote del botón. Cuando el estado instantáneo del botón es el mismo, reinicie el contador de rebote a 50. cuando los estados instantáneos y rebotados del botón difieran, disminuya el contador. Cuando el contador llegue a 0, cambie el estado abonado al estado actual del botón y restablezca el contador a 50.

Ahora que tiene el estado de rebote del botón, simplemente cuente cada vez que presione el botón y no haga nada cuando se suelte. En el caso especial de una transición de abajo a arriba, guarde el valor del contador y establezca una bandera para el código de primer plano que indique que una nueva longitud medida está disponible. Borrar el contador.

Este contador deberá tener varios bytes de longitud. Un solo byte solo puede contar hasta 255, que es aproximadamente ¼ de segundo. Con dos bytes, el botón se puede mantener presionado durante un poco más de un minuto. Eso podría ser lo suficientemente bueno. Solo para estar seguro, solo debes incrementar el contador cuando aún no está al máximo. De esta forma, al presionar un botón largo solo se recortan los valores al valor máximo representable.     

respondido por el Olin Lathrop

Lea otras preguntas en las etiquetas