Rotary Encoders and interrupts - Page 1
Forum section: electronics / general

author: Dan Moos
author_rank: Frequent Contributor
timestamp: October 18, 2016, 12:30:47 am
content: I'm wondering if I really need to use interrupts for the rotary encoder in my project.Atmega328 running at 8 mHz. Probably up that to sixteen in the end product.When not getting input, this things his be sitting idle, so 99 percent of the time, the only task the mcu has I'd to wait for input. What's more, the encoder is only for user interface purposes, so the only thing competing with the encoder will be LCD updates through I2C, and a few variables being updated. The only time the mcu will really be working hard is when other buttons get pressed.All this to ask if a rotary encoder can work well without interrupts in my scenario.Another wrinkle. My reason for asking is I would love to put my entire user interface (~18 buttons and the encoder) on a single shift register chain. I don't NEED to do this, but it gives me some options if I save some pins.Also, I'm leaning toward debouncing the encoder with Schmidt triggers. My guess is this is better for having the mcu keep up than a software debounce, but maybe bad because if it doesn't keep up, glitches can't be caught the way a proper software debounce might.Bad idea?

author: rstofer
author_rank: Super Contributor
timestamp: October 18, 2016, 01:56:41 am
content: I'm in the minority, I captured edge interrupts on all 4 transitions. The code works fine but there are a number of folks who prefer polling or polling using a timer.

author: obiwanjacobi
author_rank: Super Contributor
timestamp: October 18, 2016, 06:13:55 am
content: The thinking of polling your buttons is that when the buttons get old and wear their bounce may become more prominent. That could lead to the interrupt being fired a lot more than you anticipated - taking all the cycles. Your program will become sluggish because 'all' time is spent in the ISR.If you keep that in mind and have some hardware debouncing in place (RC at the least), I think it could work well.If you're gonna use shift regs, then polling is the only option. That polling can be triggered by a timer interrupt though...Clocking in 3 bytes (24 bits: 18+encoder) should not be problem - you won't miss any button clicks/state changes.(Remember to mark any variable shared between you main loop and the ISR as volatile.)

