I'm sure I'll exclude some vital information here, but here goes:
I am eventually trying to build a controller with 10 rotary encoders. This will be for sending MIDI. I anticipate:
At most two will be turning at any given time
The maximum speed I will turn the encoder is a very quick flick of the wrist (so certainly they won't be turned by motor)
I am testing various software solutions for decoding the encoders now, and have been testing on two quadrature encoders:
One outputs about 16 pulses per rotation
The other is a pricy grayhill encoder which outputs about 1,000 pulses per rotation
I have tried using the Quadrature decoding library for Arduino, and a few of the basic interrupt related ideas from the Arduino tutorial page. I am currently just trying to use an interrupt to check the pulses, and send the value of a counter via serial to my Mac.
When I rotate the encoder relatively slowly, all goes well! The counter increments or decrements quite smoothly and consistently.
The beguiling problem comes when I give it a very fast twist:
It would be my assumption that I am now going through the pulses significantly faster, and so long as the interrupt is keeping up, the count should move through as many decrements or increments in that angle as it would have if I moved slowly. However, this is not the case - often the counter will only move 4 or 5 ticks to say 5 times that number if I had moved slowly. This happens both on the super cheap mechanical encoder and the pricey optical.
This makes me think whatever I'm doing with this interrupt method is simply just not fast enough, and its missing pulses when I'm twisting quickly. Is this is a common problem? Does anything stand out as a glaring error to anyone?
I'd be happy to post code, schematics, parts numbers if that is necessary - first wanted to see if any immediate solutions or suggestions come to mind.
(I'm also open to suggestions of different encoder part numbers or software solutions entirely! Just have to get these guys reading consistently!)
Yes. It is the major problem with rotary encoders.
Make sure that you are not doing anything else that could block the interrupts. You are probably sending something to the serial port. I think parts of that routine block interrupts. Normally this is not a problem but here you are finding it is.
I would stick to the lower resolution one as it is less likely to swamp the system.
Sorry no simple answer to that one as it depends on what other interrupt blocking stuff is going on in the software.
For maximum speed you can go for hardware assist :- http://www.thebox.myzen.co.uk/Workshop/Rotary_Max.html
The problem with an absolute encoder is that you can't have multi turn controls, I think this is what is wanted with the MIDI controller we are talking about here.
I know that interrupts are the preferred way of dealing with rotary encoders, but if there is not much processing going on apart from reading the encoders, then you may like to try a 'tight' loop calling this function for each encoder:
int getEncoderTurn()
{
// return -1, 0, or +1
static int oldA = LOW;
static int oldB = LOW;
int result = 0;
int newA = digitalRead(aPin);
int newB = digitalRead(bPin);
if (newA != oldA || newB != oldB)
{
// something has changed
if (oldA == LOW && newA == HIGH)
{
result = -(oldB * 2 - 1);
}
}
oldA = newA;
oldB = newB;
return result;
}
You will need to add parameters for the pins to use for that controller.
It might just be worth a try. I bet the whole function compiles down to very few clock cycles.
The main problem with a tight loop is when you are off doing the things you do with the numbers you are not in the tight loop.
The other way is to use some hardware (the front half of the circuit I posted) to convert the quadrature signals into step and direction signals. Then you only need cope with half the number of pulses coming in from the encoder. This gives you twice the speed.
@syr123
It would be good if you posted the code so we could see if there are any inefficiencies in it.
Thanks to ALL for the replies so far. This is the last portion blocking me from completing a 3 year project, so getting this figured out will be fantastic!
A few notes:
Ultimately, this Arduino will be doing quite a bit of other activity, so my hope is to have an Atmega dedicated solely to reading the encoders and communicating the values to my main Atmega, which will talk to my Mac via serial. (Any tips on how to do the Atmega to Atmega communication?)
I am absolutely not opposed to a hardware solution that offers dedicated IC decoding of the encoders and increments counters, but my initial investigations made me think it's a VERY expensive route for 10 encoders and takes up a ton of board space versus just using a microcontroller.
That being said, I'm guessing the way I've been thinking about this in the meantime has been flawed - I had been trying to do better at reading pulses by increasing the frequency I was reporting the value with Serial.print, but as Grumpy_Mike has pointed out, this may be blocking interrupts...
I'm not sure whether I have if there are tweaks I could make to improve it for my purposes, or if I'm even going to have enough pins to have my 10 encoders on an ATMega328.
I also think my delay of 10 in the main loop is probably two low and a part of the problem now, I would going to decrease the frequency with which I send this serial message, and see if that improves my encoder responsiveness.
Serial.print is driving me nuts. That's such an essential part of my communication, and folks are always mentioning that it's slow, bloated, etc. I wish there were good information on a faster alternative....
Keith himself suggests this as an improvement to his library:
"Add a function to change the timer speed, hence the frequency of checking the encoder in the ISR. Right now the code works well for hand-driven rotary encoders, but would drop steps horribly for encoders attached to motor shafts, wheels, etc."
The way that library works is all wrong. It is using a timer to generate a trigger into an ISR that then checks the rotary knobs. No wonder it falls over, the whole approach is wrong.
You need to generate a call to the isr from the changes in the output bits of the encoders themselves. In that way when there is no movement there are no interrupts going off, you only fire interrupts when you need them.
Turning digital pins 2 and 3 to input with pull up resistors on.
Attaching a 'LOW' or 'CHANGE' interrupt to each pin, and decoding in the ISR.
A couple questions remain:
How can I get 10 encoders going with this technique? Seems like I'd need 20 interrupt pins. Is there any Atmega chip I can load with the Arduino bootloader that has that many hardware interrupt pins?
If not, and the solution is to use many more microcontrollers, I will need some advice on what to Google around for regarding communicating between the microcontrollers. I feel capable of coding this up, my only concern would be spending too much time on the micros I have dedicated to reading the encoders communicating values to my master controller.
The arduino mega has lots of pins you can use as interrupts, alternatively you could design some external logic to steer the pulses into your interrupt pins.
A 74F148 is an 8 line to 3 line priority encoder. This takes in 8 lines and signals the binary number on the 3 output lines of what ever signal is generating the interrupt. These can be cascaded into a general purpose priority n encoder. Using that you can tell your ISR to the line that has currently changed.
and, any leads on the inter micro communication protocol / techniques?
Personally I wouldn't bother, it's more trouble than it is worth.
That's a bit disappointing to hear. In addition to the 10 rotary encoders, I also have to read:
15 momentary switches
9 continuous hall effect sensors
and send their value via serial to my Mac.
Also, the Arduino will be controlling another IC which runs 27 lines of PWM. It will receive serial and delegate those messages to this other IC.
That being said, I was thinking it'd be good to have a master IC which received serial and sent it to this PWM IC as well as reading the switches and hall effects. Then another micro which read the encoders and send this to the master, since these are so time consuming.
I'd be happy to do this all on one micro, I just feel like I'd be really pushing it with the number of sensors I'm trying to handle...
Why it should be good news, it is simply not worth joining two processors together as all you are doing is creating a bottle neck.
also have to read:
15 momentary switches
9 continuous hall effect sensors
No problem
and send their value via serial to my Mac.
As MIDI or any baud rate serial?
Also, the Arduino will be controlling another IC which runs 27 lines of PWM.
OK so you started off by asking a question and only giving a tiny fraction of the context so that any suggestions made to you were going to be rubbish because no one knew the context. Thus wasting everyone's time. So now that we have some sort of idea what you are wanting we can begin to address the real problem.
A MIDI controller spends most of it's time doing nothing. It is only when something changes does something need to happen. A single arduino is capable of doing all that you want from it providing you design the system correctly. Most of the time projects need not be correctly designed because you can throw enough CPU cycles at the problem, but when you start expecting a lot you have to think about the whole system and not think you can just throw more CPU cycles at the problem in the form of another processor. This is because you then have the problem of communicating between the processors and this one (except the mega) is a poor choice for that because of the lack of hardware resources.
Yes I know you can do these sorts of things but the amount of free CPU cycles you release is minimal and is no match for a proper considered design trading hardware and CPU time.
The other thing you have not done is considered the balance of your requirement with your resources. For example the 9 hall sensors. Nine is an awkward number to deal with, powers of two are good, so if you are going to have 9 it is nearly as easy to have 16 and a lot simpler to have 8.
So the big unknown we still have is these PWM lines. How are these controlled and what chip are you using?