¿Puedo rebotar después de un registro de turno?

3

Estoy reconectando un órgano viejo y barato para digital y quiero usar los registros de desplazamiento para leer todas las teclas. ¿Es posible deshacer las teclas después de los registros de turnos, o tengo que hacerlo antes? De cualquier manera, ¿hay algo que deba tener en cuenta?

    
pregunta Karl Bielefeldt

3 respuestas

5

Es perfectamente legítimo utilizar todos los estados clave presentes en una serie de registros de turnos y llevar esos estados al ámbito del software. Todo lo que necesita hacer es muestrear los estados clave de forma periódica y en cada muestra realizar algunas "matemáticas de bits verticales" en el flujo de bits actual en comparación con la copia anterior del flujo de bits.

¡Lo bueno de este enfoque es que puede operar en los vectores de todos los datos de flujo de entrada, un byte a la vez y procesar efectivamente la lógica de rebote y estado actual en ocho teclas a la vez!

He usado este enfoque en varios productos comerciales a lo largo de los años y he podido admitir grandes cantidades de entradas clave sin consumir un alto porcentaje del ancho de banda de procesamiento si la MCU.

Para dar una idea de cómo funciona la matemática binaria vertical, compartiré aquí un código que procesa un vector de señales de entrada. En el ejemplo, las entradas se recopilaron de seis pines procedentes de un puerto. Sin embargo, se puede extender a un vector tan amplio como sea necesario con bytes sucesivos de datos provenientes de sus registros de turnos.

Primero algunas definiciones de un archivo de encabezado. Tenga en cuenta que este esquema de filtro de entrada se diseñó para ser llamado una vez cada 10 ms con cuatro periodos de muestra para proporcionar el debouncing del conmutador en un periodo de 40 ms.

#ifndef _INPUTS_H
#define _INPUTS_H

/* setup the number of input poll samples to filter for stable state */
#define INPUT_POLL_COUNT    4

/* define the states of the INPUT bits in the input status byte */
/* the inputs from port 4 are gathered and shifted right two bits */
#define INP_1           0x01    /* P4.2 - input 1 */
#define INP_2           0x02    /* P4.3 - input 2 */
#define INP_3           0x04    /* P4.4 - input 3 */
#define INP_4           0x08    /* P4.5 - input 4 */
#define INP_5           0x10    /* P4.6 - input 5 */
#define INP_6           0x20    /* P4.7 - input 6 */

#define INP_CNT     6           /* number of logical inputs */

/* global data declarations */
extern unsigned char code inp_table[INP_CNT];
extern unsigned char xdata inp_filter[INPUT_POLL_COUNT];
extern unsigned char xdata inp_status;
extern unsigned char xdata inp_previous;
extern unsigned char xdata inp_true;
extern unsigned char xdata inp_false;

/* input function prototypes */
extern void inp_init(void);
extern void inp_scan(void);
extern unsigned char inp_state(unsigned char inp_bit);
extern void inp_clear(unsigned char inp_bit);
extern unsigned char inp_true_test(unsigned char inp_bit);
extern unsigned char inp_true_get(unsigned char inp_bit);
extern unsigned char inp_false_test(unsigned char inp_bit);
extern unsigned char inp_false_get(unsigned char inp_bit);

#endif /* _INPUTS_H */

Aquí está la definición de datos globales para el escáner de rebote de entrada.

/* array of defined INP acess masks ordered by index number */
unsigned char code inp_table[INP_CNT] = {
                                         INP_1,      /* P4.2 - input 1 / power LED */
                                         INP_2,      /* P4.3 - input 2 / green stat LED */
                                         INP_3,      /* P4.4 - input 3 / amber stat LED */
                                         INP_4,      /* P4.5 - input 4 / blue ID LED */
                                         INP_5,      /* P4.6 - input 5 / aux in A */
                                         INP_6       /* P4.7 - input 6 / aux in B */
                                        };

