Servo Motor Control Using PWM

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,
I am trying to run a servo control using PWM in phase and frequency correct mode.After programming my AVR (Atmega16), when i run it, the servo only twitches without actually moving. Here is the code below:

#include 
#include 		//HEADER FILE FOR FUNCTIONS LIKE SBI AND CBI
#include 

int main(void) {

//Enable internal pull ups

PORTD=0xFF;

DDRD=0xFF;

//TOP=ICR1;

//ICR1=20000 defines 50Hz PWM

ICR1=20000;

TCCR1A|=(0<<COM1A0)|(1<<COM1A1)|(0<<COM1B0)|(0<<COM1B1)|

(0<<FOC1A)|(0<<FOC1B)|(0<<WGM11)|(0<<WGM10);

TCCR1B|=(0<<ICNC1)|(0<<ICES1)|(1<<WGM13)|(0<<WGM12)|

(0<<CS12)|(0<<CS11)|(1<<CS10);

//start timer with prescaler 1
//with input clock frequency = 2 MHz

while(1) {
	OCR1A = 1000;
	_delay_ms(3000);
	OCR1A = 2000;
	_delay_ms(3000);
}

}

I am using a HS-311 servo which is capable of 90 degree rotation.
My Clock frequency is 2 MHz and prescalar is N=1 so that I get a 50 Hz square wave. I set the TOP=ICR1=20000. I vary the OCR1A value for the different servo positions as shown in the code.

I am following the guidelines as given in this URL:

[url]
http://mil.ufl.edu/~achamber/ser...
[/url]

Please see the attached file which contains the table of waveform generation modes. I am using mode 8.

Please tell me if I am doing something wrong or missing something.

Thanks,
Sumair

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you absolutely sure you are running at 2MHz?

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Steve,

Yes i used the fuse bits in the AVR Studio setup to program the AVR to run @ 2 MHz internal clock.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Got a scope? Are you outputting a pulse? How wide? How often?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just got hold of a PC based oscilloscope from Virtins Technology....still reading up on configuring the scope. So that will take some time till I can measure the PWM output from the OC1A pin.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

I measured the output of OC1A pin and saw that the waveform was proper (50 Hz). Since the servo kept twitching coninously, I thought, probably the gears must have got stuck, so I opened up the servo and found out that the servo had reached its maximim position at one end. Then I quickly re-assembled the servo and manually brought the servo arm back to its minimum position on the other side. On running the code above again, I saw that the servo moved flawlessly till it reached its other end and then stopped moving (it had reached its max position). So thats a relief, my servo works fine. Now I modified the above code again so that servo moves from one position to the other continously. This is what I did.

//-------------------------

#include 
#include 		//HEADER FILE FOR FUNCTIONS LIKE SBI AND CBI
#include 

int main(void) {

//Enable internal pull ups

PORTD=0xFF;

DDRD=0xFF;

//TOP=ICR1;

//ICR1=20000 defines 50Hz PWM

ICR1=20000;

TCCR1A|=(0<<COM1A0)|(1<<COM1A1)|(0<<COM1B0)|(0<<COM1B1)|

(0<<FOC1A)|(0<<FOC1B)|(0<<WGM11)|(0<<WGM10);

TCCR1B|=(0<<ICNC1)|(0<<ICES1)|(1<<WGM13)|(0<<WGM12)|

(0<<CS12)|(0<<CS11)|(1<<CS10);

//start timer with prescaler 1
//with input clock frequency = 2 MHz
while(1) {
  for(OCR1A = 1000; OCR1A != 2000; OCR1A++) {
     _delay_ms(2000);
  }

  for(OCR1A = 2000; OCR1A != 1000; OCR1A--) {
     _delay_ms(2000);
  }
}
}

But the servo behaves the same as explained earlier. It starts at one end, then moves right till the other extreme and stops, while the motor still keeps twitching. My question is this, shouldn't the second for loop cause the servo to move back again to its initial position?

Please help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If the reciprocal of 2.000MHz (2,000,000) is 1 / 2,000,000 = 0.0000005 and the pre-scale = 1, the resolution (minimum) time step and maximum range of OCR1A using the numbers you provided is.

500nS * 1000 = 0.5mS.

And:

500nS * 2000 = 1.0mS

So your PWM is only 1/2 of that which you are thinking it should be. Meaning, you are at best, at the bottom of the servo range when at 1.000mS (0 Degrees) and driving the servo into the hard limit when at 0.500mS.

0.5mS is not in the range of most standard servos, because most servos operate in the range:
[1.0mS <= Pulse Width <= 2.0mS] EQUALS [0.0deg <= Angle <= 90.0Deg].

Try something like...

OCR1A = 2000;
while (1) { 
      while(OCR1A++ <= 4000) _delay_ms(2000); 

      while(OCR1A-- >= 2000) _delay_ms(2000); 
}

If your micro-controller was operating at 1.000MHz, then the numbers you show (1000 & 2000) would be fine. But running at 2.000MHz, you need to double the numbers because you've halved the PWM ON-TIME , which also means that the PWM is being stroked at a rate of 100Hz, rather then 50Hz.

But I haven't taken the time to verify that the repetition rate of the PWM is actually 50Hz; though, it is generally less critical to proper hobby servo motor operation.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Carl, this is the Phase and Frequency Correct PWM mode (mode 8) so the timing is correct, e.g., the PWM frequency is computed as:
2000000 / (2*1*20000) (prescaler 1, TOP=20000) which is 50Hz.

Sumair, I hope you are aware that this loop:

  for(OCR1A = 1000; OCR1A != 2000; OCR1A++) { 
     _delay_ms(2000); 
  } 

takes more than half an hour to execute. Other than that there is no problem with the code so maybe your servo is bad or you are not giving it enough power. Or a bad GND connection maybe.
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lajon wrote:
Carl, this is the Phase and Frequency Correct PWM mode (mode 8) so the timing is correct, e.g., the PWM frequency is computed as:
2000000 / (2*1*20000) (prescaler 1, TOP=20000) which is 50Hz.

Sumair, I hope you are aware that this loop:

  for(OCR1A = 1000; OCR1A != 2000; OCR1A++) { 
     _delay_ms(2000); 
  } 

takes more than half an hour to execute. Other than that there is no problem with the code so maybe your servo is bad or you are not giving it enough power. Or a bad GND connection maybe.
/Lars


So that's what happens when I get up in the morning and just start giving out advice - I "Sleep Talk ???"

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would do a test:
Set prescaler to 1024, OCR1A to 1000 and with LED on the output see if there is a pulse 1 sec and pause 20 sec.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys,

Thanks for your comments. I'll try out your suggestions.
I have one thing to add though, that even I am suspecting a bad GND connection because I am using 2 power sources:

1. To power the AVR through the PC USB connected to my trainer kit. I am feeding the OC1A output from this AVR connected to a servo.
2. The power to the servo is given through a second power source which is through a 5 V adaptor capable of sourcing 500 mA.

I noticed that when I connected the scope to the OC1A pin to see the waveform, the waveform was continously flickering. Do i have to short the GND of the AVR and the GND of the servo power supply to get rid of this problem and also the main problem at hand?

Thanks,
Sumair

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Do i have to short the GND of the AVR and the GND of the servo power supply

Of course.

If you think education is expensive, try ignorance.