Noisy rotary encoder - Page 1
Forum section: electronics / beginners

author: desertgreg
author_rank: Contributor
timestamp: October 16, 2020, 04:26:42 pm
content: I'm trying to use a rotary encoder for a user control on a simple project. Here is the part number: ACZ11BR4E-15FD1-20C. I've been having a terrible time trying to get the code to behave so I decided to hook the scope up to the encoder pins and its just terrible! I have the recommended 0.01uF caps and 10K resistors on the A and B outputs from the encoder but the signal still looks terrible. I've attached a capture of what I see when I just spin the encoder shaft with my fingers and a steady, "normal" speed. Is this a bad encoder? Is this normal? Do I need to use more capacitance? Thanks for any advice!

author: Tom45
author_rank: Frequent Contributor
timestamp: October 16, 2020, 05:04:59 pm
content: It certainly looks like you have a bad encoder.Besides the noise, the square wave duty cycle isn't even 50%.

author: desertgreg
author_rank: Contributor
timestamp: October 16, 2020, 05:27:03 pm
content: This rotary encoder has detents so maybe the duty cycle is just caused by the rotational speed changing as I move through the detents. I'm just spinning it with my fingers and its pretty stiff.

author: Benta
author_rank: Super Contributor
timestamp: October 16, 2020, 05:41:10 pm
content: Either a defective encoder or bad connection. The fact that it occurs on both channels would make me suspect the ground/common wire.

author: Peabody
author_rank: Super Contributor
timestamp: October 16, 2020, 05:53:54 pm
content: The datasheet:https://www.cuidevices.com/product/resource/acz11.pdfhas a chart showing the detent positions, and it suggests the contacts are closed at each detent. Is that the case? If so, that seems like a really bad idea.The datasheet also has a suggested debouncing circuit. But based on your scope output I don't think it would help. I have had some luck taking encoders apart and cleaning them. If you're sure the connections are good, that might be worth trying. It seems you'll have to replace it anyway if you can't fix it.

author: wraper
author_rank: Unknown
timestamp: October 16, 2020, 06:02:53 pm
content: Might be bad encoder but your code is an inadequate shit as well if it cannot deal with this. Read some stuff about debouncing in code instead of resorting to a dumb shortcut with capacitors. And nope, waiting for a certain amount of consequent readings in one state and resetting a counter and starting counting again from beginning on a first different state is not a good debounce algorithm. It's simply astonishing how many mass produced devices don't do it right.

author: SuzyC
author_rank: Frequent Contributor
timestamp: October 16, 2020, 06:13:21 pm
content: A neighbor gave me a rarely used microwave oven after it became too difficult to set cooking time with its noisy rotary encoder.After five years I am still cooking my fish in it after I added .1uF bypass caps to the two output pins. OK, it lowered the max slew rate of setting time, but who needs to set a Nuke within a split second.BTW, it already had 10K pullups on the output pins. The .1 caps also serve to clean the contact surfaces during rotation.

author: wraper
author_rank: Unknown
timestamp: October 16, 2020, 06:30:06 pm
content: Quote from: SuzyC on October 16, 2020, 06:13:21 pmIf those caps are connected directly to encoder pins without any resistor, they will cause additional wear of the contacts due to high peak capacitor discharge current and may even cause inductive kickback.

author: SuzyC
author_rank: Frequent Contributor
timestamp: October 16, 2020, 06:50:17 pm
content: Inductive kickback? Nah! Not Noticed on my scope!Contact wear at 5V..Not Noticed on my scope!A small enough cap. discharge serves to keep the contacts clean of oxidation and contamination.Keep in mind here that the rotary enc. is not being continuously spun at 1000 RPM.Five years of operation with no problems. That's a lotta nuked fish, veggies and oatmeal.

author: wraper
author_rank: Unknown
timestamp: October 16, 2020, 07:07:45 pm
content: Quote from: SuzyC on October 16, 2020, 06:50:17 pmIt will be at very high frequency since inductance is very small. And you won't see it at capacitor side but the highest voltage will be within encoder.QuoteFor contact wetting you should select pull-up resistance accordingly.