/* array of bit flags for inputs filter */
unsigned char xdata inp_filter[INPUT_POLL_COUNT];
unsigned char xdata inp_status;          /* current filtered input state */
unsigned char xdata inp_previous;        /* previous filtered input state */
unsigned char xdata inp_true;            /* saved input true transition */
unsigned char xdata inp_false;           /* saved input false transition */

La siguiente es la rutina llamada al inicio para inicializar las matrices de filtros de rebote y los vectores de estado actuales.

/*
**
** routine to initialize the inp scan port logic to the inactive
** state. 
**
*/

void inp_init(void)
{
    unsigned char i;

    for(i=0; i<INPUT_POLL_COUNT; i++)
    {
        inp_filter[i] = 0;
    }
    inp_status = 0;
    inp_previous = 0;
    inp_true = 0;
    inp_false = 0;
}

Esta es la rutina a la que se llama una vez cada 10 ms desde una interrupción del temporizador para obtener los datos de entrada actuales y procesarlos a través de los vectores de bits utilizando matemática de bits vertical.

/*
** 
** routine to poll the inp lines and filter the current state of inputs
** (note that this is called from the timer interrupt function)
**
*/

void inp_scan(void) using 1
{
    unsigned char inp_tmp;
    unsigned char inp_or;
    unsigned char inp_cur;
    unsigned char i;

    inp_cur = P4 >> 2;                      /* read inputs from P4.2 -> P4.7 */

    /* loop to shift filter up by one position and perform */
    /* bit wise equal comparison */
    inp_or = 0;                             /* this holds 1 for bits that are */  
    for(i=0; i<INPUT_POLL_COUNT; i++)       /* changing in span of filter table */
    {
        inp_or |= inp_cur ^ inp_filter[i];  /* adjacent pair != so set or */
        inp_tmp = inp_filter[i];            /* swap so to slide table up */
        inp_filter[i] = inp_cur;
        inp_cur = inp_tmp;
    }

    /* produce bit pattern for current stable input data */
    /* if a input goes true its previous false state data is */
    /* automatically cleared and as well if a input goes false */
    /* its previous true going status is cleared */
            /* no chg where toggles  */   /* mask present status where stable */
    inp_status = (inp_status & inp_or) | (inp_filter[0] & ~inp_or);

    inp_tmp = inp_status & (inp_status ^ inp_previous);
    inp_true |= inp_tmp;
    inp_false &= ~inp_tmp;

    inp_tmp = inp_previous & (inp_status ^ inp_previous);
    inp_false |= inp_tmp;
    inp_true &= ~inp_tmp;

    inp_previous = inp_status;
}

Finalmente, aquí hay una colección de subrutinas a las que puede llamar el código de la línea principal para ver cuál es el estado actual de cualquier clave dada.

/*
**
** function to return the current filtered and stable state
** of a specific inp input. returns 0 if the inp is
** inactive and 1 if the input is active. the input argument 
** is the bitmask for the requested inp.
**
*/

unsigned char inp_state(unsigned char inp_bit)
{
    if(inp_status & inp_bit)
    {
        return(1);
    }
    return(0);
}

/*
**
** function to flush the inp input
** true/false status for a bit and make that inp 
** look inactive. the input argument 
** is the bitmask for the requested inp.
**
*/

void inp_clear(unsigned char inp_bit)
{
    inp_true &= ~inp_bit;
    inp_false &= ~inp_bit;
}

/*
**
** function to check for specific inp status 
** indicating a queued TRUE transition of a inp. 
** if so then to return a 1 value, else
** return a 0 value. the input argument 
** is the bitmask for the requested inp.
**
*/

unsigned char inp_true_test(unsigned char inp_bit)
{
    if(inp_true & inp_bit)
    {
        return(1);
    }
    return(0);
}

/*
**
** function to get input status for a inp bit 
** indicating a queued TRUE transition of a inp 
** and then clear the queued true status. if the inp
** is queued true then return a 1 value, else return 
** a 0 value. the input argument is the bitmask 
** for the requested inp.
**
*/

unsigned char inp_true_get(unsigned char inp_bit)
{
    if(inp_true & inp_bit)
    {
        inp_true &= ~inp_bit;  /* clear the status */
        return(1);
    }
    return(0);
}

