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 CCS Technical Support

help needed for PWM for servos

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



Joined: 13 Feb 2008
Posts: 25

View user's profile Send private message

help needed for PWM for servos
PostPosted: Fri Feb 15, 2008 2:04 am     Reply with quote

Hi fellow forumers, I'm a new member in the programming world here. I've got a problem here about setting up PWM signals for multiple servo motors for a robotic arm I've designed.

Servo motors : Hi-Tech HS series servomotors
Microcontroller : PIC16F877A
External Oscillator : 20Mhz

From browsing through the previous posts by other forumers, it seems that servos need a constant 50Hz of PWM signal. I've done some minor modifications to the ones posted by previous forumers to suit my circuit and this is how my code looks. But my servo does not respond to this somehow.

Code:
 
#include <16F877A.h>
#fuses HS, NOLVP
#use delay(clock = 20000000)

#define PWM_PIN  PIN_C0 

#define LOOPCNT 390

int8 widthC;          // center

//-------------------------------
void tick_interrupt(void);
void init_main();
//====================================

void init_main()
 {
   set_tris_a(0x00);            // LED port
   output_a(0x00);
   
   set_tris_b(0xff);            //button port set to all input
   output_b(0x00);
   
   set_tris_c(0x00);            // servo port
   output_c(0x00);
 }

//================================================
main()
{
   init_main();

   widthC = 29;            //1.5 ms duty cycle

   setup_counters(RTCC_INTERNAL | RTCC_8_BIT, RTCC_DIV_1); // (20MHz / (256*4*1)) = 20KHz
   enable_interrupts(INT_RTCC);                            // equals 0.05ms per interrupt
   enable_interrupts(GLOBAL);                              // 0.05ms*390 = 19.5ms(almost 20ms)   
   
   while(1);
}
//====================================
#INT_RTCC
void tick_interrupt(void)
{
static int16 loop = LOOPCNT;
static int8 pulse;

 if(--loop == 0 )                 
   {
    loop = LOOPCNT;
    pulse = widthC;

 if(pulse)
     {
    output_high(PWM_PIN);
    pulse--;
     }
  else
     {
    output_low(PWM_PIN);
     }   
   }

}



Please help me identify where have i gone wrong guys.

Thanks a lot.
Smile
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sat Feb 16, 2008 12:31 pm     Reply with quote

Use the forum's search page to search for the keyword: servo http://www.ccsinfo.com/forum/search.php

Here is one of many topics about servos, and it has sample code:
http://www.ccsinfo.com/forum/viewtopic.php?t=33183
John P



Joined: 17 Sep 2003
Posts: 331

View user's profile Send private message

PostPosted: Sat Feb 16, 2008 4:31 pm     Reply with quote

Thank you for using the code format, which so few people trouble themselves to do!

Just concentrating on the interrupt routine, it looks as if there's a disastrous mistake, which you might have avoided if you made more careful use of indents.

I've rewritten this the way I would do it (which not everyone agrees with, but we each have to find our own style):

Code:

#INT_RTCC
void tick_interrupt(void)
{
static int16 loop = LOOPCNT;
static int8 pulse;

  if(--loop == 0 )                 
  {
    loop = LOOPCNT;
    pulse = widthC;

    if(pulse)
    {
      output_high(PWM_PIN);
      pulse--;
    }
    else
    {
      output_low(PWM_PIN);
    }   
  }
}


What you can now see clearly is that the if(pulse) line is inside the if(--loop == 0 ) routine, and it definitely shouldn't be. All that should happen after if(--loop == 0 ) is initialiizing a servo pulse.

Like this:

Code:

#INT_RTCC
void tick_interrupt(void)
{
static int16 loop = LOOPCNT;
static int8 pulse;

  if(--loop == 0 )                 
  {
    loop = LOOPCNT;
    pulse = widthC;
  }

  if(pulse)
  {
    output_high(PWM_PIN);
    pulse--;
  }
  else
  {
    output_low(PWM_PIN);
  }   
}
soong2020



Joined: 13 Feb 2008
Posts: 25

View user's profile Send private message

PostPosted: Sun Feb 17, 2008 4:10 am     Reply with quote

Thanks PCM Programmer for the link. I'm trying to get to grips with the discussions in it as I'm still new in this.

Thanks John P for the correction. I think i've managed to generate a PWM signal but I'm not sure whether my calculations for the interrupts and timer part is correct though.

I've tested it by connecting the PWM pin into an LED and set the LOOPCNT and WidthC to much higher values and it blinked alternately. But it seems i cant get the calculations correct.

Can anyone comment on this?

Appreciate it
Greg Richter



Joined: 15 Feb 2008
Posts: 18
Location: Atlanta, Tulsa, Asheville

View user's profile Send private message Visit poster's website