author: SuzyC
author_rank: Frequent Contributor
timestamp: October 16, 2020, 07:30:08 pm
content: Adjusting contact wetting by lowering pullup resistance leads to high power waste, esp. not desirable in low-power circuitry. Adding small bypass capacitance does not and cleans the sw contacts.The Trc of 10k x .1uf =1mSec, so it takes 5 RC to reach ~99% Vhi and less RC's to reach Vin Hi thresh of mcu pin monitoring the enc output.This RC means very likely the existing instrument's MCU's SW debounce will work well seeing no debounce spikes above a pin's Vin Hi after a mSec or two dly.The UHF freq. blip by inductive kick within a Rot Enc upon contact closing will not be a problem with most common use, making settings.Adding a small series res, as like shown in the Rot Enc specsheet suggested ckt is always an option, but no, not always a necessity.

author: wraper
author_rank: Unknown
timestamp: October 16, 2020, 08:08:21 pm
content: Quote from: SuzyC on October 16, 2020, 07:30:08 pmYou can reduce overall current consumption very simply. Dedicate one MCU pin and connect all pull-up resistors to it. And power it only when reading MCU inputs.EDIT: also in case of encoder, capacitor will discharge when wiper just touched the contact, so only wiper and contact edges are cleaned while middle of contacts is not.

author: desertgreg
author_rank: Contributor
timestamp: October 16, 2020, 08:23:16 pm
content: Here is the circuit I'm using. I tried to follow the datasheet exactly. I dont really think its a good idea to make the code handle such noisy inputs. For example if it were interrupting on this noise, yes the logic should end up with the correct count but it will spend all of the cpu time babysitting these signals. I'm hoping someone will spot a dumb mistake I've made :-)

author: wraper
author_rank: Unknown
timestamp: October 16, 2020, 08:29:56 pm
content: Quote from: desertgreg on October 16, 2020, 08:23:16 pmIt's the most robust and cheapest way how to deal with it. BTW it's not that noisy on your waveforms.QuoteUsing interrupts from inputs with switches/encoders is one of the dumbest things you can do unless you really need it for some reason and take measures to prevent issues it may case. If you want to use interrupts, then use timer as a source and read inputs at certain intervals of time.QuoteOnly if you have no idea how to code properly. You can read and debounce inputs thousands of times per second and spend a few % of CPU time at most easily.

author: desertgreg
author_rank: Contributor
timestamp: October 16, 2020, 08:54:48 pm
content: Ok maybe I'm missing something on the code side. I implemented the state machine with 4 states and I have tried a couple of different 'fixed rate' polling options and it doesn't seem to work that well. Do you have an example or could you explain the psuedo-code for how to do it "properly"?

author: desertgreg
author_rank: Contributor
timestamp: October 16, 2020, 09:07:14 pm
content: Well, I'll be darned! Looked over the code again and I just found my mistake. I was updating the count based on the previous and current state of the encoder inputs but never saved the new state as the 'current state' for next time. Thanks for pushing me in the right direction! I'm surprised the signal looks so bad but it turns out I was looking in the wrong place and you were right about the code :-)

author: BrianHG
author_rank: Super Contributor
timestamp: October 16, 2020, 09:18:08 pm
content: Change your 0.01uf to 0.1uf and maybe place 0.01uf to GND on the encoder output pins. It will help, though, still need to improve your coding.

author: wraper
author_rank: Unknown
timestamp: October 16, 2020, 09:19:51 pm
content: You can try this to debounce inputs.Code:#define counter_max 10 //number of poll cycles during 10 milliseconds if you poll inputs 1000 times per second. 10 millisecond is contact going open debounce time, subject for adjustment.input_state_debounced = 0;if(input == 0){counter = 0;}else{if(counter >= counter_max){input_state_debounced = 1;}else{counter++;}}By this code it immediately sees that wiper touched the contact once input goes low. Counter should be somewhat lower than a minimum possible time needed for wiper to travel from one contact to another, so if high state time is lower than that, code knows that it was a bounce actually (since it was impossible to reach another contact in this time) and resets the timer. You can use this code to debounce usual switches as well.EDIT: just in case, it's assumed thatcounterandinput_state_debouncedare preserved between polling cycles. input_state_debounced can be not preserved as well, but then you must add input_state_debounced = 0; in the last else condition in addition to counter ++;. Also on second thought you may want to reduce debounce time to something like 10ms or so instead of 25ms. Optimal number depends on PPR number of encoder, with larger PPR , debounce time should be smaller. Larger time makes it more robust but if it's too large, you will get skipped pulses during fast encoder rotation.EDIT2: actually I made the code a little bit simpler. No need to preserve input_state_debounced between polling cycles.

