# Can I debounce after a shift register?

I am rewiring a cheap old organ for digital and want to use shift registers to read all the keys. Is it possible to debounce the keys after the shift registers, or do I need to do it before? Either way, is there anything I need to keep in mind?

• You effectively asked a few new questions. In general you should not do that, it makes a perfect answer to your original question seem sloppy for nor answering the new parts. Better open a new question, or ask a (small) follow-up in a comment to the answer. – Wouter van Ooijen Feb 24 '15 at 14:34
• Sorry, rolled back. People just wrote more general answers than I expected. Michael's answer gave me enough confidence to order the components, and they're cheap enough that I don't risk much by finding the rest of my answers via experimentation. – Karl Bielefeldt Feb 24 '15 at 14:47

It is perfectly legitimate to strobe all the key present states into a series of shift registers and bring those states into the software realm. All you need to do is to sample the key states on a periodic basis and at each sample do some "vertical bit math" on the current bit stream as compared to the previous copy of the bit stream.

The neat thing about this approach is that you can operate on the vectors of all the input stream data a byte at a time and effectively process the debounce and current state logic on eight keys at a time!

I have used this approach in a number of commercial products over the years and have been able to support large numbers of key inputs without consuming a high percentage of the processing bandwidth if the MCU.

To give an idea of how the vertical binary math works I will share some code here that processes a vector of input signals. In the example the inputs were gathered from six pins coming in from a port. However it can be extended to as wide of vector as you need with successive bytes of data coming in from your shift registers.

First some definitions from a header file. Note that this input filter scheme was designed to be called once each 10 msec with four sample periods to provide for switch debouncing over a 40 msec period.

#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 */


Here is the global data definition for the input debounce scanner.

/* 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 */


Next here is the routine called at startup to initialize the debounce filter arrays and current state vectors.

/*
**
** 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;
}


This is the routine called once each 10 msec from a timer interrupt to fetch the current input data and process it through the bit vectors using vertical bit math.

/*
**
** 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;
}


Finally here is a collection of subroutines that can be called by mainline code to see what the current status of any given key is.

/*
**
** 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);
}

• 40ms is getting to the edge of acceptable delays, though, from a playability standpoint. – Scott Seidman Feb 24 '15 at 16:39
• @ScottSeidman - The code I posted was a sample from some other application. One is free to set the sampling rate as fast as the residual bandwidth of the MCU can support and then set the debounce filter depth accordingly. Each type of switch and application will have its own set of unique requirements. One beauty of the software approach is how easily it can be adjusted to the requirements at hand. – Michael Karas Feb 24 '15 at 21:13

I am going to assume are reading all of the keys by feeding them in parallel into shift registers, and then clocking them out.

You need to debounce them as they are latched into the shift registers; otherwise if their is noise from a key the wrong state could be latched and then shifted out.

There are lots of circuits around for debouncing switches; this is considered one of the best: