Update 12/23/2014: Not being satisfied where I left my understanding of the pins and references, I spent some time digging around on the internet and in the header files for the Arduino development environment. For those who might be interested or might find it useful, I've put my notes and findings on a separate page: Arduino Micro Board Pins.
Next step is to get the rotary encoder working with the Arduino Micro. This is key, as the rotary encoder will be the user input interface for navigating menus and altering the operating parameters of the signal generator.
A rotary encoder looks like a potentiometer, but operates very differently. Instead of varying an analog value such as resistance as the user rotates the shaft, the encoder generates on/off signals that can be used to determine direction of rotation and even calculate speed and distance of travel (angular position from start). The encoder type chosen for this project also includes a momentary SPST switch which is used by pushing the shaft axially. This switch will be used as the "enter" button in the user interface.
I won't spend any time describing the internals of the encoder and the
Gray code used to represent the angular motion, but the Wikipedia site for
rotary encoders is a good jumping off point if the reader is curious.
Turns out that using an encoder with Arduino is (almost) trivially simple. The snarky "almost" is due to my not thoroughly understanding how to address the Arduino pins from within my code. In any Arduino sketch, one can read or write data from/to pins on the Arduino by first telling the system what which pin is an input or an output, using the
pinMode(pinNumber, mode) function. To read or write that pin, we can use
digitalRead(pinNumber)function or it's output counterpart
digitalWrite(pinNumber). There are similar analog pin functions as well, but we'll leave that be for the time being.
Arduino Micro Pins...what's in a name?
Notice the
pinNumber argument to these functions; this tells the system which specific pin on the board is "in play" for the function call. The argument value is expected to be an integer value, or we could use a
#define macro or
const int variable assignment to make a "friendlier" pin name. But what caused me no end of pain here was determining the actual pin number or name to use in the function call which would match the pins I had my rotary encoder lashed to.
From the previous
Signal Generator post, you'll recall that this project uses the I2C bus to control and communicate with the LCD module and in future iterations of the projects will add the Si5351 board on to that bus. This means I've already used pins 2 (SDA) and 3 (SCL) on my Micro board. Where do you think almost all of the example code, to include the examples in the
SQ9NJE Rotary library embedded in his
Si5351 project (look under the "examples" section of his repository). That's right, Virginia, pins 2 and 3.
Shouldn't be too much a problem, right? Just pick a different pair of digital pins to connect to the A and B port of the rotary encoder. You're half right; picking a pair of pins on the board is easy, but knowing how to refer to them in the code is another animal. For no particular reason, I chose the pins which had the silkscreen label
TX and
RX, but quickly found that one cannot refer to the pins in code with the printed label from the board, at least on the Micro. It will, generally work, on the full sized boards, according to numerous posts on the Arduino
Forums just not on the smaller boards.
The Tonylabs
image of the Arduino Micro pinout was very helpful in learning the depths of the Micro's capabilities and the multi-purpose nature of the board, but it actually made solving this problem more difficult. By way of example, if you examine the pin labeled
RX in the image above, you can see that it is also pin 18 on the ATmega32u4 chip and can be referred to as
PD3,
TXD1, and
INT3, none of which can be used in the pin assignment variables as that name, no matter how much I wished and tried. Adding insult to injury, none of them are obviously transmutable to a pin integer; in other boards, a pin labeled
D0 is referred to in code as the integer 0, but not in the case of my Micro.
To the rescue comes the main Arduino page for the
Micro, from which the image to the right came. Notice that here the
RX and
TX pins have a "0" and a "1" on the graphic. Using those numbers in the pin assignments finally had me receiving the rotary encoder codes. From the code snippet below, you can see how simple it is to read the status of the pins from which you can then use some conditional logic to change the rotary encoder Gray code into a directional value (CW or CCW), a simple "turns" counter, or whatever strikes your fancy. I strongly recommend that you use a preprocessor macro or const int variable assignment, as I have below, to give the pin numbers a more friendly name. If your code ports to a new board or your hardware design changes, we don't want to try and hunt down all references to "0" and "1" in function calls to change them. If the "pin name" is assigned a value once, it only needs to be changed once to adapt to the changing hardware.
Using the SQ9NJE Rotary Library
Once I resolved the pin reference problem, using this library is quite literally a no-brainer. Include the library in the code header, then simply instantiate the Rotary object, also in the header. Reading the directionality of the encoder is accomplished in the main (setup or loop) code sections. The library is exceedingly simple; there is only the one constructor,
Rotary(pinA, pinB) and one function to read data,
process(). The process() function only reports the direction of the encoder rotation, but one can easily use it to track the count of turns as each time the function is read, the encoder has moved one increment of rotation. Here's a snippet that shows how one uses the library.
Nota Bene: the Rotary.h header file says that there is a third return value for the process() function, DIR_NONE, presumably to indicate that the encoder is in some undecipherable state, but the actual library code can never return this value as written.
We'll just have to wait and see how I actually employ the encoder in final signal generator. We'll also have to wait for the next post (Signal Generator Part 2b), 'cause I ain't done with my rotary encoder frustration.