author: wraper
author_rank: Unknown
timestamp: October 16, 2020, 09:32:52 pm
content: You could forward input_state_debounced to MCU outputs and check with oscilloscope how well this code cleaned up the inputs and compare it with signal directly from encoder connected to another oscilloscope channel.

author: desertgreg
author_rank: Contributor
timestamp: October 16, 2020, 09:57:21 pm
content: Thanks, I'll give it a try!

author: jmelson
author_rank: Super Contributor
timestamp: October 16, 2020, 11:27:44 pm
content: This is not an "encoder". It is a mechanical switch that produces a quadrature switch duty cycle. As you turn it, contacts wipe on a conductive trace.You need to use extensive software filtering (debouncing) of the signals, forcing the output to be registered slowly, or you need to get a real "encoder".One of the obvious distinctions is the real encoder will have a power supply pin, as well as a ground and two (A, B) outputs.Jon

author: mikerj
author_rank: Super Contributor
timestamp: October 17, 2020, 11:32:16 am
content: That trace looks like the output from the cheapest Chinese encoders; you don't just get a bit of bouncing when the contacts make or break but instead if happens over the entire duration of the wiping contact. The genuine Alps/Bournes encoders still get contact bounce but they are vastly better than this IME.

author: desertgreg
author_rank: Contributor
timestamp: October 19, 2020, 09:02:44 pm
content: Right, it seems pretty ugly to me. But I haven't ever actually looked at one of these with the scope. I am going to try some different rotary encoders so it will be interesting to see if they look any better.

author: wraper
author_rank: Unknown
timestamp: October 19, 2020, 09:14:31 pm
content: Quote from: desertgreg on October 19, 2020, 09:02:44 pmThere certainly are better encoders but your code should work flawlessly with signal much worse than this. Even good encoders deteriorate and become worse than this over time. Mechanical wear is not required for this to happen since contacts will oxidize and lubricant thicken into hardly penetrable mass regardless. So robustness of your firmware will make the difference between still working flawlessly after a decade or two or starting acting erratically with age.

author: Ground_Loop
author_rank: Frequent Contributor
timestamp: October 21, 2020, 03:42:05 am
content: Try an optical encoder. Every mechanical one I've used has a bit of nasty I it. But not as bad as what you show.

author: Peabody
author_rank: Super Contributor
timestamp: October 21, 2020, 02:34:08 pm
content: Quote from: wraper on October 19, 2020, 09:14:31 pmWell I'd like to see an example of such code. I've used the lookup state table method, and it works quite well even with no hardware debouncing. But as mikerj says, this one is bouncing during the entire contact period, and I've never seen code that would deal successfully with that. If one switch doesn't settle down before the other switch closes, I don't see how code could deal with that "flawlessly".But I think, as Dave would say, he should "take it apart" and clean it. Usually, but not always, it's just some crud in there that can be removed. I've rescued encoders doing that. And if he's going to replace it anyway, he has nothing to lose trying it.

author: wraper
author_rank: Unknown
timestamp: October 21, 2020, 02:46:10 pm
content: Quote from: Peabody on October 21, 2020, 02:34:08 pmIf your code waits for switch to settle it's a piece of inappropriate junk. Code example I provided previously does exactly opposite, it does not wait for switch to settle. It exploits nature of the switch, that there are no false positives, only false negatives. As there are only false negatives, the only thing it needs to account is that release time does not exceed minimum possible travel time from one position to another. Given that polling is done frequently enough, it will work just fine even with extremely bouncy contacts as long as contact is made at all.

author: Peabody
author_rank: Super Contributor
timestamp: October 21, 2020, 04:01:48 pm
content: The code shouldn't wait for the switch to settle, but I still think each switch has to settle before the other switch changes state. Otherwise you can't determine the direction properly. But if you have code that overcomes that problem, that's great.

author: MarkF
author_rank: Super Contributor
timestamp: October 21, 2020, 04:32:40 pm
content: Quote from: wraper on October 21, 2020, 02:46:10 pmSorry @wraper.But, your code is a wonderful example of incrementing a counter.However, no indication whatsoever of how to process the encoder inputs.It's nothing but a simple delay. Is that what you are advocating as foolproof?I would not call that debouncing.Show us a complete example that will produce a useful output.

