4 March 2016

VFO encoder vicissitudes

Warning: Techy Stuff!

The VFO encoders I have chosen (at least for starters) are 400 pulse/revolution optical units. Like all encoders, these have two square wave outputs, the phase relationships of which determine the direction of rotation. On the face of it, it's an easy task to interrupt on state changes, read the two outputs, do a state table lookup to determine the direction of travel and post a CW/CCW transaction. And that's exactly what I have done.

The code works just fine until you jerk the VFO knob suddenly. At this point the Arduino dies and only comes back to life with a reset. It's fairly obvious that the problem is due to state changes happening too quickly for something to cope with. The question is what.

To put this in context, let's look at some numbers. The encoder actually produces four level transitions (interrupts) for each step. So that's 1600 in 360 degrees. Let's say I can jerk the VFO knob at a rate equivalent to 10 RPM (not sure if I can but it's probably not far out). So that's 16,000 interrupts/second or one every 62.5uS. Hmm that's actually quite quick but surely the ARM-based Arduino Due can handle that?

Well it turns out that it cannot. After a lot of experimenting, I've discovered that the Arduino digitalRead() instruction, which one might expect to take a microsecond or so can actually take up to 70uS. This in turn is because a whole load of code has to be executed to handle the different pin assignments on various different models of Arduino.

I was surprised by this apparent inefficiency and did some research of my own. The Arduino has a micros() function that enables one to see how many microseconds have elapsed. It is thus a simple matter to measure how long any section of code is taking. Sure enough, the digitalRead instructions are the problem. I'm guessing that the digitalRead code is not re-entrant and the next interrupt comes along, re-entering the digitalRead code before the previous read has completed. Not very good programming practice!

There is a solution. It is possible to directly read the I/O pins using low level code and that will be very much faster. It's not device independent code any more but that doesn't matter as I do not plan to be changing processor boards once the project is completed.

I'm probably going to leave a decision on this until later to see whether it's an issue in practice but I thought it was quite an interesting problem to contemplate. I am sure there will be many more.


No comments:

Post a comment