author: rs20
author_rank: Super Contributor
timestamp: October 18, 2016, 06:32:10 am
content: Quote from: obiwanjacobi on October 18, 2016, 06:13:55 amAbsolutely right about the issue I have with interrupt-based rotary encoders -- they are optimized for picking up contact bounce which has no useful information content. What I don't understand though, is why people start spending $$ on board space and components for RC debouncing when you can simply poll for free. There seems to be a mistaken notion that interrupts "are for external events", and so if you have an external event, you "obviously use interrupts". So people seem to be biased towards using them even when this use doesn't have any actual engineering reason behind it.The main disadvantage of polling is that you are spending a certain percentage of your CPU time looking for rotary encoder events, even when there is nothing happening on the rotary encoder. Having said that, a fixed CPU load is preferable to a spiky, unpredictable CPU load any day of the week -- especially with most MCU firmware, which has nothing useful to do with spare cycles anyway. So in most cases (including the OP's), the only difference is a marginal saving in power consumption.

author: filssavi
author_rank: Frequent Contributor
timestamp: October 18, 2016, 07:04:39 am
content: Quote from: rs20 on October 18, 2016, 06:32:10 amFirst of all you have to consider that for all but the highest volume products (as in 10's of million of units) r' and C'è are practically free (you are talking fractions of a cent), you would obviously use values you already have uses in the desig, so they will not take up any additional PnP reelsAs for board space of course you need some, but with 0402/0201 components it is not that bad in most casesAs for polling, if the MCU is doing nothing most of the time un production, you probably have chosen an mcu that is too powerfull, for some applications like household appliances they still use 4 bit MCU's (yes 4 bits it's not a mistake), those parts are usually not in the catalogo/on website though so you might need to ask a Sales RepThere are cases then where the MCU is already working hard enough and adding debouncing isn't that helpfulWe come now at the other big problem with polling, if not done at supidly high frequencies (and then Power consumption is an issue) you might have delays between switch closing and action, for a UI it might br fine,but for example on an interlock in n X-ray macchine (of any kind) it certainly is not ideal

author: obiwanjacobi
author_rank: Super Contributor
timestamp: October 18, 2016, 07:17:56 am
content: I have seen more bugs in software debounce routines than hardware debounce circuits.Also, developing software is relatively more expensive for smaller production runs.Another reason why I think people lean towards using interrupts, is because its a 'free' way to let your MCU do multi-tasking.Having to poll a lot of inputs may turn you program into spaghetti.

author: rs20
author_rank: Super Contributor
timestamp: October 18, 2016, 07:57:03 am
content: No debouncing required, hardware or software. Just poll. And polling isn't spaghetti if you use a timer interrupt.

author: newbrain
author_rank: Super Contributor
timestamp: October 18, 2016, 08:27:01 am
content: Quote from: Dan Moos on October 18, 2016, 12:30:47 amThe attached code contains the user input handling in my power supply: encoders' rotation, push buttons (long and short presses) and UART - which I edited out, as not relevant here.It's for a STMF042, and uses the dreaded HAL, but should be fairly readable and portable.The core is theDebounce()function, which I call more or less every 1ms, triggered by the SysTick.Since only one button and one encoder is served in each call, the actual sampling interval is 2ms.TheDebounce()function is fast enough to be called in the SysTick handler, but it could be also called in the main loop.No need for edge interrupts, and input events are delivered in a queue (inputRing) the application can read withReadInput()(no spaghetti here).Excess events are simply discarded.As for the Schmitt triggers: the encoders are HW debounced with RCs (10kohm/47nF IIRC)+74HC14.Events are not lost even when turning the encoders at highest speed possible (for my fingers, at least...I did not try attaching a drill to the shaft).As the encoders are the 30-detents 15-pulses type, only a very simple direction detection routine is needed (and, indeed, possible).

author: dannyf
author_rank: Super Contributor
timestamp: October 18, 2016, 10:25:57 am
content: Quoteit depends on the application. the key here is to understand that the incoming pulses are unpredictable so the polling has to be done sufficiently fast. that means that 1) the main loop needs to be done sufficiently fast if the polling is done there; or 2) the polling routine needs to be called periodically.I would say that it takes 80 ticks to process a input, and 20 ticks for isr overhead. So if you have to execute the polling routines once every 1ms on a 1MIPS mcu, the "cost" to the mcu is 80 / 1000 = 10%.You can similarly estimate the cost via isr and I'm fairly confident that unless you are in a situation of constantly rotating encoder (a motor sensor for example), you are going to spend less via interrupts, even if you factor in the isr overhead and debouncing.on debouncing: I generally do it in hardware - problems are best handled locally.

author: dannyf
author_rank: Super Contributor
timestamp: October 18, 2016, 10:28:03 am
content: for individual keys, the kahn debouncing routine I posted here a while ago is quite good.for encoders, use the state machine approach (I posted an example here as well) - it is incredibly resistant to bounces.

author: madires
author_rank: Super Contributor
timestamp: October 18, 2016, 10:55:58 am
content: Quote from: newbrain on October 18, 2016, 08:27:01 amThose work fine with polling every 5ms, which are also sufficient to get the turning velocity, if you like to support a dynamic change of steps. Something developers often miss, is the diameter of the knob attached to the rotary encoder. With a larger diameter the encoder becomes slower, i.e. you can increase the polling delay, For multiple encoders you would simply poll all in a row every few ms. From what I've seen, good encoders got a bounce period of about max. 2-3ms. Encoders with detents = pulses, i.e. all four Gray code steps per detent, are harder to handle.

author: newbrain
author_rank: Super Contributor
timestamp: October 18, 2016, 11:04:04 am
content: Quote from: dannyf on October 18, 2016, 10:28:03 amAh, yes, for four-phases-per-click encoders (detents == pluses) that's a really good way.For two-phases-per-click (detents == 2*pulses) there's very little you can do...once the state machine is adapted, it is equivalent to my code.Unless you want a detection every two clicks.Quote from: madires on October 18, 2016, 10:55:58 amI though about that, but then preferred to keep it simple (I should have attached a timestamp to each event).I just press the shaft to change step. Long press for preset mode and other functions.Smallish knobs (~2cm diameter).The whole thing work remarkably well (no bounces, no jitter, very responsive).

author: rs20
author_rank: Super Contributor
timestamp: October 18, 2016, 11:39:28 am
content: Quote from: dannyf on October 18, 2016, 10:25:57 amWhat exactly is being "spent" here? Again, unless we're talking about marginal power savings, those "wasted" cycles weren't going to be doing anything useful anyway -- or at least, it's bold to make that claim without knowing details of the project.

author: filssavi
author_rank: Frequent Contributor
timestamp: October 18, 2016, 11:49:46 am
content: There seem to be a strange focus on HMI, in that case the user is so slow that anything can be done in software, if we step out of that area things get hairy fast,for example let's say that the encoder (100 pulses per rotation) is attached to an electric motor running at 3k rpm (and keep in mind that i've been very generous choosing a lowish motor Speed and not a top notch encoder (500, 1000 pulses per rev) then you need to poll at al least twice the frequency of the encoder signal (Shannon) or you'll miss pulses and the control isn't precise any more, so after few simple calculations you'll find that the polling frequency has to be at least 10 kHz, now that Number could very easily go up by a factor ~10 or more if we up rpm's and encoder resolution or if direction detection is needed (in that case you need to sample both A and B so double the polling frequency again)Now is such a load on the processor fine just to read the sensors depends on the application, keep in mind that you will have also to do also a couple of pi(d) loops and more often than not clarke,park,anticlarke,antipark (if you are controllino all but a dc/bldc motor)Now there is a point where a dedicated hardware encoder detector is necessary and then the polling vs interrupt depate is not relevant any more...

author: filssavi
author_rank: Frequent Contributor
timestamp: October 18, 2016, 11:58:39 am
content: Quote from: rs20 on October 18, 2016, 11:39:28 amMoney is spent, as in I'll have to specify a faster MCU since in a real product (as in not an hobbist/maker project) you will buy a MCU which is just capable enough to do the job, not a faster one, just to waste some PowerAnd the "marginal Power savings" might not be as marginal as you think first because using interrupts enables you to go in Deep sleep mode, with polling sleep is not an option, secondly in a battery powered devices ant Power saving is important even marginal ones since they add up

author: Jeroen3
author_rank: Super Contributor
timestamp: October 18, 2016, 12:32:24 pm
content: STM32's have timers with Encoder input mode. Simply read the timer to see how much it has moved.Could be an option. I used the cheapest I could find for a project with two hmi rotary encoders. A STM32F051.

author: newbrain
author_rank: Super Contributor
timestamp: October 18, 2016, 12:49:53 pm
content: Quote from: Jeroen3 on October 18, 2016, 12:32:24 pmYup,I also had a version with that (should do some git commit message scavenging to dig it out), but it's not useful for the OP on an ATmega.In the end, though, I preferred this version as the code is much simpler: the controller has plenty of time to handle the LCD, the encoders and buttons and the OCP (hardware CC mode, but SW controlled OCP with 5 selectable delay 1ms-10s).

author: rstofer
author_rank: Super Contributor
timestamp: October 18, 2016, 02:50:38 pm
content: Here is code for an ATmega128 (I think) that uses the 4 edges to count and measure distance. I have used the identical code with an encoder on one of the Digilent AVR boards when I was adding switches, knobs and dials to Microsoft Flight Simulator. That was a few years back...