I'm working on a project where I'd like to read five rotary encoders at once. Is that's going to cause a lot of trouble? How can I do this?
This is the encoder I wish to use:
For my project, I will be using the five encoders to control five of the pots on one of those six channel digital pots. The reason I need digital is that there will be presets to create and load. This is pretty much the entire point of the project.
Yes, it's entirely possible to read 5 rotary encoders. Doing it the straightforward way requires 2 input pins per encoder (total 10 pins), or 3 per encoder (total 15) if the encoder also incorporates a push button. Wire the centre pin of each encoder to ground and the other 2 pins to digital inputs, with the internal pullup resistors enabled. The push button goes between ground and another input, again with the internal pullup enabled.
Alternatively, you can multiplex them by using 1 output pin per encoder, plus 2 common pins (or 3 if you want to include the push button). Total 7 or 8 pins. Wire like this:
centre pin of each encoder goes to its own output pin
left and right pins of each encoder each go to cathode of a signal diode e.g. 1n4148
all the anodes of the left-pin diodes go to the same input pin with internal pullup enabled
all the anodes of the right-pin diodes go to another common input pin with internal pullup enabled
if there in a push button, connect one side to the centre pin, the other to cathode of a signal diode, and connect all the corresponding anodes to a third input pin
If you are short of pins, you can use the same multiplexing arrangement but save 2 pins by using a 74HC138 3-to-8 decoder. Use 3 output pins to drive the 74HC138, and connect the encoder centre pins to 5 of the 8 outputs of the 74HC138.
You'll need to poll the encoders quite rapidly to avoid losing encoder states - probably best done in a timer interrupt. You may be able to share the output pins with anything else you need to multiplex, e.g. 7 segment LEDs.
Rotary encoders output a 2-bit Gray code, so they don't need to be debounced in the usual way if you interpret the output correctly. I have some code which I can post when I find it.
Keep in mind those mechanical encoders have real contact bounce problems that has to be dealt with either external hardware or with software. If you will have problems reading five that are all turning is all about the rate of pulses arriving (pulses per revolution X RPM), it's quite possible you will have a challenge on your hands. What will be turning those encoders all at the same time?
Ok cool I just have a few questions... It looks to me that this method will read all the encoders at once, even if moving at the same time. Which is good. Correct me uf that isn't the case.
Next, I will be multiplexing the bar graphs that correspond to each encoder/pot so I can't have any lag when reading them. Will that be an issue? I don't have a ton of experience but fir instance I found that shiftIN registers mess up multiplexing a lot. Will I have this issue with the 74HC138?
What is a 74HC138? Is it anything like a 595? And again, will I have lag issues?
They will be hand turned so I can't imagine more than two at a time being turned. What are my options for dealing with bounce?
The shiftin function took a long time. So whenever I chose to call it, all my multiplexing would halt for a fraction of a second. Just long enough for it to be noticeable. It was even more of an issue with momentary buttons because I had to call it so often. I wasn't using SPI so maybe that was my problem.
It sounds like your problem is that you were not doing the multiplexing correctly. You shouldn't be doing it continuously but at a specific rate and you should be slotting the shift in in the slack display time.
Alternatively you should have the multiplexing kicked off by interrupts, the shift in is quite happy to be interrupted.
Hmmm... maybe I just don't really know what I'm doing well enough. I would use a blink without delay sort of thing to multiplex, so it would spend some amount of time just scanning through digits, in this case, then every so often jump out to do whatever else, like check time or something. Is that not the correct way of doing it? My problem would be that every time I took a pause from multiplexing, it would literally pause long enough to be noticeable.
Pseudo code:
loop{
if(100 milliseconds has gone by){
call shiftin fucntion;
do whatever else;
}
display number for 1st digit;
delayMicroseconds(50);
display number for 2nd digit;
delayMicroseconds(50);
etc;
}
I would get a pause every 100 milliseconds. I have not worked with interrupts, is that the key i am missing here? Here is my original thread about it.
Anyway, I'm still interested in the rotary encoders. I would like to save pins by multiplexing them, will that create additional issues with filtering out the bouncing?
I suggest you use the same output pins to multiplex both the bar graphs and the rotary encoders, and use a timer interrupt to do the multiplexing.
The 74HC138 is a 3-to-8 line decoder with no internal state, so unlike a shift register, it doesn't impose any delays. With 3 inputs you can multiplex up to 8 bar graph displays and 8 rotary encoders. You only need it if you can't afford 1 pin per (encoder, bar graph) pair. It has active low outputs, which will need to be buffered to drive the bar graph, so it's better suited to driving common anode displays.
Rotary encoders don't need the sort of debouncing that ordinary switches do, if you use them as intended (i.e. decode the Gray code outputs) and if your application doesn't mind seeing the switch move forward, back and forward again in a short space of time (which is how a bounce may appear). If it does mind, then thay can be easily debounced in software if you are polling them on a regular basis in a timer ISR. You only get a problem if both contacts bounce at the same time, which shouldn't happen in a good quality encoder because the two contacts make at different times.
Just for the record that is totally the wrong way to go about multiplexing. The multiplex refresh routine should be called every 50mS in your case in the blink without delay fashion. A variable should keep track of what display to refresh each time the routine is entered. There should be no delay commands anywhere in your code.
Read this:- http://www.thebox.myzen.co.uk/Workshop/LED_Matrix.html
for a more detailed explanation, it deals with a matrix but applies equally well to what you are doing.
Grumpy_Mike:
Just for the record that is totally the wrong way to go about multiplexing. The multiplex refresh routine should be called every 50mS in your case in the blink without delay fashion. A variable should keep track of what display to refresh each time the routine is entered. There should be no delay commands anywhere in your code.
Read this:- http://www.thebox.myzen.co.uk/Workshop/LED_Matrix.html
for a more detailed explanation, it deals with a matrix but applies equally well to what you are doing.
Absolute rubbish I beg to differ on several counts:
50ms is far too long a time when multiplexing rotary encoders because you will miss state changes.
50ms as a refresh interval for LED displays is only 20Hz and will almost certainly lead to noticeable flicker.
when multipexing LED displays, to get sufficient brightness you may need to drive them at a higher current than their maximum continuous rating, which means working within the pulsed current rating - which is usually specified for much lower times than 50ms, e.g. 0.1ms for the 7-segment displays I am currently working with
in a complex sketch, trying to do everything in loop() is a bad idea, you need to break things out into separate functions, and the multiplexing must go on even if those functions take a while to execute (e.g. because they are waiting for something).
Doing the multiplexing in loop() as you suggest is only practical for simple sketches. For more complex sketches, you need to avoid having calls to the multiplex function littered throughout the code. In the absence of an RTOS, it is far more satisfactory to use a tick interrupt to do the multiplexing. You get an even and consistent multiplex interval, which avoids display flicker and makes for reliable reading and debouncing of switches and rotary encoders.
In one project I used another scheme, which is to write my own verson of delay() that keeps track of the last time the multiplex function was called, and calls the multiplex function whenever the required multiplex interval has been reached or exceeded. The multiplex function is also called in any wait loops if the multiplex interval has been exceeded. This isn't ideal, because library functions that include wait loops (e.g. Serial.print) or calls to the original version of delay() suspend the multiplexing.
You know if you were half as good as you think you are you would be brilliant.
I did not suggest 50mS for multiplexing the rotory switches, that figure came from the OPs psudo code as the rate he was multiplexing the digis.
Grandmother and sucking eggs comes to mind with your advice, remember the skill is in tailoring advice to the actual situation. Maybe you will grow up soon.
Ok, I see my issue. That makes much more sense. However still that shift in function took a good couple tenths of a second or so, so it still seems like it would have been a problem. Is that where interrupts come in?
Grumpy_Mike:
I did not suggest 50mS for multiplexing the rotory switches, that figure came from the OPs psudo code as the rate he was multiplexing the digis.
You said that my suggestion of using a tick interrupt to multiplex the LEDs and the encoders "is totally the wrong way to go about multiplexing", and suggested instead to multiplex every 50ms in loop(). How is anyone supposed to interpret that as not meaning that the encoders get multiplexed (and therefore polled) every 50ms?
Grumpy_Mike:
Grandmother and sucking eggs comes to mind with your advice
You may think you know it all, but others reading this forum may wish to know the reasons that I give the advice I do.
Grumpy_Mike:
remember the skill is in tailoring advice to the actual situation.
We don't know the actual situation that the original poster is aiming for, but the presence of all those pots and rotary encoders pretty much guarantees that it is not nearly as simple as your LED matrix sketch that you linked to. Besides, the solution of using a tick interrupt to do the multiplexing is scalable from small to large sketches. Trying to achieve a regular poll interval in loop() is not, except at very low poll rates. Unless you are certain that the sketch you are writing will stay simple, it is better to use a scalable solution from the start.
stoopkid:
Ok, I see my issue. That makes much more sense. However still that shift in function took a good couple tenths of a second or so, so it still seems like it would have been a problem. Is that where interrupts come in?
You haven't said what you mean by your "shift in" function - can you explain and/or post the code? If you do need to call a function that takes a couple of tenths of a second, then using a timer interrupt for the multiplexing will ensure that the multiplexing (and hence the rotary switch polling) continues during that call. The caveat is that any functions you call that disable interrupts for long periods (SoftSerial?) will suspend the multiplexing during those periods.