Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Servo jitters

Status
Not open for further replies.

Wilko

New Member
I have finally got my code working but my servo jitters really badly. I'm using a 20ms interrupt to start my pulse and then a second interrupt running at 100us and counting it 11 times before turning my pulse off. Is this the wrong way to go about it? I'm using a 20MHz crystal.

#include <p18f4620.h>
#include <timers.h>
#include <stdio.h>
#include <delays.h>


void power (void);
void signal (void);
void HandlerHigh (void);
void HandlerLow (void);

unsigned char clock = 0;
unsigned char clock2 = 0;
unsigned char delay = 0;
int servo_on = 0;

void main (void)
{
int Time = 0;

INTCON = 0x20; //disable global and enable TMR0 interrupt
INTCON2 = 0x84; //TMR0 high priority
IPR1 = 0x00; //TMR1 low priority
RCONbits.IPEN = 1; //enable priority levels
INTCONbits.GIEH = 1; //enable interrupts
INTCONbits.GIEL = 1; //enable low interrupts
TMR1H |= 0xfe;
TMR1L |= 0x10;
WriteTimer0(61);

power(); // Led output to show pic has power

OpenTimer0(TIMER_INT_ON & T0_8BIT & T0_SOURCE_INT & T0_PS_1_256);
OpenTimer1(TIMER_INT_ON & T1_8BIT_RW & T1_SOURCE_INT & T1_PS_1_1);
WriteTimer0(61);

while (1)
{
signal(); //send out servo signal
}
}

void signal (void)
{
if (servo_on == 1)
{
servo_on = 0;
PORTAbits.RA1 = 1;
delay = 1;
clock2 =0;
}

if ((delay == 1) && (clock2 == 11))
{
clock2 = 0;
delay = 0;
PORTAbits.RA1 = 0;
}

}

/*Set a Led to show PIC is running*/
void power (void)
{
ADCON1 = 0x0f;
TRISA = 0x00000110;
PORTA = 0;
PORTAbits.RA0 = 1;
}


//----------------------------------------------------------------------------
// High priority interrupt vector

#pragma code VectorHigh = 0x08
void VectorHigh (void)
{
_asm
goto HandlerHigh //jump to interrupt routine
_endasm
}

//----------------------------------------------------------------------------
// High priority interrupt routine

#pragma code
#pragma interrupt HandlerLow

void HandlerHigh (void)
{
if (INTCONbits.TMR0IF) //check for TMR0 overflow
{
INTCONbits.TMR0IF = 0; //clear interrupt flag
WriteTimer0(61);
clock++;
Nop();
}
if (clock > 1)
{
clock = 0;
servo_on = 1;
}

}
//----------------------------------------------------------------------------
// Low priority interrupt vector
#pragma code VectorLow = 0x18
void VectorLow (void)
{
_asm
goto HandlerLow
_endasm
}

//----------------------------------------------------------------------------
// High priority interrupt routine
#pragma code
#pragma interrupt HandlerHigh
void HandlerLow (void)
{
if (PIR1bits.TMR1IF) //check for TMR1 overflow
{
PIR1bits.TMR1IF = 0; //clear interrupt flag
TMR1H |= 0xfe;
TMR1L |= 0x10;
clock2++;
}
}
 
Gosh, there are many ways to produce a jitter free low-overhead hi-rez' Servo pulse with a 20.0 msec period without tying up two Timers.

Let me study your code and get back to you Sir.

Please tell us what Clock frequency you're using?

Mike
 
Last edited:
I'm using the PIC18F4620 but I've wired in a 20MHz oscillating crystal. This I believe gives me a 0.2us per instruction.
 
Blatant AVR plug =)
Just use an AVR at 20mhz, get a 50ns instruction clock =)
Can't help you with the code I'm afraid, it's C. Timeing sensative applications (even interupt based) Are best done in assembly.
 
Sceadwian,

Yes, AVR devices have some incredibly nice features. You can get 50 nsec Servo pulse width resolution with a 20-MHz PIC by using the PWM module to develop the Servo pulse but I'm not certain that kind of Servo pulse width resolution is really necessary or perhaps even desirable.

Wilco,

I suspect there may be a few problems with your code and method.

One thing I noticed is you're trying to use 'or' instructions in the ISR to update the TMR1 register pair and you're probably not getting the result you're expecting. You might temporarily turn off Timer 1, write your 16 bit value to the TMR1 register pair, then turn Timer 1 back on but this still leaves you with screwed up 100 usec timing. You might want to consider using set-and-forget Timer 2 interrupts for your 100 usec timing or investigate the Timer 1 "special event trigger". Both methods could provide the reliable 100 usec timing you're looking for.

I suspect a Timer 0 problem in your code too. When you write the TMR0 register it clears the prescaler and you loose counts.

Have you considered using a single 100 usec Timer 2 interrupt and a counter in your ISR instead of your current method?

Increment the counter each interrupt and reset the counter to 0 after 200 interrupts (20 msecs). Set the Servo pin in the ISR when the counter is 0 and clear the pin in the ISR when the interrupt counter = 11.
Code:
//
//  pseudo ISR code
//
void ISR ()
{
    Counter++;            // bump 100 usec counter
    If (Counter = 200)    //
    {
      Counter = 0;        // new 20-msec period
      LATAbits.RA0 = 1;   // pulse on
    }
    if (Counter = 11)     //
    {
      LATAbits.RA0 = 0;   // pulse off
    }
    PIR1bits.TMR2IF = 0;  // clear Timer 2 interrupt flag
}
If you absolutely must use Timer 0 then you should use a 1:1 prescaler and add the period value to the free running TMR0 register to keep from losing counts;

Code:
//
//  pseudo ISR code
//
void ISR()
{
    INTCONbits.TMR0IF = 0;  // clear timer 0 interrupt flag
    TMR0 += 256-250;        // 250 x 200 nsecs = 50 usec interrupts
    Counter++;              // bump counter (integer)
    If (Counter = 400)      // if end of 20 msec period
    {
      Counter = 0;          // new 20-msec period
      LATAbits.RA0 = 1;     // turn on pulse
    }
    If (Counter = 22)       // if 1100 usecs
    {
      LATAbits.RA0 = 0;     // turn off pulse
    }
}
Good luck with your project.

Mike
 
Last edited:
BRILLIANT :)

Works perfectly. Thank you very much Mike. I used timer 2 and copied your code example.
 
Cool. Glad to help.

Are you really only using ten steps for the entire 1.0 to 2.0 msec swing range of the Servo?
 
Status
Not open for further replies.

Latest threads

Back
Top