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

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

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

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

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

¿Cómo se conecta el interruptor al pin? ¿Es para que obtenga un 1 cuando se presiona? ¿O un 0 cuando se presiona? También deduzco que no está permitido usar un temporizador para esto. ¿Es eso correcto?
Un cero, en realidad se exige que se utilicen botones desplegables.
Si puede probar el botón que se presiona, también puede probar cuándo se ha soltado el botón.
El problema no es probarlo estando o no estando presionado. El problema es hacerlo de manera que presionas el botón durante un minuto, se incrementa un número en la pantalla, uno y solo un número. La forma en que estaba haciendo esto, mientras mantenía presionado el botón, la pantalla estaba agregando números.
No hay razón para que tenga que ser una interrupción. ¿Por qué el programa no puede verificar que el botón esté presionado ahora y que no estaba presionado la última vez que el programa presionó el botón?

Respuestas (3)

Dado que no puede usar un temporizador (recopilado de los comentarios que ha realizado), necesita una rutina de retraso adecuada para proporcionar un período de tiempo específico. me gusta el periodo de 8 EM , por experiencia previa. Pero puede usar cualquier período que considere apropiado. Suponiendo que su procesador está utilizando el calibrado de fábrica 4 megahercio tasa, el ciclo de instrucción será 1 m s y tomará 8 , 000 ciclos para formar un 8 EM período.

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 como:

t = 5 [ D L O + 2 + 256 ( D H I 1 ) ]

dónde 1 D L O 256 y 1 D H I 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 debería tomar exactamente 8 , 000 ciclos y, en 4 megahercio esto significa 8 EM .

Tendrás que crear esas dos variables, D L O y D H I 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 algún lugar específico.

Ahora que tiene una rutina de retraso, puede continuar con el siguiente paso. Necesitas dos rutinas nuevas. Uno que retrasa repetidamente hasta que el botón se vuelve activo y otro que retrasa repetidamente hasta que el botón se vuelve inactivo. El antirrebote está incluido 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 número de puerto o pin, así que solo puse valores "ficticios" allí. Necesitas reemplazarlos, correctamente. Las dos rutinas anteriores asumen 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 estarlo, pero ¿y si lo está?), entonces el código restablecerá el contador y lo mostrará... pero esperará hasta que lo suelte. antes de continuar. Así que tienes que soltar el interruptor.

Luego, una vez que eso ha sucedido, el ciclo básico solo espera un estado activo de rebote del interruptor. Cuando ve eso, incrementa el contador inmediatamente (al presionar, no al soltar) pero luego espera a que se suelte el botón antes de continuar, nuevamente.

Eso es todo. Todavía necesita escribir el código apropiado para el contador y la pantalla. Pero eso transmite la idea al resto.

Señor no tengo palabras para agradecerle! ¡En realidad! Ya lo tenía funcionando con retrasos para encargarme del rebote de alguna manera, solo adapté todas las cosas a su código y ya está casi listo, solo estoy trabajando en el botón de reinicio que ahora no funciona (RA7), pero eso es un asunto del tiempo y analizando. ¡Muchas gracias! Salvaste mi día. ¡¡¡¡¡GRACIAS!!!!!
@ERS Me alegra saber que ayuda. Estúdialo un poco y asegúrate de seguir bien la lógica. Si no, no dude en hacer más preguntas. Además, no olvide seleccionar una respuesta si cree que tiene lo que necesita (y si cree que ha esperado lo suficiente para que otros también agreguen sus pensamientos).
Definitivamente lo estudiaré, ya que después de que el proyecto esté terminado, tendré que hacer un informe correcto sobre todos los aspectos del mismo. Ok, si tengo alguna duda, te contactaré aquí. gracias de nuevo
solo dos preguntas, alguna razon para repetir codigo en las rutinas INATIVA y ACTIVA? ¿Qué hace exactamente, por ejemplo GOTO $-3 ? ¿Eso menos 3? gracias de antemano
@ERS ACTIVO e INACTIVO no son el mismo código. Tenga en cuenta BTFSS y BTFSC. Y podría usar una etiqueta en la otra rutina con el presente de $-3, supongo. El $ significa "aquí" y el -3 significa tres palabras de instrucción al revés.
Hablaba del interior de cada uno, ACTIVO e INACTIVO. Hay 6 líneas de código, pero creo que podrían tener solo 3 ya que el código está repetido.
@ERS No. Eche un vistazo de cerca a las instrucciones Goto. Si aún no puede ver la razón por la que no puede acortarlos, supongo que agregaré una explicación detallada más bien larga.
Ya lo probé en el mundo real, y hasta ahora es increíble, pero necesito más ayuda y agradecería mucho su explicación detallada de todo. Algunos detalles: RB0 es la entrada desde el botón o 555, todos los demás puertos PORTB son salidas a la pantalla. RA0 se conecta a RB0 de los siguientes PIC (3 imágenes, 3 pantallas de 7 segundos, unidades, decenas, centenas) RA7 tendrá un botón de reinicio. Dentro de LOOP agregué un contador de decremento, dentro de LOOP_NXT el contador apuntará a una posición de una tabla para mostrar un número. Mi siguiente problema es el botón de reinicio que solo funciona mientras se presiona la entrada. ¡Gracias de nuevo!
Estaba releyendo mi última publicación, cuando digo que lo probé y funciona bien, es decir, lo probé con todo su código de ACTIVO e INACTIVO, no eliminé nada. Creo que estoy completamente perdido en lo que respecta al botón de reinicio, es decir, creo que con esas dos rutinas ahora todo depende de presionar el botón de entrada, ¿no? Saludos
@ERS No tengo una imagen completa de todo lo que está tratando de lograr. Ha mencionado un botón de reinicio, pero en realidad no tengo una especificación completa en este momento. Mencionaste que podría haber un 555 también. Esto se está convirtiendo en un blanco en movimiento y me he perdido un poco. Tendrá que ampliar mucho su pregunta, o escribir una nueva (mejor).
Lo siento, no soy un hablante nativo de inglés y tal vez mi escritura parezca un poco confusa. No estaba diciendo que mencionaras un botón de reinicio. Lo que estaba tratando de decir es que estoy tratando de agregar un botón de reinicio, y solo hace el reinicio solo cuando lo presiono al mismo tiempo que el botón de entrada, creo que tiene algo que ver con las rutinas ACTIVAS, INACTIVAS ¿No permite que un botón de PORTA funcione solo? ¿Es una posible causa? De nuevo, gracias por tu ayuda.
Lo siento, no vi tu respuesta, todo funciona bien cuando se trata de contar. Agregué un 555 para probar la pantalla, funciona bien con el sensor óptico, lo único que estoy atascado ahora es el botón de reinicio.
@ERS Tiene una ENORME limitación al no poder usar un temporizador. Las rutinas que proporcioné consumen totalmente su CPU para que SOLO pueda HACER UNA COSA: observe ese interruptor. Si tiene la intención de observar más de un interruptor y poder responder a ellos de forma independiente, entonces el diseño del código debe ser DIFERENTE. Peor aún, realmente hace que querer ese temporizador sea MUY importante. Se puede hacer de otras maneras. Pero ahora la máquina estatal se vuelve significativamente más compleja. Por eso se utilizan temporizadores.
@ERS Si escribe los objetivos completos (incluido el REINICIO, y CUALQUIER OTRO interruptor de entrada que crea que NECESITARÁ), intentaré un nuevo enfoque que podrá observar y usar hasta 8 interruptores a la vez SIN temporizador. Pero es una respuesta diferente. Así que debería ser una pregunta diferente.