author: wraper
author_rank: Unknown
timestamp: October 21, 2020, 05:24:43 pm
content: Quote from: MarkF on October 21, 2020, 04:32:40 pmIt's opposite of what I meant in that quote when people make it wait until reading stabilizes. This code does opposite, it does not wait until reading stabilizes. It acts immediately when switch closes. Delay is only on contact release. For some stupid reason people want to make exactly opposite when think about debouncing. You can think about it as a peak detector of sorts. Or hysteresis which works one way only. You can make such counter which counts both ways depending on input and transition happens when it reaches min or max (symmetrical hysteresis) but for encoder one way is more appropriate IMHO. As symmetrical counter is only useful when you also want to fight EMI besides simply bounce as you may have both false negatives and positives. In this case there are only false negatives (false contact open).QuoteNot really.QuoteFeed into state machine as usual. That code is for debouncing individual input.

author: MarkF
author_rank: Super Contributor
timestamp: October 21, 2020, 05:48:50 pm
content: It is a simple delay!- You detect the initial encoder state change- Then, you wait a specific delay time- And then, you process the encoder inputIt appears that you are trying to sample the encoder in the middle of a state change?Delaying long enough for the bouncing to stop?But since you are unwilling to post some code, we can not be sure.Even some 'pseudo-code' would be acceptable. (Assuming you know the term.)Edit:You are also assuming that both encoder inputs are low when it's not being rotated.I don't think that's always the case.

author: wraper
author_rank: Unknown
timestamp: October 21, 2020, 05:57:41 pm
content: Quote from: MarkF on October 21, 2020, 05:48:50 pmNo. When contact is closed there is no delay. State is immediately reported as 0/closed (as there cannot be false positives by the nature of how contacts are made). When state is low it gets locked for a certain amount of time (counter) before can be released to high and if new low reading arrives lock time is extended.QuoteWhat additional code you want, is that code not clear enough? As of what you do later, feed into your favorite state machine.

author: wraper
author_rank: Unknown
timestamp: October 21, 2020, 06:06:20 pm
content: Quote from: MarkF on October 21, 2020, 05:48:50 pmIt's you assuming what I'm not assuming. This code works with individual input. If you have 2 inputs, you need to run it twice (one time per input) for each poll.

author: wraper
author_rank: Unknown
timestamp: October 21, 2020, 06:53:00 pm
content: Yellow and green lines illustrate output of my code. As you can see, there is no delay on the falling edge. Delay is present on the rising edge only.

author: MarkF
author_rank: Super Contributor
timestamp: October 21, 2020, 07:04:21 pm
content: Quote from: wraper on October 21, 2020, 05:57:41 pmI want to see ALL of your processing code. From start to finish. From encoder input to your state machine's output.Those few lines you posted DO NOT illustrate any of the actions you're stating.

author: wraper
author_rank: Unknown
timestamp: October 21, 2020, 07:14:01 pm
content: Quote from: MarkF on October 21, 2020, 07:04:21 pm. They do illustrate exactly what code will output. If you see it otherwise, you fail to read the code. And state machine which is used afterwards is irrelevant here.

author: MarkF
author_rank: Super Contributor
timestamp: October 21, 2020, 09:08:02 pm
content: I have been coding professionally for 35 years and your code states NOTHING of what you claim!The code just sets a flag after counting for 10 cycles.You don't provide enough to see how it interacts with the state machine.You boast how superior your algorithm is. Enough talk! So, post it!This is just a waste of my time. I thought you were going to present a better approach. (...Not)I'll just stick with my six lines of code that equally work as flawless.Show how it is incorporated into your processing. On its own, this is useless.Code:#define counter_max = 10input_state_debounced = 0;if(input == 0){counter = 0;}else{if(counter >= counter_max){input_state_debounced = 1;}else{counter++;}}

