View previous topic :: View next topic |
Author |
Message |
Izzy
Joined: 27 May 2007 Posts: 106
|
50Hz/20ms software PWM for Servo! |
Posted: Wed Jan 07, 2009 8:48 pm |
|
|
Hello,
I have searched and read few of the codes here in this forum for the servo motor. But instead of using them I wanted to write my own small code for controlling servos.
The problem is, it's not working. I would appreciate if someone could go over and see what's wrong here.
The code is very simple. It uses two timers. One timer will over flow after every 20ms (50Hz) which will set the PWM frequency and the other one will over flow after every 1ms/2ms which will set the pulse width of the PWM. So the 20ms timer will turn the pin high and the pulse width timer will change the pin back to low. So it will looks like a PWM.
___| |______| |______| |______
Code: | #include <18F4685.h>
#device ICD=TRUE
#use delay(clock=40,000,000)
#fuses HS,NOLVP,NOWDT
#use rs232(stream=debug, DEBUGGER)
int16 Servo_pulse_width=63661;
#INT_TIMER1
void pulse_freq()
{
enable_interrupts(INT_TIMER3);
set_timer1(40536); // So that the interrupt will overflow after every 20 ms
output_high(PIN_A6);
}
#INT_TIMER3
void pulse_width()
{
disable_interrupts(INT_TIMER3);
set_timer3(Servo_pulse_width); // So that the interrupt will overflow after every 1ms/1.5ms/2ms
output_low(PIN_A6);
}
#INT_EXT // Push buttons to change the direction
Void command()
{
disable_interrupts(int_ext);
delay_ms(200);
Servo_pulse_width = 64286; //to interrupt after every 1ms
fprintf(debug,"Right\n");
clear_interrupt(int_ext);
enable_interrupts(int_ext);
}
#INT_EXT1 //Another push buttons to change the direction
Void command1()
{
disable_interrupts(int_ext1);
delay_ms(200);
Servo_pulse_width = 63036; //to interrupt after every 2ms
fprintf(debug,"Left\n");
clear_interrupt(int_ext1);
enable_interrupts(int_ext1);
}
void main()
{
set_timer1(40536); //// So that the interrupt will overflow after every 20 ms
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
enable_interrupts(INT_TIMER1);
setup_timer_3(T3_INTERNAL|T3_DIV_BY_8);
ext_int_edge(0,H_TO_L);
enable_interrupts(INT_EXT);
clear_interrupt(INT_EXT);
ext_int_edge(1,H_TO_L);
enable_interrupts(INT_EXT1);
clear_interrupt(INT_EXT1);
enable_interrupts(GLOBAL);
While(TRUE)
{
}
} |
|
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
Re: 50Hz/20ms software PWM for Servo! |
Posted: Thu Jan 08, 2009 7:35 am |
|
|
One problem is that when Timer 1 overflows, you enable Timer 3 INT, but you do not know where Timer 3 is at the moment. So it could be any length of time before the Timer 3 interrupt hits. Actually, it may already have hit, and the Timer 3 interrupt flag is already set, in which case the Timer 3 INT will hit immediately upon the return of the Timer 1 ISR.
An easier way to do this is to use just one timer, say Timer 1, for both the beginning and the end of the servo pulse. Use X for the first interval and 20msec - X for the second interval (where X is the pulse width). Use a static variable to keep track of which portion of the pulse you are in so that when the Timer 1 INT hits, you can check that variable and decide to either:
1. Set PIN_A6 high and set an overflow time of X, or
2. Set PIN_A6 low and set an overflow time of 20msec-X.
Toggle this variable on each INT.
This will be pretty accurate. However, for even better accuracy, you could use the CCP module in Output Compare mode. But that would require you using the dedicated CCP output pin, not A6. _________________ Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
Izzy
Joined: 27 May 2007 Posts: 106
|
|
Posted: Thu Jan 08, 2009 8:52 am |
|
|
Thanks.
That definately sounds like a better idea.
But I thought this when we disable the timer1, it will completely stop and the following code should restart the timer from the 40536.
Isn't that true?
Code: |
#INT_TIMER1
void pulse_freq()
{
enable_interrupts(INT_TIMER3);
set_timer1(40536); // So that the interrupt will overflow after every 20 ms
output_high(PIN_A6);
} |
|
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Thu Jan 08, 2009 10:56 am |
|
|
Izzy wrote: | Thanks.
That definately sounds like a better idea.
But I thought this when we disable the timer1, it will completely stop and the following code should restart the timer from the 40536.
Isn't that true?
Code: |
#INT_TIMER1
void pulse_freq()
{
enable_interrupts(INT_TIMER3);
set_timer1(40536); // So that the interrupt will overflow after every 20 ms
output_high(PIN_A6);
} |
|
Your problem is not with Timer 1. It is with Timer 3. You enabled the Timer 3 interrupt without setting Timer 3 to a known value. _________________ Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
Izzy
Joined: 27 May 2007 Posts: 106
|
|
Posted: Thu Jan 08, 2009 12:25 pm |
|
|
Hello,
Well it looked like something wrong with PIN_A6. I cahnged to C0 and it KIND OF worked.
I have FUtaba s3003 and it should be 180 Degree servo.
But with 1 timer code I am only getting about 60 degree turn in either way.
Code: |
#INT_TIMER1
void PWM()
{
if(pulse_freq == True)
{
output_low(PIN_C0);
pulse = 106072 - Servo_pulse_width; //// 65536- [(65536-40536) - (65536-Servo_pulse_width)]
disable_interrupts(INT_TIMER1);
set_timer1(pulse);
enable_interrupts(INT_TIMER1);
pulse_freq = FALSE;
}
else
{
output_high(PIN_C0);
disable_interrupts(INT_TIMER1);
set_timer1(Servo_pulse_width);
enable_interrupts(INT_TIMER1);
pulse_freq = TRUE;
}
}
|
And with 2 timer code, I only get like 120 degree turn right from the center for both right and left.
Code: | #INT_TIMER1
void pulse_freq()
{
output_high(PIN_C0);
disable_interrupts(INT_TIMER1);
set_timer1(40536); // So that the interrupt will overflow after every 20 ms
enable_interrupts(INT_TIMER1);
set_timer3(Servo_pulse_width); // So that the interrupt will overflow after every 1ms/1.5ms/2ms
enable_interrupts(INT_TIMER3);
}
#INT_TIMER3
void pulse_width()
{
disable_interrupts(INT_TIMER3);
output_low(PIN_C0);
} |
|
|
|
Izzy
Joined: 27 May 2007 Posts: 106
|
|
Posted: Thu Jan 08, 2009 2:37 pm |
|
|
I got it working, but I had to decrease the pulse width time.
I got centered with ~1.3ms ,
90 degree left with ~0.35ms pulse and
90 degree right with ~2.2 ms.
Do the pulse width requirement vary for different servos? I am using Futaba S3003. |
|
|
|