Necesitas "rebotar" tu interruptor. Esto se puede hacer en el hardware agregando un pequeño condensador a través de la entrada o en el software al verificar que el interruptor mantenga 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. Imagine el botón presionado por 1 minuto y no debe agregar más que el incremento inicial de 1.

Ahora necesita 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.
}
Gracias por su ayuda, supongo que el problema de rebote está resuelto por ahora. Ahora, el otro problema es que necesito agregar el valor de 1 una vez y solo una vez mientras presiono el botón. Imagine el botón presionado por 1 minuto y no debe agregar más que el incremento inicial de 1. No sé si estoy escribiendo esto de manera confusa, el inglés no es mi idioma principal, lo siento.
Ver la actualización. Tu inglés es bastante claro.
Gracias de nuevo. Tiene mucho sentido en C, intentaré hacerlo en ensamblador.
@ERS Muchas formas de eliminar el rebote. En un microcontrolador más sofisticado, tendría interrupciones sensibles al borde y temporizadores independientes que puede usar para activar un tiempo muerto de NN milisegundos una vez que se activa la interrupción del botón. Obtener interrupción, establecer bandera. Temporizador de interrupción de incendios, despejar bandera. Y, por cierto, desea una resistencia y un condensador para un antirrebote de paso bajo adecuado.
@Barleyman: Todas las cosas buenas, pero el tipo recién está comenzando. Deje que domine y comprenda los conceptos básicos primero.
Tbh con el fondo de la electrónica (analógica), el negocio de sondear el estado de la señal en lugar de activar el borde me hace un poco incómodo. Edge + tiempo muerto parece más robusto y, francamente, menos trabajo. Por otra parte, siempre me molestó con las clases de software integradas básicas que harías bucles por retrasos, etc. Solo un perfeccionista, supongo.

Hay varios problemas. Parece por su vaga descripción que desea medir el tiempo que se presiona un botón. Tiene dos problemas, eliminar el rebote del botón y medir el tiempo que el botón bloqueado está presionado.

Haría ambas cosas en una interrupción periódica de 1 ms. Acostúmbrate a hacer eso. Un tic de 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 muy adecuado para esto. Configúrelo para que provoque una interrupción cada 1 ms (velocidad de 1 kHz).

En la rutina de interrupción, primero elimina el rebote del botón. Me gusta usar 50 ms como período de rebote. La mayoría de los botones rebotan durante unos 10 ms, pero he visto algunos que rebotan casi 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 50 ms para 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 sea el mismo, restablezca el contador de rebotes a 50. Cuando los estados instantáneo y de rebotes del botón difieran, disminuya el contador. Cuando el contador llegue a 0, cambie el estado de rebote 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 se presiona el botón y no haga nada cuando se suelta. 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 indica que hay disponible una nueva longitud medida. Limpia 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 debe incrementar el contador cuando aún no esté al máximo. De esa manera, una pulsación prolongada de un botón solo recorta el valor máximo representable.

Hola, antes que nada, gracias por tu respuesta. Estoy probando el rebote de un botón con la salida de un led. Hasta ahora, todo bien. El segundo problema, ¿debe resolverse con temporizador? Quiero decir, me dijeron que en este trabajo necesito evitar los temporizadores. ¿Puede ser algo así como mientras se presiona el botón, agregar valor uno y nada más? ¿Algún tipo de interrupción?
Agregar valor uno, solo una vez
Por cierto, el rebote será demasiado rápido para verlo en un LED.
Puede hacer un sistema de control integrado completo con un programador de tareas basado en un temporizador de latido del sistema. Me gusta usar dos temporizadores, uno para cosas de alto nivel con un período de alrededor de 1 ms (500 microsegundos, lo que sea) y otro con un período corto para métricas en tiempo real, análisis de rendimiento, etc.