author: newbrain
author_rank: Super Contributor
timestamp: October 21, 2020, 09:10:46 pm
content: Ok, I'll bite.Here's the code I use, works extremely well and is very light on CPU.The function PollEncoder() is called once for each encoder at your preferred rate <= 5 ms, the actual rate does not even need to be very precise.The encoders are HW debounced as the OP does (and the vendors advise to).This version is simplified to remove acceleration.No timers, no interrupts, no complicated code, it compiles to a handful of instructions plus the state table.Not suitable, of course, for 'half pulse' encoders (usually 15 ppr, 30 detents).Code:/* Read a full step encoder with negative logic *//* Based on a simple state machine */typedef enum{ENCIDLE = 0,CW__INI,CW__MID,CW__END,CCW_INI,CCW_MID,CCW_END,ENC_ERR,CW__OUT = 0x10u, /* The actual state is IDLE, action encoded in high nibble */CCW_OUT = 0x20u, /* The actual state is IDLE, action encoded in high nibble */} State;#define ENC_STATE_MASK 0x0Fu/** As the new state for the encoder FSM depends only on the phase inputs and* the previous state, a simple state table is enough.* Clockwise step input sequence: 11, 10, 00, 01, 11, 11 is rest posistion* CounterCW step input sequence: 11, 01, 00, 10, 11* It's acceptable to go from MID state (00) to output.* Unexpected transitions goto an error state, ENC_ERR, then to idle, ENC_IDLE.* The error state persists until a 11 is read (return to rest position).* If the unexpected transition is to the rest position, ENC_ERR is skipped.**/unsigned char const nextState = {/* Previous stateENCIDLE CW__INI CW__MID CW__END CCW_INI CCW_MID CCW_END ENC_ERR */{ENCIDLE, ENCIDLE, CW__OUT, CW__OUT, ENCIDLE, CCW_OUT, CCW_OUT, ENCIDLE}, // Input 11{CW__INI, CW__INI, CW__INI, ENC_ERR, ENCIDLE, CCW_END, CCW_END, ENC_ERR}, // Input 10{CCW_INI, ENC_ERR, CW__END, CW__END, CCW_INI, CCW_INI, ENC_ERR, ENC_ERR}, // Input 01{ENC_ERR, CW__MID, CW__MID, CW__MID, CCW_MID, CCW_MID, CCW_MID, ENC_ERR}, // Input 00};typedef struct{GPIO_Type * encGPIO;const uint32_t ph1Pin;const uint32_t ph2Pin;uint32_t timeout; /* This is only used with acceleration - not shown here */State FSMstate;} Encoder;/* Example array of encoders */static Encoder encoders[] = {{.encGPIO = ENC0_PHA_GPIO,.ph1Pin = ENC0_PHA_CHANNEL,.ph2Pin = ENC0_PHB_CHANNEL,.FSMstate = ENCIDLE,},{.encGPIO = ENC1_PHA_GPIO,.ph1Pin = ENC1_PHA_CHANNEL,.ph2Pin = ENC1_PHB_CHANNEL,.FSMstate = ENCIDLE,},};/* Read full step encoder, call this function at a reasonable sampling rate (<= 5 ms), looping on all the encoders */int PollEncoder(Encoder *e){int key = 0;bool ph1 = GPIO_PinRead(e->encGPIO, e->ph1Pin);bool ph2 = GPIO_PinRead(e->encGPIO, e->ph2Pin);uint32_t FSMinput = (ph1 | (ph2 << 1)) ^ 0x03u; /* Use negative logic for the encoders, alternatively, you can flip the table! (╯°□°）╯︵ ┻━┻ *//* Advance the state machine */State newState = nextState[e->FSMstate];/* Check if we have a full step */if (newState == CW__OUT)key = +1;else if (newState == CCW_OUT)key = -1;/* Clean up the state from the (possible) ouput indication */e->FSMstate = newState & ENC_STATE_MASK;return key;}The state machine is less strict when in ENC_MID , it can go directly to a valid output state.This is to cater for some shitty Bourns encoder that change both phases almost at the same time when going into the rest/detent position (< 1 ms when turning them at a reasonable speed), but not when moving away from it.With more well behaved encoders (Alps), this stricter FSM table can be used:Code:unsigned char const nextState = {/* Previous stateENCIDLE CW__INI CW__MID CW__END CCW_INI CCW_MID CCW_END ENC_ERR */{ENCIDLE, ENCIDLE, ENCIDLE, CW__OUT, ENCIDLE, ENCIDLE, CCW_OUT, ENCIDLE}, // Input 11{CW__INI, CW__INI, CW__INI, ENC_ERR, ENCIDLE, CCW_END, CCW_END, ENC_ERR}, // Input 10{CCW_INI, ENC_ERR, CW__END, CW__END, CCW_INI, CCW_INI, ENC_ERR, ENC_ERR}, // Input 01{ENC_ERR, CW__MID, CW__MID, CW__MID, CCW_MID, CCW_MID, CCW_MID, ENC_ERR}, // Input 00};

