Why polling misses steps
The first sketch has to read CLOCK within a quarter of a cycle of each fall, or DATA has already moved and the direction comes out wrong. Turned briskly, a quarter of a cycle is a few milliseconds. A loop with a delay() in it, or a slow print, is late, and the count goes wrong in ways that look like a faulty encoder.
A quarter of a cycle to get there
The first sketch reads CLOCK once each time round loop(). When it sees CLOCK
LOW for the first time, it reads DATA. That read is only right if it happens
before DATA falls too, a quarter of a cycle after CLOCK.
How long a quarter of a cycle lasts depends on how fast the knob turns. At 20 clicks a turn, the figure most EC11 parts give, two turns a second is 40 cycles a second: 25 ms a cycle, 6.25 ms a quarter. A quick flick of five turns a second leaves 2.5 ms.
Start with a 2 ms loop: every cycle counts, at every speed. Now drag the loop time up with the knob turning briskly. Past about 6 ms some reads land after DATA has fallen, and those cycles count backwards. Past half a cycle, whole cycles fall between two reads and are never seen.
Why it looks like a broken encoder
The failure is not a missing click here and there. It is the count stalling, jumping back, or going the wrong way while the knob goes the right way, and only when someone turns it quickly. Turned gently on the bench, the same sketch is perfect. That is the worst kind of bug: it shows up only in use, and it points at the hardware.
The usual culprits are the ones any sketch collects: a delay() left in to
slow the serial output down, a display that takes a few milliseconds to
redraw, a sensor read that waits for its answer. None of them is wrong in
itself. Each one makes loop() late for the encoder.
Two ways out
The first is to keep loop() short and never block, as the XL LED's
blinking without
stopping does for a
different reason. It works, and it breaks again the next time somebody adds
something slow.
The second is to stop asking. An interrupt makes the chip run a small
function the moment CLOCK falls, whatever loop() is doing at the time, even
if it is inside delay(). The read of DATA happens at the right instant by
construction. That is the next article.
When it does not work
Updating a display can take several milliseconds a pass, and that time comes out of loop(), where the encoder is being read. Turned slowly it keeps up; turned briskly it misses. Move the counting into an interrupt, which the next article does.
Yes, and for a slow knob and a short loop that is enough: the figure counts every cycle with a 2 ms loop at two turns a second. It stays fragile, because the next thing anybody adds to loop() slows it down again, and nothing tells you when it crosses the line.
A late read still sees CLOCK LOW, but by then DATA has fallen too, so the sketch concludes DATA fell first. That is the wrong direction. A read later still, after CLOCK has risen again, misses the cycle altogether.
Let CLOCK's fall interrupt the sketch, so the count never waits for loop().
Counting with an interrupt →Edit this page — content/books/rotary-encoder/why-polling-misses-steps.mdx
Questions about this product
See what other owners have asked, and read their solutions.
Rotary Encoder
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.