PostPosted: Mon Feb 18, 2008 12:39 pm     Reply with quote

Search for Software PWM -- I just posted a tiny snippet that does a very elegant job of this.
_________________
Madness takes it toll; please have exact change.
soong2020



Joined: 13 Feb 2008
Posts: 25

View user's profile Send private message

PostPosted: Fri Feb 22, 2008 10:35 am     Reply with quote

Thanks for the recommendations guys. I've managed to get it sorted out as it finally worked after several tweaks. As I proceeded in expanding the programming, I've encountered another problem as in setting it to check button conditions. I've used Timer1 as a counting timer to check the buttons for a certain interval. But the motor failed to respond to the changes in the button states. Here is how i wrote it:

Code:

#include <16F877A.h>
#fuses HS, NOLVP
#use delay(clock = 20000000)

#define XTAL_FREQUENCY  20000000
#define TIMER1_FREQUENCY (XTAL_FREQUENCY / 40)

#define PWM_PIN1  PIN_A0
#define PWM_PIN2  PIN_A1
#define PWM_PIN3  PIN_A2

#define BTN_CEN PIN_B7
#define BTN_R PIN_B6
#define BTN_L PIN_B5

#define LOOPCNT 400

int32 widthC, widthL, widthR, width0, Ticker;
static int32 loop = LOOPCNT;
static int32 pulse;

unsigned int fl_interrupt, fl_cen, fl_right, fl_left, fl_pulse;

//-------------------------------
void tick_interrupt(void);
void init_main();
void Initialize_RTC(void);
void move_servo();
void check_button();
void TIMER1_isr(void);
//void pwm();
void main();

//====================================

void init_main()
 {
   set_tris_a(0x00);            // servo port
   output_a(0x00);
   
   set_tris_b(0b1111111);            //button port set to all input
   output_b(0x00);
   
   set_tris_c(0x00);            // xx
   output_c(0x00);
   
   fl_cen = TRUE;
   fl_right = FALSE;
   fl_left = FALSE; 
   fl_interrupt = 0;
   fl_pulse = 0;
 }
 //***************************************************************************
 void Initialize_RTC(void)
{
  Ticker = TIMER1_FREQUENCY;                 
  setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 );
                                             
  enable_interrupts( INT_TIMER1 );           
}
//===========================================================================
void check_button()
{
      if(BTN_CEN==0)
      {
         fl_cen = TRUE;
      }   
      if(BTN_R==0)
      {
         fl_right = TRUE;
      }   
      if(BTN_L==0)
      {
         fl_left = TRUE;
      }
}

void move_servo()
{
   if(fl_cen == TRUE)
   {
      fl_cen = FALSE;
      widthC = width0;
   }
   else if(fl_right == TRUE)
   {
      fl_right = FALSE;
      widthR = width0;
   }
   else if(fl_left == TRUE)
   {
      fl_left = FALSE;
      widthL = width0;

   }
}

//================================================
void main()
{
   init_main();
   widthL = 20;  //1.0 ms duty cycle
   widthC = 30;  //1.5 ms duty cycle
   widthR = 40;  //2.0 ms duty cycle
   
   width0 = widthC;
   
   Initialize_RTC();
   
   setup_counters(RTCC_INTERNAL | RTCC_8_BIT, RTCC_DIV_1);
   enable_interrupts(INT_RTCC);                           
   enable_interrupts(GLOBAL);                                 
   
   while(1)
   {
    while (fl_interrupt == 0)
     {
     check_button();
     move_servo();
     
     fl_interrupt = 0;
     }
   }
}
 
//====================================
#INT_RTCC
void tick_interrupt(void)
{
  if(--loop == 0 )             
  {
    loop = LOOPCNT;
    pulse = width0;
  }
 
  if(pulse)
  {
    output_high(PWM_PIN1);
    output_high(PWM_PIN2);
    output_high(PWM_PIN3);
    pulse--;
  }
  else
  {
    output_low(PWM_PIN1);
    output_low(PWM_PIN2);
    output_low(PWM_PIN3);
  } 

}


#INT_TIMER1  //////*************************************************************
void TIMER1_isr()
{
  Ticker -= 65536;                  // Decrement ticker by clocks per interrupt
  if ( Ticker < 65536 )             // If second has expired
  {
      Ticker += TIMER1_FREQUENCY;
      fl_interrupt = 1;
  }
 
}
//******************************************************************************

 


Is this a correct way to write it ? For now it is supposed to by default let the motor stay in central position upon being turned on. After that, it should be checking the buttons for it to move to different positions (Left and Right).

In the future I need it to receive feedback for its positions (as in how many degrees) using ADC and interface with a PC via RS-232 communications and perform some error corrections as well using the P.I.D algorithm.


Really appreciate it for all your comments and suggestions. Very Happy
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