Mengontrol hingga 8 buah servo (multi servo) menggunakan AVR ATmega32

Menggerakkan servo dengan mikrokontroler AVR/atmega biasanya menggunakan PWM (Timer) namun. Namun pada chip AVR seperti ATmega8 hanya memliki 3 pin OC (output dari comparator) juga ATmega16, 32 memiliki 4 pin OC,  ATmega328 5 pin OC.

Untuk menggerakkan servo lebih banyak (multi servo) dari ketersediaan pin OC, bisa digunakan metode dua timer interrupt, yaitu :

  1. Timer1 berfungsi membangkitkan frekuensi 50Hz (periode 2ms) yang ditetapkan spesifikasi servo umum.
  2. Timer0 berfungsi mengatur lebar pulsa untuk masing-masing servo.

pengaturan

Servo memiliki sensor resistansi (potensio) untuk mendeteksi posisi dari aktuator. Terkadang ada servo yang nilai resistansinya berbeda-beda. untuk itu perlu diatur nilai offset dan tick-nya. berikut ini bebera variabel yang harus diatur sebelum digunakan:

Mengatur servo dengan atmega / AVR menggunakan codevision/Atmel studio harus memperhatikan bilangan pecahan, jadi pastikan hasil perhitungan posisi servo tepat.

1
2
3
4
5
6
7
8
9
#define F_CPU 16000000L
 
#define jumlahServo 8       //Servo 1 = pin 0, servo 2 = pin 1
#define portServo PORTD
 
//kalibrasi
#define servoTickOffset 1200
#define servoTickMinimum 0
#define servoTickMaksimum 4000L

berikut listing programnya: (AVR Studio 6.2, ATmega32A)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#define F_CPU 16000000L
 
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>
 
#define jumlahServo 8    //Servo 1 = pin 0, servo 2 = pin 1
#define portServo PORTD
 
//kalibrasi
#define servoTickOffset 1200
#define servoTickMinimum 0
#define servoTickMaksimum 4000L
 
volatile uint16_t servo[jumlahServo];
#define servoPortMask 0xFF >> (8-jumlahServo)
 
ISR(TIMER1_COMPA_vect)
{
  portServo = 0xFF;
  TCCR0 = (1<<WGM01) | (1<<CS00); // CTC, prescale 1, top ocr1a
}
 
ISR(TIMER0_COMP_vect)
{
  uint8_t nilaiPort = 0x00;
  uint8_t byteNilai;
  for(uint8_t i=0;i<jumlahServo;i++)
  {
    uint16_t tick = servo[i] * (uint8_t)((servoTickMaksimum - servoTickMinimum) / 180);
    if(tick + servoTickOffset < TCNT1)
    {
      byteNilai = 0x00;
    }
    else
    {
      byteNilai = 0x80;
    }
    nilaiPort = byteNilai | (nilaiPort >> 1);
  }
  nilaiPort >>=  (8 - jumlahServo);
  nilaiPort &= servoPortMask;
  portServo = nilaiPort ;
  if(TCNT1 > servoTickOffset + servoTickMaksimum)
  {
    TCCR0 = 0;
  }
}
 
int main (void)
{
  (*(&portServo - 1)) = 0xFF; //port sebagai output
   
  OCR0 = 88 - 1;
  TIMSK = (1<<OCIE0);
 
  TCCR1A = 0;
  TCCR1B = (1<<WGM12) | (1<<CS11);  // CTC, prescale 8, top ocr1a
  OCR1A = 20000 - 1;//50Hz
  TIMSK |= (1<<OCIE1A);
  sei();
 
  while(1)
  {
    for(uint8_t i=0;i<180;i++)
    {
      servo[7] = i;
      _delay_ms(10);
    }
 
    for(uint8_t i=180;i != 0;i--)
    {
      servo[7] = i;
      _delay_ms(10);
    }
  }
}