Issues with HW debouncing a rotary encoder or SW issue - Page 1
Forum section: electronics / beginners

author: newtekuser
author_rank: Frequent Contributor
timestamp: July 22, 2023, 11:23:49 pm
content: With the attached design, I can get channel A and channel B de-bounced pretty nicely (using a 74HC14 Schmitt trigger and RC network), but I'm having issues with the de-bouncing the push button. The scope trace does show a pretty clean signal for most of times (signals for channel A and B always look great) with exception for rare occasions where I have almost 200mV spike on channel 3 (the push button).I thought maybe the push button on the rotary encoder was faulty but I get the same results using another discrete push button and I'm starting to think it's because my PIC16F887 is missing pulses because while the signal trace looks good it's still missing button pushes.I'm saying this because I only see this issue when I'm handing the rotary encoder and button pushes in a secondary loop (i.e.: second menu of a LCD) and always works when in the first menu.Is this because I'm using polling instead of interrupts?

author: newtekuser
author_rank: Frequent Contributor
timestamp: July 23, 2023, 05:29:51 pm
content: I do want to solve this via HW and not SW unless my approach can't work due to setup limitations (i.e.: PIC16F887 and rotary encoder with a push button + 16x2 LCD display)Basically I have the main while loop where I'm handling the encoder to increment/decrement the index of a menu and update the menu option displayed.Then I have a switch statement for the button press to handle when each of the item is selected. This is where things get nested, for each item that is selected I start a different while loop where I also check if the button is pressed to end the selection and exit out of the current menu option.Code:char *main_menu = {"opt1", "opt2"};int current_main_menu_index = 0;while(1){handle_encoder(&current_main_menu_index, MAIN_NUM_OPTIONS);LcdPrint(main_menu);if(Encoder_BTN){switch(current_main_menu_index){case 0:while(1){LCD_Print("opt1 selected");if (Encoder_BTN){LCD_Print("bye from opt1");break;}}break;case 1:while(1){LCD_Print("opt2 selected");if (Encoder_BTN){LCD_Print("bye from opt2");break;}}break;}}void handle_encoder(int *index, int limit){if (Encoder_CLK != position){if (Encoder_DT != position){ // CCWif (*index > 0) // do not go below menu items{__delay_ms(ENCODER_DELAY_MS);LCD_Cmd(LCD_CLEAR);(*index)--;}else*index = 0;}else{ // CWif (*index < (limit - 1)) // do not go beyond menu items{__delay_ms(ENCODER_DELAY_MS);LCD_Cmd(LCD_CLEAR);(*index)++;}else*index = 0;}}}

author: ledtester
author_rank: Super Contributor
timestamp: July 23, 2023, 11:14:49 pm
content: I see a problem with this code... if executing the outer while loop and Encoder_BTN becomes true, then the LCD will display "opt1 selected" immediately followed by "bye from opt1".You have to somehow clear Encoder_BTN so that a second press is required and this is going to be hard if Encoder_BTN is the GPIO pin itself.Also, I think you want LCD_print("opt1 selected") to be outside the inner while (1) loop.

author: newtekuser
author_rank: Frequent Contributor
timestamp: July 24, 2023, 12:59:52 am
content: Sorry, this was rough code from memory and not the actual code. I do not have access to the code atm I'm afraid. However, with the code that I'm using on the target device I don't have any issues displaying wrong text or navigating into the wrong menu item, just that the push button has trouble registering/ sometimes needs a few presses to register. This only happens in the inner loops and once I'm in the outer loop, it works fine again.The Encoder_BTN is actually connected to port RB2 and I did try to clear it after the button press but that didn't make any difference with the behavior.I tried stepping through the code with the debugger to watch the state of the button changing but mplab complains about HW breakpoints not being available.

author: EPAIII
author_rank: Super Contributor
timestamp: July 24, 2023, 10:49:08 am
content: OK, here go the assumptions. You say "rotary encoder". Are we to assume it is an OPTICAL ENCODER? And you say "push button". Are we to assume it is a mechanical switch and not another optical based part of what is apparently the same package in your schematic and description?If the answers to both those questions is yes, then I will remind you that optical and mechanical parts will have different bounce characteristics. Most optical encoders will have very little bounce to begin. While, most mechanical switches will have a lot of "bounce" problems. So your A and B channels may not even need any precautions to eliminate bounce while your C channel may need a lot more. You can not assume that a de-bounce circuit that works for one will automatically work for the other.Contact bounce is a real problem and there are many hardware circuits that have been used to cure it. For one thing, different logic families have different characteristics in that regard. I believe older CMOS chips are said to eat or decrease the high frequencies of the bounce waveform while TTL is said to be more sensitive to them. I am not sure about more modern IC types. Simply running the switch signal through one or more CMOS inverters can improve or even eliminate bounce. Of course your circuit can also work, but you may need to experiment with different resistor and capacitor values. And there are specific circuits that can eliminate it: some using standard ICs and some using ICs that are specifically made for that purpose.I suggest that you try an internet search for "push button debounce circuit". You will get a lot of ideas. Here is a start:https://www.google.com/search?client=firefox-b-1-d&q=push+button+debounce+circuitandhttps://circuitdigest.com/electronic-circuits/what-is-switch-bouncing-and-how-to-prevent-it-using-debounce-circuit

author: newtekuser
author_rank: Frequent Contributor
timestamp: July 25, 2023, 03:34:47 am
content: Quote from: EPAIII on July 24, 2023, 10:49:08 amI'm using a mechanical rotary encoder with built-in push button as per the previously attached schematic. Using a dedicated push button doesn't make any difference and I don't think the cause is the bounce since it fails to register button pushes even when there's a clean signal which is most of the time.I'm more interested to know if my approach to use polling to detect rotation and button pushes to navigate nested menus could be the culprit. Is the PIC16F887 too slow and could it be missing pulses?Does this type of UI have to be implemented using interrupts and if so, is this do-able with the PIC16F887 considering it only has one external interrupt pin, or do I need to add more push buttons? (I'm trying to implement the UI using just the rotary encoder and the integrated push button)

author: JustMeHere
author_rank: Frequent Contributor
timestamp: July 25, 2023, 04:52:37 am
content: I think what you're seeing on your scope is your probing technique and not bounce.

author: donlisms
author_rank: Frequent Contributor
timestamp: July 25, 2023, 06:16:44 am
content: Hm. To me, you seem pretty intelligent and are asking the some good questions. The answer to "Is polling too slow?" seems like it should be solvable. There ought to be a way to figure out what the timing is... in other words, this seems like a good little project to ramp up engineering skills, using nothing more than what you already have.I might be inclined to write some test code. For example, "How many times can I call WhatsisMethod in one second?" Not trying to make it work - trying to understand it. There are probably other ways to find out where the time is going. (Ever counted instructions?)But as a general comment... interrupt good, polling bad. Interrupt routine should probably just capture the state as quickly as possible, and politely return control to the main. It usually doesn't need to respond to the event itself -- takes too long. There are other event interrupts to attend to.Then, Do The Thing (respond to the event) at your leisure. Maybe perhaps some cleverness is required to respond to the event at a different time; an event queue is a pretty common technique. Or the interrupt routine does a tiny bit of work to figure out whats going on with the contacts. Time matters; gonna have keep track of that - what happened when. (It's an event, after all! It would be good to know when it happened.)

author: Simon
author_rank: Unknown
timestamp: July 25, 2023, 07:54:25 am
content: How fast is the polling? I typically check button inputs every 50-100ms, obviously I expect all other code to execute in that time so that it does poll at those intervals. With an encoder it is key to get it early when the input has just changed so that both channels can be looked at to determine direction. I have never done this and on a manual encoder I have no idea if bounce is an issue.

author: Ian.M
author_rank: Super Contributor
timestamp: July 25, 2023, 08:43:39 am
content: Your debouncing circuit for the rotary contacts looks suspicious. A mechanical encoder uses a sectored contact disk and wiping contacts. When the contact is over an insulating sector it physically cannot bounce closed and when it is over a conducting sector, it skips, bouncing open. It is therefore desirable to have a significantly asymmetric debouncing response, quick to go low and slow to go high. I therefore believe it would be advantageous to swap the 1K and 10K resistors for CHA and CHB.I also strongly suspect that the button debounce circuit will need different time constants to the CHA and CHB encoder debounce circuits. The button debounce needs near symmetric press and release time constants and to transition within a few tens of ms of actuation, certainly in less than 100 ms, whereas the encoder debounce is constrained by the maximum speed of rotation, and number of pulses per turn, and must never exceed half the interval between edges.Seehttp://www.ganssle.com/debouncing-pt2.htm(part 1 is also worth a read)Interrupts triggered by a mechanical event are a Bad Thing, unless you suppress the interrupt source for a period after each interrupt, as you risk anInterrupt stormas the switches wear and their contact bounce increases.Timer based interrupt driven polling is far more likely to give predictable results.