author: wraper
author_rank: Unknown
timestamp: October 21, 2020, 09:13:28 pm
content: Quote from: MarkF on October 21, 2020, 09:08:02 pmIt simply takes input data from input connected to a switch (inputvariable) and outputs either 0 or 1 depending on what it thinks is debounced state (input_state_debouncedvariable) which by default is 0 unless counter reached counter_max value. What interaction with state machine do you expect?

author: Peabody
author_rank: Super Contributor
timestamp: October 21, 2020, 09:31:14 pm
content: Let me see if I understand this. When you first detect that the line has gone low (switch has closed), you consider it to be low, with no waiting. But you don't consider it to be high again until it's been consistently high for some(?) time. If you get a low before the time is up, you start over on the count.If that's right, I still have the problem that the minimum high period could be satisfied by polling on bounces. It might appear to have been high for x milliseconds, but if it's still low, but bouncing, you could get several successive high readings, and would report an invalid high to the state machine routine.And you can't have a minimum low period because that depends entirely on how fast the knob is being turned.I've seen a number of decode routines for these things, and even invented one myself, but I still think that while bouncing can take place immediately after a transition, that bouncing has to stop before the other switch changes state. If the encoder doesn't work that way, it's not going to give good results no matter how good your code is. But the last encoder I had that behaved that badly I fixed by taking it apart and cleaning it, so I have no way to test your code even if I had your full routine.My favorite method is interrupt based, and the pin which just generated the interrupt is then interrupt-disabled in the ISR, and it's enabled on the other pin, which should be stable by now. By alternating the interrupt enables, you can go from one detent to the next on literally four interrupts, even if there is bouncing. None of the bouncing triggers interrupts. But the switches have to settle down in a reasonable time, or it doesn't work.

author: wraper
author_rank: Unknown
timestamp: October 21, 2020, 09:49:03 pm
content: Quote from: Peabody on October 21, 2020, 09:31:14 pmYes.QuoteIf there are too many false high readings in succession, no algorithm will help. How are you supposed to know that input is actually low, when all of the readings are high? The only way to fight this is increasing polling rate and counter_max (or using interrupt on inputs which is not advisable). But it's more like on a faulty encoder/switch territory.Quotecounter_max exactly defines minimum LOW period. Minimum high period is not defined. As I wrote, counter_max is subject to adjustment to get the strongest possible debounce without getting missing pulses during fast rotation.

author: ogden
author_rank: Super Contributor
timestamp: October 22, 2020, 04:18:38 am
content: Quote from: MarkF on October 21, 2020, 09:08:02 pmYou wasted your 35 years of your life then - if you can't even read what people say, not mentioning code snippet. Other option - you programmed relational databases or something similar which has nothing to do with bare metal MCU programming. Quite classic debounce code posted indeed does exactly what @wrapper said - debounce of *single* polled input signal. You have to understand all terms used, also word "single". If proof that code works in form of scope waveforms is not enough for you then using your own words - you are indeed wasting time of yourself and everybody else here.Edit: @wraper, why don't you show poll routine used to get scope waveform, tell polling frequency. Should be enough for professional to understand - this code is able to give shown results or not.

author: wraper
author_rank: Unknown
timestamp: October 22, 2020, 08:15:31 pm
content: Quote from: ogden on October 22, 2020, 04:18:38 amFrankly I have more productive things to do which actually bring me money but OK.

author: wraper
author_rank: Unknown
timestamp: October 22, 2020, 11:03:30 pm
content: Eat itMarkF! 35 years of so called programming experience, yet cannot read a few simple lines of code.

author: wraper
author_rank: Unknown
timestamp: October 22, 2020, 11:25:08 pm
content: Actual code running on MCU. I did not bother to make proper polling routine, so simply used delay function as this device doesn't do anything besides debouncing.Code:#include <SI_EFM8UB1_Register_Enums.h> // SFR declarations#include "InitDevice.h"#include "stdint.h"#include "timer_delay.h"#define counter_max 10#define channel_quantity 2 //number of channels to debounce.int main(void) {enter_DefaultMode_from_RESET();while(1){uint8_t input, input_state_debounced, channel_no;uint8_t counter = {0};// P0 output port// P1 input portinput = P1; //read portinput_state_debounced = 0;for(channel_no = 0; channel_no < channel_quantity; channel_no ++){if((input & (0x01 << channel_no)) == 0) //channel mask{counter = 0;}else{if(counter >= counter_max){input_state_debounced += (0x01 << channel_no);}else{counter ++;}}}P0 = input_state_debounced; // output debounced signalsTimer0_Delay_100_us(5); //wait 0.5 ms}}

