Small project #4 — Serial Knob

terminal

Want one? Check out tindie.com.

Is it useful? Maybe. I have a couple of existing projects that would benefit from a rotary knob, but not enough code-space is left to add encoder handling. Serial comms is already working, so this is the simpler way to make it work.

Serial-Knob-front

{1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0}

The onboard microcontroller does the decoding, optionally some more processing, and sends byte-sized data to the main project board. The byte contains information about direction, push-button status and maybe even about average rotation speed (encoded in the higher bits). I think I will like it – if it works ;-)

A quadrature encoder has exactly four states, which are determined by the 2 output signals A and B. These states may be called: (00,01,10,11). The signals A and B are 90° out of phase, shifted with respect to each other. Ideally only one signal can change at any given time, never both. That way one can determine which one changes first (A before B, B before A), which carries the information about direction of rotation.

quadrature_encoder

You can use an edge-triggered interrupt to do it, but that requires absolutely clean edges. No switch bouncing tolerated. Otherwise the cpu load would surge. We don’t want that. This is also not about reading a high-frequency signal, so the polling approach is sufficient. We also don’t care about sleep modes, so there’s no need for interrupts to wake up the micro.

As it happens, there is already a timer interrupt running at 1kHz, which provides ‘time’ and ‘delay’ functionality (recycled code from another project). Reading and evaluating the encoder state is simple enough, so we just plug that into the system-ticker routine.

By comparing the current vs. the previous encoder state, one can determine if a step has been made and in which direction. This assumes that every step is recognized. If steps are skipped, there are disambiguities and it can’t be determined for sure what has happened. Time to increase the polling frequency. But for a hand-turned knob with just 24 ticks per revolution this extremely unlikely.

A typical time-sequence of states may look like this:

… – 11 – 01 – [ 00 - 10 - 11 - 01 ] – 00 – 10 – …

Going from left to right could be clockwise rotation, right to left anti-clockwise rotation.

The state names (00,01…) also match the bits that are set or unset in the GPIO input register of the microcontroller. One way to make the decision of what step has taken place is to use a lot of if-statements, but that is a lot of text to write. A simpler way is to create a truth-table of sorts (array), with elements of {+1, 0, -1}, representing the step-direction. The bit-values of the encoder states (previous and current) are joined into one byte, which will become the index of the array. If a combination indicates a valid step forwards, the associated array-element will be ‘+1′. A negative step will of course be represented by ‘-1′. No change or any invalid combination (e.g. from 00 to 11) will get a ’0′.

The main loop checks for a changed encoder state (flag is set by the interrupt), and processes the data – i.e. send out a status-byte via serial. Done.

Quick and dirty application: Volume control knob for Amarok or kmix. All you need is a 5V USB-serial adapter and a little perl script that listens for data. Oh, and linux of course. Amarok’s volume is controlled using dbus calls. Very simple to do, actually. Part of the demo code for this little project!


As usual you can have all design files and code. Search for “Serial-Knob” and take a look inside the “ATtiny_projects” repo for code: [
1],[2].


How to get started with my demo firmware

I’ll blatantly assume you use linux. From my point of view it is the easier way to get things running. Using virtualbox is a good way for you winblows users.

1) Download the latest Arduini-IDE. Currently this is V1.0.3
2) Unzip in your home folder. Anywhere you like it to be.
3) Download the firmware and save in a nice place. I’ll just call it fw-dir from now on.
4) cd to fw-dir and modify ‘config.txt’ to match Your installation / board.

Now the script knows where to find the compiler etc.

5) Make adjustments to the code as necessary.
6) Run ‘./compile.sh XX’ to recompile. ‘XX’ can be 25, 45 or 85.

The scripts accepts a parameter to compile for either attiny25, 45 or 85. If you got your ‘Knob’ from Tindie, just omit the placeholder ‘XX’. If you have a different microcontroller (25 or 85), just replace the ‘XX’ with either 25 or 85.

7) Connect your programmer to the ISP header. Orientation does matter.
8) run ‘./flash.sh XX’ to program the chip. ‘XX’ can be 25, 45 or 85.

This entry was posted in Electronics. and tagged , , , , , . Bookmark the permalink.

4 Responses to Small project #4 — Serial Knob

  1. Pingback: Fun With Rotary Encoders « adafruit industries blog

  2. kscharf says:

    With optical encoders you are asured of clean edges. I’ve used two interrupt pins to detect edge changes and built an encoder interface that way. Newer AVR’s have pin change interrupts and you can use this as a trigger and then poll to see which pin changed (allows multiple encoders on one micro) With switch (mechanical) encoders bounce is a problem, but you can clean things up with ’14 type inverters and an RC network. For a dedicated encoder controller your approach is fine. Hint: if you are bit-banging the serial port up the baud rate and use the timer interrupt to both poll the encoder and send/receive data.

    • robert says:

      Optical encoders cost a bit more, cost was an issue here. I could get higher rotation speeds out of them. The mechanical ones max out pretty soon. With a 1kHz timer I should get quite a high rate of revolution for a simple knob, but the mechanical switches just don’t deliver. I should take a look at the waveform to see what happens. Probably looks pretty awful.

      Size was also an issue, so I opted to go without any analog signal conditioning. At least the quadrature signals don’t require any debouncing with a timer approach. Using a pin-change interrupt without debouncing is a nice educational lesson. I don’t need this one anymore. The push-button needs some debouncing though. Maybe a bit of bit-shifting will suffice.

      Of course I could have the timer take care of the data transmission as well, but I don’t see the point right now. I only send single characters, so no need for a buffer and non-blocking writes either. Upping the data rate sounds better. I’ll have to check the baud-rate errors for 8MHz system clock first.

      I only used 9600 as the other project for which this device was made as an upgrade uses that. I could change the baud rate for everything, but… never change a running system.

  3. robert says:

    A happy customer ;-)

    Navid Gornall incorporated “The Knob” into one of his creations.

    http://navidgornall.com/blog/make/

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA Image

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">