CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

50Hz/20ms software PWM for Servo!

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Izzy



Joined: 27 May 2007
Posts: 106

View user's profile Send private message

50Hz/20ms software PWM for Servo!
PostPosted: Wed Jan 07, 2009 8:48 pm     Reply with quote

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

View user's profile Send private message

Re: 50Hz/20ms software PWM for Servo!
PostPosted: Thu Jan 08, 2009 7:35 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Jan 08, 2009 8:52 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Jan 08, 2009 10:56 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Jan 08, 2009 12:25 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Jan 08, 2009 2:37 pm     Reply with quote

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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group