author: vk6zgo
author_rank: Super Contributor
timestamp: October 23, 2020, 02:14:17 am
content: Quote from: desertgreg on October 16, 2020, 04:26:42 pmThe "old school" solution would be to simply clip the bottom 2/3 or so of your waveform, then amplify the resulting clean pulses up to whatever voltage level is required.This, of course assumes that the amount of "crud" is always similar to that shown, & doesn't ever closely approach the full pulse amplitude.

author: wraper
author_rank: Unknown
timestamp: October 23, 2020, 12:45:23 pm
content: BTW, it takes 4.5us to for this code to debounce 2 inputs on 1T Silabs 8051 @ 25MHz. So when polling at 2000 Hz rate it takes 0.9% of CPU time (or 0.45% at 1000Hz poll rate).

author: ogden
author_rank: Super Contributor
timestamp: October 24, 2020, 04:04:23 pm
content: Quote from: vk6zgo on October 23, 2020, 02:14:17 amI am not familiar with "old school" debounce through clipping+amplifying. Could you please provide more info about this approach? Circuit?

author: vk6zgo
author_rank: Super Contributor
timestamp: October 25, 2020, 11:35:20 am
content: Quote from: ogden on October 24, 2020, 04:04:23 pmIt is not normally described as "debounce", but is a way of removing noise which can be used for such noise at both top & bottom of the pulse, but in this case the "bounce " looks like noise existing only on the bottom (most negative) end of the pulses.I think you will find descriptions on the Internet of how such devices work, but basically they use semiconductor or tube diodes biased to conduct when a pulse reaches an adjustable, or preset level.This can remove the "crud", & are normally followed by an amplifier of really good specs, which will contribute very little distortion.

author: ogden
author_rank: Super Contributor
timestamp: October 25, 2020, 11:50:23 am
content: Quote from: vk6zgo on October 25, 2020, 11:35:20 amTry me. Please show few relevant links to information that would confirm what you say. I could not find any.

author: BrianHG
author_rank: Super Contributor
timestamp: October 25, 2020, 12:01:37 pm
content: I am not sure why everyone is having such trouble with this. Think carefully. There is only noise when the encoder is on the on / pull down position, not the high position. The MCU pins a high impedance CMOS digital inputs.Try this.VCC|100k|ENC-OUT# pin ----- 1k --+---------------- MCU input.|100nf|GNDThe 1k protects the encoder switch from high current short to VCC when turning on shorting to GND.The cap will be discharges really fast.The slow 100k pullup wont allow the MCU to register a high during that random noise which will stay below ~2v.Yes, when turning the knob really fast, the signal will turn into a weak triangle/sawtooth wave.Yes, the 100nf may need to be 47nf, or 22nf, but this is what breadboards are useful for testing.And, ok, you might get a noisy event to hiccup one in awhile, but, stepping up/back down once in a blue moon will not eat away your processor through a massive stream of transitions to filter out depending on your coding techniques.

author: ogden
author_rank: Super Contributor
timestamp: October 25, 2020, 12:27:32 pm
content: Quote from: BrianHG on October 25, 2020, 12:01:37 pmBefore you let others think carefully, you are advised to read carefully firstCapacitor noise filter is mentioned early in this thread already. BTW 1k resistor is pointless, especially if capacitor is close to encoder. 100k/100nF will be "too slow" indeed for most encoder applications, this combination is popular for pushbuttons. 100k/10nF seems like better match for encoder.QuotePeriodic polling debounce like shown by @wraper consumes constant CPU resources. In case one plans to process noisy input using pin change interrupt - it is dumb idea unless combined with timer "gating", thus quite complex in result.

