Controlling Hobby Servos

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

Hi clever people

I am trying to control a hobby servo from an ATMEGA16, on PortD.4 (OC1B). 16Mhz Crystal.

I need to set a PWM with a frequency of 50Hz, then adjust the PWM to adjust the servo position.

I followed a tutorial I found here:
www.mil.ufl.edu/5666/handouts/at...

I use codevision, which sets up the timer parameters using a wizard. The tutorial advises me to use the 2000 kHz timer.
(I don't really understand why this is - surely I want a 50Hz timer?)

So hopefully, there should be a PWM signal at 50Hz at this point. I then write to OCR1B to adjust the pulse width.

It totally doesn't work. :(

The servo doesn't move at all.
(the diag LEDs show that the code is being executed, and the uP is alive, just no PWM)

Here's my code:




#include 
#include 

// Declare your global variables here

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTA=0x00;
DDRA=0x00;

// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=Out Func2=Out Func1=Out Func0=Out 
// State7=T State6=T State5=T State4=T State3=0 State2=0 State1=0 State0=0 
PORTB=0x00;
DDRB=0x0F;

// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTC=0x00;
DDRC=0x00;

// Port D initialization
// Func7=In Func6=In Func5=In Func4=Out Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=0 State3=T State2=T State1=T State0=T 
PORTD=0x00;
DDRD=0x10;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x00;
TCNT0=0x00;
OCR0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 2000.000 kHz
// Mode: Fast PWM top=ICR1
// OC1A output: Discon.
// OC1B output: Non-Inv.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x22;
TCCR1B=0x1A;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

PORTB.0 = 1;     // Prove you are alive


while (1)
      {
      
      PORTB.3 = 0;
      PORTB.1 = 1;      //1st LED ON
      OCR1B = 1000;           //Servo 0 degrees
      delay_ms(1000);
      
      
      PORTB.1 = 0;
      PORTB.2 = 1;     //2nd LED ON
      OCR1B = 1500;          //Servo 90 degrees
      delay_ms(1000);
      
      PORTB.2 = 0;
      PORTB.3 = 1;      //3rd LED ON
      OCR1B = 2000;          //Servo 180 degrees
      delay_ms(1000);
      
      
      } //end while 1
      
}  //end main

Questions:
Why use the 2000 kHz timer? Is this cos it gets prescaled down to 50Hz?
Have I done enough setup to get the PWM running?
Is OCR1B the right thing to be adjusting to adjust pulse width?
The tutorial talks of setting ICR1 to 20000.
You will notice from the code that:
ICR1H=0x00;
ICR1L=0x00;

Could this be part of the problem?
In this thread:
https://www.avrfreaks.net/index.p...

and this thread:
https://www.avrfreaks.net/index.p...

There is talk of how to READ from this, and of editing the .h files, but to be honest, I don't understand it.

I notice that I can access OCR1B as a single byte, or as OCR1BL and OCR1BH, but you can't do the same for ICR1H and ICR1L.

So, how would I write the number 20000 (as instructed by the tutorial), across the two registers?

Or is this whole line of enquiry a red herring, taking me nowhere?

Any other ideas that might help?

Thanks once again
Pete

Attachment(s): 

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

Quote:
Could this be part of the problem?
Definitely. With ICR1 being 0 you won't get any output in that timer mode.

Quote:
So, how would I write the number 20000 (as instructed by the tutorial), across the two registers?

ICR1H = 20000>>8;
ICR1L = 20000; 

But:
16 MHz, Prescaler 8, ICR1 20000 => 100 Hz

Stefan Ernst

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

Quote:
So, how would I write the number 20000 (as instructed by the tutorial), across the two registers?

ICR1 = 20000;

Regards,
Steve A.

The Board helps those that help themselves.

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

The tutorial from your first link is writen for Phase and Frequency Correct mode.
In your code you have choosen Fast PWM mode.
Then you have to multiply values ICR1 and OCR1B by 2.

ICR1 = 40000 for period 20 ms (50 Hz)

OCR1B = 2000 for servo left
OCR1B = 3000 for servo in center
OCR1B = 4000 for servo right

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

sternst wrote:
Quote:
Could this be part of the problem?
Definitely. With ICR1 being 0 you won't get any output in that timer mode.

Quote:
So, how would I write the number 20000 (as instructed by the tutorial), across the two registers?

ICR1H = 20000>>8;
ICR1L = 20000; 

But:
16 MHz, Prescaler 8, ICR1 20000 => 100 Hz

Awesome, thanks!

The servo now moves!

It doesn't move correctly, probably because of the prescaler giving me 100Hz, but I will sort that out eventually.

Thanks very much!
Pete

:D

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

Visovian wrote:
The tutorial from your first link is writen for Phase and Frequency Correct mode.
In your code you have choosen Fast PWM mode.
Then you have to multiply values ICR1 and OCR1B by 2.

ICR1 = 40000 for period 20 ms (50 Hz)

OCR1B = 2000 for servo left
OCR1B = 3000 for servo in center
OCR1B = 4000 for servo right

Hey, thanks! That works really well!

I noticed that the tutorial was for phase + frequency correct, but other threads on here suggested that fast pwm was the way to go.

Now I have the options, I can try them both and see which is better.

děkuji
Pete