So I'm turning to the forum here as my last hope.
I have been stuck on trying to debounce buttons for the better part of 3 days now and I simply can't figure out what is going wrong.
I would massively appreciate any insight that you might have into how to fix this issue.
Or if you have any thoughtful advice on how to debug that would be great as well, cause I can't even figure out where the fault lies.
The problem is that I get very sporadic double presses on my buttons, probably like once every 50 presses or so. There seems to be no way to replicate the problem. It seems to just be totally random.
I have rewritten the code about 3 times and simply can't figure out where my logic is going wrong.
Ok so heres the setup,
I have 6 buttons using 10k pull down resistors, the buttons are fed into a 4021 shift register. The remaining 2 inputs are tied to ground (via 10k resistors). I am polling the 4021 in the main loop of my program. It returns all the proper values. I have also connected the buttons I am using to an oscilloscope and looked at their "bounciness". There is almost no bounce. Occasionally a very little glitch but usually in the sub 1ms range. Based on all this I am pretty confident that there is no hardware issue with how the buttons are connected or being read.
Ok, on to the software - I need to be able to read single button presses and different combinations of buttons pressed at the same time. (I realize this isn't ideal, but I can't change it at this point due to the hardware being finalized).
The basic code logic is this:
poll the shift register
when the shift register returns a non zero value this means
a button is/was pressed and we store the initial value of that button and start a timer
(using a shift register so I get a binary value 1,2,4,8,16, etc…representing what button was pressed)
Wait until a certain amount of time has elapsed (to allow for additional buttons to be pressed)
When time has elapsed we reread the value of the shift register and "or" it with the previously stored button value.
(I am saving and "or" ing the initial button press so that the user does not need to hold a single button press down until the time elapses.)
return the value representing which buttons were pressed from the function.
Set a flag stating that we can't read any more button presses until the shift register value returns to 0
Set a timer saying we can't clear the flag until some time has elapsed (so we don't get bounces on button release.
and heres the code for the function.
Oh and one last note- the function does not return anything, it just changes the value of the "buttonsPressed" variable.
Sorry for the wall of text. :o
void readButtons(byte shiftRegButtonsVal)
{
//button variables
static boolean readOk; //have we met the criteria necessary before we can read the buttons agin?
static byte lastButton; //holds the previous button value to compare against and see when a change has occured
static unsigned long readTime; //the time that we will actually read the button values
static unsigned long upTime; //how long do the buttons need to be "unpressed" before we can read again
static unsigned int actionDelay = 90; //delay between first detection of a press and when we read the button values
static byte valueAtFirstPress; //Which button was initially pressed?
// 1
// is the button either not pressed or just shift?
if ((shiftRegButtonsVal == 0) && (millis() > upTime)) //the buttons have been released and we have waited 5ms before we can start reading again
{
readOk = true; //its ok to read again if the shift reg is returning 0 and enough time has elapsed
}
// 2
//this section sets the timer to start counting down the delay between firstpress and when we read the button state
//this will reset the timer any time a new buttonpress is registered...
if((shiftRegButtonsVal != prevButtons) && (shiftRegButtonsVal != 0) && (readOk == true)) //if we have a button value change and the change is not to nothing or shift
{
readTime = millis() + actionDelay; // set the startTime- start time is the time when we will actually read what buttons are pressed
valueAtFirstPress = shiftRegButtonsVal; //this will hold the value representing what button is pressed when we start the timer
}
// 3
//wait then check if multiple buttons are pressed
//if starttime is not 0 then we have pushed a button / and we wait for millis to get larger than our delay setting, and if the button pressed is not shift then we read the value of combined buttons
if((readTime != 0) && (millis() > readTime) && (readOk == true)) // is it the time to check for other (second/third) buttons yet? Only pressed buttons have a nonzero startime.
{
buttonsPressed = shiftRegButtonsVal | valueAtFirstPress; //wait the alloted time then check what buttons are pushed and output the or'd value
readTime = 0;
valueAtFirstPress = 0;
readOk = false; // we cant read the buttons again until we release the buttons back to 0 or shift
}
if ((shiftRegButtonsVal == 0) && (prevButtons != 0)) //if we just released the button, set the timer.
{
upTime = millis()+5;
}
prevButtons = shiftRegButtonsVal; //update buttons so we know if there is a change
}