author: vk6zgo
author_rank: Super Contributor
timestamp: October 25, 2020, 12:32:56 pm
content: Quote from: ogden on October 25, 2020, 11:50:23 amhttps://www.eevblog.com/forum/projects/clipping-a-fast-negative-pulse/Such circuitry was used extensively in Analog TV, where the amplitude, rise time, & duration of line sync pulses were of great concern."Keyed clamper" circuits could assure correct level of the "back & front porches" (blanking level), but there could still be distortion of the negative extremies of the sync pulse.It was common to increase (stretch) the amplitude of the sync pulse, then clip it back to the correct amplitude & rise time.Clipping was also used in Long Line machine telegraphy "regenerators" to remove noise from both voltage extremities of the pulse, as well as to restore the rise time to spec.It was a common tool in the arsenal of designers in the Comms field, back in the day.If it was 1990, I could find dozens of references to the use of such devices, but they have fallen out of favour with the demise of analog TV.All that said, for the OP's problem, shouldn't it be possible to mess with the amplitude & DC offset, so that an ADC wouldn't see the noise at the negative end of the pulse, or use a plain old Schmitt trigger?PS:- I had a bit of "brain fog" there, as you normally wouldn't be using an ADC, would you, as the input is ideally, just pulses.

author: BrianHG
author_rank: Super Contributor
timestamp: October 25, 2020, 12:46:26 pm
content: Quote from: ogden on October 25, 2020, 12:27:32 pmThe mechanical noise the op opened with trying to filter is from a slow turning encoder, usually a hand turned one. Anything high speed is usually optical or magnetic.That 1k is not pointless. Having a 10nf cap or 100nf cap charged to 5v, when turning the encoder, every time it shorts to GND, a tiny spark due to shorting that cap straight to GND will eat away at a mechanical rotary brush contact which was not designed to handle that over the life of potentially a million rotations. The mechanical encoder's demise will be accelerated as the edge of a cheap tin PCB surface which has the encoder's pattern printed on it gets charred at the beginning of the contact at each position. This is why specifically in many mechanical encoder datasheets example filter circuits has a 10k series at the encoder output pin before it reaches the cap.As for the 100k pullup, 100nf pulldown, yes it was a recommended test value. Yes it can be fast enough for a low resolution, or low speed hand turned rotary encoder. Looking at the OP's scope shot and current filter, probably 10nf with 100k pullup would also probably work fine.I tend to operate on a 50/50, make a an adequate filter solution for all case use, degrade that filter to allow 4x faster rotation performance than what you need. Then make a software de-bouncer to clean up what that 4x might be letting through.

author: wraper
author_rank: Unknown
timestamp: October 25, 2020, 01:03:07 pm
content: Quote from: ogden on October 25, 2020, 12:27:32 pmYou can use either interrupt by timer, your own scheduler for timing various things or RTOS. It consumes some CPU resources but does not cost any money/additional components/PCB space unlike HW debounce and is very reliable. My approach is to not waste any parts on something that can be done in code.

author: BrianHG
author_rank: Super Contributor
timestamp: October 25, 2020, 01:13:25 pm
content: Quote from: BrianHG on October 25, 2020, 12:01:37 pmOOOps, little mistake, the 100k belongs here:VCC|100k|ENC-OUT# pin --*-- 1k --*---------------- MCU input.|100nf|GND

author: ogden
author_rank: Super Contributor
timestamp: October 25, 2020, 01:37:32 pm
content: Quote from: vk6zgo on October 25, 2020, 12:32:56 pmI did not ask pointers to signal clamping and clipping. That's common knowledge indeedI want to see how such can be used for debounce.Why I ask? - There's problem in approach because mechanical contact noise usually gos from 0V to full amplitude unless filtered. To limit signal amplitude for clamping - you need RC filter. When you have RC filter then by properly picking it's time constant you can process input without any additional (clamping) circuits. In short: using clamping for debounce is pointless.

author: vk6zgo
author_rank: Super Contributor
timestamp: October 25, 2020, 02:52:53 pm
content: Quote from: ogden on October 25, 2020, 01:37:32 pmIndeed, but the OP's scope screenshots do not show the noise going to full amplitude.Unless the DSO has a problem with displaying fast noise spikes, the obvious conclusion is that they don't reach full amplitude.It seems to me that the noise spikes are a bit more complex than simple contact bounce.

author: madires
author_rank: Super Contributor
timestamp: October 25, 2020, 03:54:06 pm
content: Quote from: newbrain on October 21, 2020, 09:10:46 pmI've seen several implementations based on FSM tables. With some simple logic you can condense the whole thing into one byte, i.e. the four expected AB states for a given AB based on the the Gray code. And the expected states are arranged based on the numerical values of AB. So you can simply shift the byte right by "2 * old AB" to get the expected next AB. That simplification also allows you to use encoders with one or two Gray code steps per detent. For an example please see function ReadEncoder() in user.c of the Transistortester's m-firmware.