Controlling a servo

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

I've got a ATMega8 clocked at 14.745600MHz and I need to control one servo preferably with a hardware interface (namely Timer1) on OC1A or OC1B pin. But I need it to be 50Hz.

I've once done this by software, but it wasn't very good, and I can do it again, but is there any better way?

Unfortunately, forum search doesn't work.

There are pointy haired bald people.
Time flies when you have a bad prescaler selected.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
//TIMER1 initialize - prescale:8
// WGM: 0) Normal, TOP=0xFFFF
// desired value: 50Hz
// actual value: 50.000Hz (0.0%)
void timer1_init(void)
{
 TCCR1B = 0x00; //stop
 TCNT1H = 0x70; //setup
 TCNT1L = 0x00;
 OCR1AH = 0x90;
 OCR1AL = 0x00;
 OCR1BH = 0x90;
 OCR1BL = 0x00;
 ICR1H  = 0x90;
 ICR1L  = 0x00;
 TCCR1A = 0x00;
 TCCR1B = 0x02; //start Timer
}

thats what the iccavr appbuilder spit out... just init timer1 in 'clear on compare' at 50hz..0x9000 is 20hz, so use whatever piece of that gives .5 to 2.5ms... just change the OCR1A whenever you like

Imagecraft compiler user

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

daqq wrote:
But I need it to be 50Hz.

Does it really need to be? I'm working on a project involving servo control. I wasn't able to figure out how to control the period to be 20ms either. The closest I could get was 16ms (that or 32ms), and I couldn't figure out how to do a reset on match (Using an old Mega163).

I had read that the overall period is not that important. The duration of the on pulse is what the servo reads, and that the "command" is repeated at approximately 50Hz. So I tried it. With the servos I have, this seems to work just fine. 1500us pulse is neutral, with a range of about 900-2100us.

I'm also happy with the shorter period. Since the pulse is a small PWM range, this allows a greater resolution...

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

As long as it is close to 50Hz it will be fine. But if you use a 16 bit timer, getting 50Hz shouldn't be a problem at all.

Regards,
Steve A.

The Board helps those that help themselves.

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

I use fast PWM mode 14, code examples here:
https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=32907
To get 20ms (50Hz) with /8 prescaler:
(F_CPU is 14745600)

ICR1 = F_CPU / 8 / 50; 

That is the 0x9000 magic number in the iccavr appbuilder code above.
You can use, e.g., 1-2ms for the pulse (value to put in OCR1A and/or OCR1B) so from

#define SERVO_MIN ((F_CPU / 8 / 50) / 20))

to

#define SERVO_MAX ((F_CPU / 8 / 50) / 10)

That is a very safe range, more is possible.
/Lars

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

The code I am using has an update rate of 43 Hz (23ms). The 43 Hz varies because 16 servos are being controlled and the 43 Hz is the sum of the 16 1ms – 2ms servo periods. For example 1/(1.5ms*16) = 43 Hz. So in other words, I don’t think the update rate is that critical.

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

From the link you gave me (lajon)

   ICR1  = 0x9C3F; 
   OCR1A = 0x0BB8; 
   OCR1B = 0x0BB8;

Lajon: Can you put 16bit values into regs. just like that? Didn't know it was possible

There are pointy haired bald people.
Time flies when you have a bad prescaler selected.

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

avr-gcc lets you do that. It takes care of the high/low/whichever first byte ordering when reading or writing 16-bit registers. I think it works on ADC, timers and UBRR.

- Jani

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

I'm using this code.
made by Dean (of course)

#define PWM_TOP	20000
#define STOP	1500
#define FWD0	1900
#define REV0	1000	

#define SERVO(channel, value)    \
      switch (channel)           \
      {                          \
         case 0:                 \
             OCR1A = value;      \
             break;              \
         case 1:                 \
             OCR1B = value;      \
             break;              \
      }
 
//********************************************
 
void Servo_init(void)
{
   // PWM mode 14 (fast PWM, TOP=ICR1)
   TCCR1A = (1 << WGM11);
   TCCR1B = ((1 << WGM13) | (1 << WGM12));
     
   // Start timer (no prescaler)
   TCCR1B |= (1 << CS10);
  
   // Clear OC1A/OC1B/OC1C pins on compare
   TCCR1A |= (1 << COM1A1);
 
   // Set timer TOP value
   ICR1   = PWM_TOP; // 50Hz (T=20ms)
 
   OCR1A  = STOP; // 1.5ms
   OCR1B  = STOP; // 1.5ms
  
   // OC1A/OC1B pins as outputs
   DDRB |= ((1 << 1) | (1 << 2));
}

Fcpu = 1MHz
Cpu = Mega 8