Servo Timming range
Up to Project Discussion
Hello,
I got my tachometer working using a fasttimer. Thank you Aaron and Liam for the hints.
Now I'm having jitter issues with the servo system. I fairly sure my tach fasttimer is messing with my servo timer. Does anyone know what timming ranges the servo timmer will run at, and/or a value I can set a fasttimer to that is under 1000 and won't interfere with the servo's fasttimmer.
I believe the answer to my question is in servo.c, in ServoIRQCallback().
I'm not sure what the period is set to in the following.
ServoControl* s = Servo->control[ Servo->index ];
period = s->position >> 6;
s->pIoBase->PIO_SODR = s->pin;
FastTimer_SetTime( &Servo->fastTimerEntry, Servo->gap + ( SERVO_CYCLE - period ) );
Servo->gap = 1882;
SERVO_CYCLE = 2048;
but what is the period?
Has anybody solved this problem before?
Thanks,
Ryan
sorry, bro
I should have mentioned that fasttimer is used by the servo subsystem. You'll get jittering because the two try to interfere, I twiddled around with this for quite a while inside the servo firmware before I just gave up and added a second controller to the project, I was on a deadline and had to just make the project work.
which it did in the end.
Is there a single write method of turning on the servo line? I describe this more in the Tachometer forum.
Previously Aaron Tunell wrote:
You'll get jittering because the two try to interfere,
Did you figure out what the source of interference is? Jitter, I can understand, if two fasttimer events are supposed to go off simultaneously or nearly so, and that alone might be a showstopper for the proposed tachometer application. One callback would have to wait for the other to complete before it can start. Is that all it is, or is there more to it?
I don't completely understand the FastTimer code, but I do understand enough of it to be able to discuss it.
Yes, the underling issue is the two timmers trying to fire at the same time. If you use a fasttimer with the servo system you will have interference. The way around this is to put your servo controls in the same fasttimer call back function. I have one that runs at 200 microsecond intervals, and it almost works perfectly. By almost I mean there are small dead spots where you don't get servo response around the interrupt time. My next approach is to use the PWM system to run the servos. Does anybody know what to set the clock divider to to get a 20ms period?
The existing PwmOut system in the firmware does not appear to give you enough control. Since you mentioned setting a clock divider, I assume you're talking about accessing the PWM hardware directly, anyway.
Here's the back of my napkin.
I want my position values to agree with the original firmware, 'cause I'm funny that way, so that means I want 1us per PWM input clock. To get that, I need 1MHz, which you can't get by dividing 48MHz by a power of 2, so I have to use CLKA or CLKB. I'd probably just select MCK/16 for the prescaler and put 3 in the divider. Alternately, you could select MCK for the prescaler and put 48 in the divider. That might consume a little more power, but only trivially so.
Then, getting a 20ms frame just means you put 20000 in PWM_CPRDx and you put your desired position in PWM_CDTYx (which will always be smaller than 20000).
You'll be on your own if you want to have the sort of ramping that is provided by the usual servo driver. But, then, you could catch an interrupt from the overflow of the PWM (every 20ms) and do it there. For instaneous position changes (subject to mechanical limits of the servo, of course), though, writing to PWM_CDTYx ought to do it.
Previously Liam Staskawicz wrote:
It would be nice to create an abstraction for setting the frequency of these kinds of systemsYup. It has the potential to get thorny, though.
Just for example, what if the requested rate/period cannot be met due to granularity issues? Something I've done in the past is to have the function to select a certain speed/period set the closest possible answer and have another function to retrieve the actual effective speed/period, which may well be different than what was requested due to rounding. The caller can compare that to what they wanted and decide if it is close enough. They can always set it back to the previous value and abort if the new one isn't good enough.
Also, there are cases where any of the usual three rounding methods may be desired in the calculation. I run into that a lot with things like setting up the PLL, where you really don't want it running too fast. Either by having three functions or by having one with a parameter to specify rounding would be in order. In the PLL case, of course, I'm always rounding down (or up, depending on how you look at it), but the whole idea of an abstracted API is to make it work in as many cases as possible.
Actually, in the PLL case, I couldn't do it with straightforward math at all. I ended up writing a Perl script to try all MUL and DIV combinations and show me the best ones. As that only needed to be set during low-level setup, and assuming the crystal isn't changed (which it couldn't easily be on the MC), that was fine.
This sort of thing seems to be an obvious thing to add, at least upon initial consideration, to the existing pwmout driver, as it already exists and as any new PWM stuff has to share the resources, anyway. The TC, of course, not having a driver, would be a start-from-scratch kind of thing.
I think you've mentioned a desire for a proper interrupt API, as well, which I agree with. In that case, that would even suggest things like being able to do userlike things with the PWM and TC interrupts. So you'd want such an API to handle routines for any of several PWM channels as well as regular IRQ handling, which is different. That pretty much means that the interrupt API would As PWM uses 1 interrupt line for all 4 channels, that becomes a simple dispatch table in a common first-level interrupt handler. TC uses 1 interrupt line per channel, so that ought to be as straightforward as any other interrupt.
That, in turn, suggests that the interrupt API might be integrated with the devices that use interrupts. So the function to register a PWM rollover interrupt would be part of the PWM API, which makes sense since the first-level interrupt handler would be a common (though largely invisible) part of the PWM API anyway. The other way, of course, would be to make the interrupt API completely separate. Nothing stops device API functions from simply calling general interrupt API functions with or without preprocessing.
I have not gotten so far as to perform these kinds of abstractions in my own little OS. Yet. Well, I do sort of have ISRs abstracted. Into a C++ class. But, then, my interrupt system is a little weird. I assign each interrupt to use the stack of the thread that created it.
Rather than the stack of whatever was running at the time or a global interrupt stack. That causes a little extra interrupt overhead, but not as much as one might think. It is a tradeoff between some extra latency and being able to be a bit more deterministic about stack sizing.
The hardest thing about developing such an abstraction layer is trying to predict all of the ways a user might want to use it and accomodate all of them as uniformly and as generally as possible without having a bunch of weird special cases. Sometimes, you just have to make the user responsible for certain things (such as making sure the PWM duty cycle is less than the total length) or spend extra CPU cycles doing checks.
If you need under 1000 us resolution from your timer, you need to put you bit wiggling in the servo timer. I put my servo driver and tach functions in the same fasttimer isr. It's not perfect, but it works. I did this by hacking up a copy of Servo_IRQCallback, that is run from a fasttimer in one of my tasks. If you have access to a scope, a heartbeat signal is very helpfull. It is a little wierd as to how the servo system works depending on where you start it from. If you run it from the osc level it works buetifully, but I found it was hard to get it to work right from the c level. It has to do with Servo_IRQCallback handling only 4 4ms blocks , one per servoline. The problem is 4 x 4 is 16, not 20, 4 X 5 is 20 the base period of the servo signal. Somewhere in initialization with OSC the 5th chunck gets handled. I just went around the problem before I completely figured it out. As a wise old white haired man once told me "If you find a pothole, don't find out how deep it is unless you have to. Just put a warning flag in it so you don't fall in the next time you come down that road."
I think using the PWM to drive the servos is the best approach if you need to use the fasttimer. I still havn't had the time to figure that path out yet, but I hope to soon.
Good Luck
I figured out how to make the pwm drive a servo.
in pwm.c change
// AT91C_BASE_PWMC->PWMC_MR = (( 4 << 8 ) | 0x08 ); // MCK selection or'ed with Divider
to
AT91C_BASE_PWMC->PWMC_MR = 0x30 ; // MCK selection or'ed with Divider 48
then
//pwm->PWMC_CPRDR = PWM_DUTY_MAX;
to
pwm->PWMC_CPRDR = 20000;
Then drive the duty from 1000 to 2000 for your position. This generates nice 20ms servo signal pulse.
Previously Ryan Chard wrote:
I figured out how to make the pwm drive a servo.
Yup.
You are driving the servo control lines from even (or odd) numbered digital outs and ignoring the others, right? Either that or using the prototyping board and using the PWM lines more or less directly.
For incredible resolution and zero jitter, hardware PWM is the way to go. Too bad there are only 4 PWM channels on the chip.

