Code to control R/C servos via Wixel

I would like to announce the first release (v1.01) of my Wixel-Servo-Lib software.

This allows a Wixel to perform the PWM control necessary to move standard R/C servos according to a 1-byte position variable. It requires the exclusive use of the TIMER3 peripheral.

The current version is available at github.com/jpvlsmv/Wixel-Servo-Lib/tree/v1.01 and is currently licensed under the GNU Lesser GPL v3.0.

I’ve tried to make this as space- and time-efficient as possible, but as I can improve it, I will.

It is currently fairly stable, I have tested it in the jm2.c code provided on 3 servos. I welcome suggestions, comments, and will happily review code contributions.

–Joe

Thank you for your contribution, Joe!

I noticed a few things you might want to fix:

In do_T3, there is a comment that says “turn off pullups” but the code it is referring to does not modify P0INP, P1INP, or P2INP, so it will not actually turn off the pullups. Anyway, I don’t think you want to turn off the pullups. Instead, you should probably just drive the line low when you are not sending pulses. This avoids having a floating input that might cause the servo to do unexpected things. This is what we do on our servo controllers.

Suppose two of servo pulses are almost the same width: one pulse is 140 ticks and the other is 141 ticks. The way your algorithm is designed, you would need to stop the 141-tick pulse a mere 5 microseconds after stopping the 140-tick pulse. I’m pretty sure your ISR will take longer than 5 microseconds, so you won’t be able to shut off the 141-tick pulse in time. This blog post by Jan about advanced servo control explains how to overcome that limitation.

–David

You’re right-- I (incorrectly) assumed that the 8051 worked like the ATMega chips, where the pullups are controlled by the I/O bits when the pin is in input mode. I’ll correct the comment :slight_smile:

I was debating what the right behavior is when a servo is disabled. I don’t want to try to sink too much current if wires get crossed, which is why I set the pin into input (high-Z) mode rather than forcing it low. I should probably make it a configurable setting.

Yeah, that probably explains why I get a little glitch when servos are in close position to each other. It’s actually 8 uS per timer3 tick, which if I do my math right corresponds to 160 instructions (24MHz crystal, 12 clock cycles per instruction = 2MIPS). But that’s still fewer instructions than I have in my ISR.

But to restructure the servo timings to counteract that, I think would make the code very complicated. Maybe for a 2.0 release :slight_smile: In the mean time, I’ll document that the servos won’t be very precise if two are “too close” to each other.

Thanks for the suggestions,
–Joe

A minor point: According to the documentation, the CC2510Fx/CC2511Fx CPU runs the 8051 instruction set at 1 clock cycle per instruction, not 12 cycles/instruction as in the original 8051.

I don’t think the ticks are 8 us. The system clock is 24 MHz so it ticks every 1/24 of microsecond. You configured Timer 3 with a 1:128 prescaler, so it should tick every 128/24 = 5.3333 microseconds.

In your code, you wrote:

// Set the timer for 0.9ms and done
T3SET(169);

which implies that the timer ticks are about (0.9 ms)/169 = 5.33 microseconds

You can generate accurate pulses with relatively simple code if you restrict yourself to at most 8 servos. You would divide the 20 ms servo period into eight 2.5 ms chunks, and give each servo its own chunk. You wouldn’t have to worry about sorting or overlapping pulses. Does this make sense?

Most of the instructions supported by the CC2510Fx/CC2511Fx take more than one cycle to execute. The only instructions that take a single cycle are the instructions that operate on the accumulator, registers, and status byte. Anything that accesses normal memory takes at least two cycles. More info is in section 10.4 of the datasheet, which lists every instruction and how many cycles it takes.

–David

Oops, I was misled by the following, on page 39 of the CC2511f32 data sheet, and should have looked more closely at the details of the instruction timings. Thanks for correcting me!