Estoy recableando un órgano viejo y barato para digital y quiero usar registros de desplazamiento para leer todas las teclas. ¿Es posible desbordar las teclas después de los registros de desplazamiento, o tengo que hacerlo antes? En cualquier caso, ¿hay algo que deba tener en cuenta?
Respuestas
¿Demasiados anuncios?Es perfectamente legítimo hacer un estroboscopio de todos los estados actuales de las teclas en una serie de registros de desplazamiento y llevar esos estados al ámbito del software. Todo lo que tienes que hacer es muestrear los estados clave de forma periódica y en cada muestra hacer algo de "matemática de bits vertical" en el flujo de bits actual en comparación con la copia anterior del flujo de bits.
Lo bueno de este enfoque es que puedes operar en los vectores de todos los datos del 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 utilizado este enfoque en una serie de productos comerciales a lo largo de los años y he sido capaz de soportar un gran número 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 fueron recogidas de seis pines que vienen de un puerto. Sin embargo, puede ser extendido a un vector tan amplio como necesites con sucesivos bytes de datos provenientes de tus registros de desplazamiento.
Primero algunas definiciones de un archivo de cabecera. Tenga en cuenta que este esquema de filtro de entrada fue diseñado para ser llamado una vez cada 10 mseg con cuatro periodos de muestreo para proporcionar el rebote del interruptor en un periodo de 40 mseg.
#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 */
A continuación se muestra 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 llamada una vez cada 10 mseg desde una interrupción del temporizador para obtener los datos de entrada actuales y procesarlos a través de los vectores de bits utilizando la matemática de bits verticales.
/*
**
** 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 que pueden ser llamadas por el código de la línea principal para ver cuál es el estado actual de cualquier tecla.
/*
**
** 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);
}
Voy a suponer que están leyendo todas las teclas alimentándolas en paralelo en los registros de desplazamiento, y luego las están sincronizando.
Es necesario eliminar el rebote de los registros de desplazamiento, de lo contrario, si hay ruido de una tecla, el estado incorrecto podría ser bloqueado y luego desplazado.
Hay muchos circuitos para desconectar interruptores; éste se considera uno de los mejores:
Para más información, véase " Guía para el desguace ".
Si todos los eventos de rebote de las teclas ocurren rápidamente, no hay nada malo en alimentar los interruptores de las teclas directamente a los registros de desplazamiento y dejar que el software se encargue de ellos. De hecho, en los casos en los que los eventos de rebote de las teclas sean más cortos que la resolución de tiempo mínima de interés, el sondeo lento se encargará del rebote de las teclas "automáticamente" [si una tecla se pulsa justo antes de un escaneo, pero el rebote de las teclas hace que se lea como no pulsada, simplemente se percibirá como que se ha pulsado un ciclo de sondeo más tarde de lo que realmente fue]. Para un órgano, es probablemente deseable evitar la cuantización de los eventos de las teclas de forma demasiado gruesa, por lo que el "procesamiento de bits vertical" como se describe en la otra respuesta puede ser bueno.
Sin embargo, algo como un teclado de órgano puede tener problemas de rebote del interruptor que no puede se resuelva puramente en el software. La dificultad radica en que algunos interruptores pueden tener una resistencia que varía irregularmente con la posición; si se empuja con suficiente lentitud, este proceso podría extenderse durante un tiempo arbitrariamente largo. Si se utilizan resistencias de pull-up de tamaño adecuado, este problema puede resolverse introduciendo los interruptores en tampones de disparo Schmidt antes de los registros de desplazamiento. Si se utiliza este enfoque, puede ser bueno arreglar las cosas de manera que cuando se maneja con VDD completa los pull-ups sean un poco más "rígidos" de lo deseado, pero en lugar de conectarlos a VDD conectarlos a un voltaje ajustable.
Alternativamente, dependiendo del tipo de órgano del que provenga el teclado, puede ser deseable alimentar los interruptores desde una fuente de alimentación 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 Schmidt (uno necesitaría un pulldown pasivo en ese caso). Por lo que tengo entendido, algunos interruptores de aleación de plata pueden empañarse y tener una resistencia significativa, pero la aplicación de un voltaje suficiente para forzar la corriente a través de ellos ayudará a romper el empañamiento. Estos interruptores pueden funcionar inicialmente bien cuando se conectan a la lógica de bajo voltaje/baja corriente, pero el rendimiento puede degradarse con el tiempo. Algo como un teclado electroneumático de órgano de tubos conmutaría a 24 voltios más o menos con una corriente significativa; un teclado de órgano electrónico Conn Minuet (basado en tubos de vacío) conmutaría a menos corriente, pero a 90 voltios.