Topic: Can the Servo lib be made to control more than 12 servos per timer? Maybe! (Read 2128 times)previous topic - next topic

I was taking a look at the Servo() lib, wondering why it was limited to only 12 servos per timer, and though I'm not certain, it seems like there may be room for improvement. (And that is an understatement.)

First, let's discuss how servos work, and why 12 servos per timer is the magic number here.

Servos work by keeping track of the length of time between pulses on their signal line. To keep them in a particular position, every 20 milliseconds, you need to set their signal pin high for between 1 and 2ms. A pulse of 1.5ms, with a low period of 18.5ms, will keep the servo centered. These pulses need to be sent constantly or the servo will stop trying to center itself and move freely instead.

So how does the Servo() lib send these pulses? Well, it looks like it uses the interrupt to time the pulse for only one servo at a time. So, for servo one, it sets the servo's signal pin high, then sets the interrupt to trigger when it should go low again... up to 2ms later. It then moves on to the next servo and does the same thing. The limit on the number of servos comes from the amount of time you have between when you need to send pulses. And the Servo() lib seems to play fast and loose with the standards, allowing up to 24 milliseconds between pulses instead of the usual 20, which give enough time for 12 pulses up up to 2ms each before it needs to repeat the first.

This seems really inefficient to me however.

It seems to me that every 20ms, one could toggle all the servo pins high at once and then turn them off one by one as needed when a particular servo's time is up. Using some sorted arrays could make deciding on how long to wait to trigger the next interrupt very fast. In this way, one could control as many servos as they wanted from a single interrupt. I see no reason why you shouldn't be able to control 64 of them in this manner with just one timer interrupt.

Anyway I'm interested in hearing your thoughts on this. I'm not going to have time myself to test this theory out for a while, and I don't really need more than 12 servos myself, but if one were developing a spider-bot or something and one didn't want to waste their precious timers or use a MEGA, then the ability to control 20 on a standard Arduino might come in handy.

The interface to this one is ugly, but the philosophy is - if you asked me to do something you had a good reason. The ugly part is that it does not enforce a fixed refresh rate and so if you so not set one it will pulse your servos at much higher frequencies - some people want this ability -

the reason it is not done this way is that you would need to loop through the servos at every pin change to see who should go next, with 20 servos that would be a lot of 16 bit operations.

alternativley you could try and sort the servos at the start of each frame but again lots of 16 bit operations.

The current servo library uses around 1% of arduino processing power for 12 servos, my library is more efficient but has a less friendly interface - I would only consider using it if you want those extra servos, a higher refresh rate or smoother RC signal interrupts.

the reason it is not done this way is that you would need to loop through the servos at every pin change to see who should go next, with 20 servos that would be a lot of 16 bit operations.

You are mistaken.

You see this? http://www.youtube.com/watch?v=Klh7bslfpBI

That is a circuit I built, which has an array of 64 multiplexed LEDs. Nothing special about that you might think, and I wouldn't blame you since the video doesn't demonstrate it very well, but if you look closely, you may notice the LEDs that aren't brightly lit are displaying a gradient. That's pulse width modulation. On a multiplexed display. How did I achieve this? By sorting the LEDs so as I illuminated each column of the display I needed only check the time against the next LED to turn off.

Quote

alternativley you could try and sort the servos at the start of each frame but again lots of 16 bit operations.

There's no need to sort the servos each frame as long as you keep your list sorted. When you set a new angle, all you need to do is a quick binary search to the left or the right of where the previous position was in the list to figure out where to insert the value, and you're done. Yes, this requires a few operations. But setting the servo position doesn't need to be super fast. Servos can't respond within microseconds to a change in position anyway. Where speed is important is in the servo interrupt, and that would be super fast with the list already sorted for it.

Fair enough - as arduino is single threaded and you only need to re sort if a servo position changes, you can safely figure out the sort order with interrupts still enabled and then just disable them to do the update - should be smooth and simple.

The advantage of that is that it is inherently PPM, if you output the pwm pulses on the same pin.

The disadvantage is that those pwm pulses are not phase aligned and there are channel limitations.

It is not hard to write pwm output on any arbitrary number of channels. I think I provided one example somewhere. Basically you establish a counter that counts up to a the period. At each increment for the counter, you test for overflow and output pins; At the roll-over of the counter, you re/set all pins.

If you look at the response after mine from scswift, he is quite right, its trivial to sort the servos whenever the angle is updated and then just let the ISR CYcle through a pre sorted table.

The next problem is if you have 20 servos at very similar values, Do you stay blocking in the ISR to set all 20 of them ? What about if each is set to a value 1, 2, 3 or 4 microseconds more than the last ?

5us makes the gears in the servo jiggle around, 10us is a noticable tick at the output shaft. An ISR is going to be around that mark so for 10 or more servos all within 50us of each other, the simultaneous servos approach is going to have a problem - my guess is thats the reason that current approaches are sequential rather than having a simultaneous rising edge.