/*
**
** function to check for specific input status 
** indicating a queued FALSE transition of a  
** inp. if so then to return a 1 value, else 
** return a 0 value. the input argument is the 
** bitmask for the requested inp.
**
*/

unsigned char inp_false_test(unsigned char inp_bit)
{
    if(inp_false & inp_bit)
    {
        return(1);
    }
    return(0);
}

/*
**
** function to get inp status for a specified bit 
** indicating a queued FALSE transition of a inp
** and then clear the queued false status. if the input
** is queued false then this so then to return a 1 value,
** else to return a 0 value. the entry argument is a bit 
** number 0-7 to look at.
**
*/

unsigned char inp_false_get(unsigned char inp_bit)
{
    if(inp_false & inp_bit)
    {
        inp_false &= ~inp_bit;  /* clear the status */
        return(1);
    }
    return(0);
}
    
respondido por el Michael Karas
1

Voy a suponer que están leyendo todas las claves al introducirlas en paralelo en los registros de turnos y luego registrarlas.

Debes rebotarlas ya que están enganchadas en los registros de turnos; de lo contrario, si hay ruido de una tecla, el estado incorrecto podría bloquearse y luego cambiarse.

Hay una gran cantidad de circuitos para deshacer los interruptores; Este es considerado uno de los mejores:

Paraobtenermásinformación,consulte" Una guía para la denuncia ".

    
respondido por el tcrosley
1

Si todos los eventos de rebote de clave suceden rápidamente, no hay ningún daño en alimentar los interruptores clave directamente a los registros de turnos y dejar que el software se ocupe de ellos. De hecho, en los casos en que los eventos de cambio de conmutación serán más cortos que la resolución de interés mínima, el sondeo lento se encargará de que el rebote del interruptor se realice "automáticamente" [si se presiona una tecla justo antes de un escaneo, pero el rebote del interruptor hace que se lea si no se presiona, simplemente se percibirá como un ciclo de sondeo más tarde de lo que realmente fue]. Para un órgano, probablemente sea conveniente evitar la cuantización de eventos clave de manera demasiado amplia, por lo que el "procesamiento vertical de bits", como se describe en la otra respuesta, puede ser bueno.

Sin embargo, algo parecido a un teclado de órgano puede tener problemas de conmutación y rebote que no puede resolverse únicamente en el software. La dificultad es que algunos interruptores pueden tener una resistencia que varía irregularmente con la posición; si se lo presiona lo suficientemente lento, este proceso podría extenderse durante un tiempo arbitrariamente largo. Si se usan resistencias de pull-up de tamaño adecuado, este problema se puede resolver al alimentar los interruptores en los buffers de gatillo Schmidt antes de los registros de desplazamiento. Si se usa ese enfoque, puede ser bueno organizar las cosas de modo que cuando se manejen con VDD completo, los pull-ups sean un poco más "rígidos" de lo deseado, pero en lugar de conectarlos a VDD, conéctelos a un voltaje ajustable.

Alternativamente, dependiendo del tipo de órgano del que provenga el teclado, puede ser conveniente alimentar los interruptores desde una fuente de mayor voltaje, pasarlos a través de una resistencia y sujetarlos con un diodo a un voltaje que sea aceptable para el búfer de disparo de Schmidt (en ese caso, se necesitaría un despliegue pasivo). Por lo que entiendo, algunos interruptores de aleación de plata pueden deslustrarse para tener una resistencia significativa, pero la aplicación de suficiente voltaje para forzar la corriente a través de ellos ayudará a descomponer el deslustre. Dichos interruptores inicialmente pueden funcionar bien cuando están conectados a una lógica de bajo voltaje / baja corriente, pero el rendimiento puede disminuir con el tiempo. Algo así como un teclado de órgano de tubería electroneumático cambiaría 24 voltios aproximadamente con una corriente significativa; un teclado de órgano electrónico Conn Minuet (basado en tubo de vacío) cambiaría menos corriente, pero a 90 voltios.

    
respondido por el supercat

Lea otras preguntas en las etiquetas