Sunday, April 03, 2011

PWM dengan Interrupt Timer

PWM dengan Interrupt Timer

Ada beberapa teman (especially me2t) yang masih bingung bagaimana sebenarnya Interrupt Timer dapat digunakan sebagai PWM (Pulse Width Modulation). Sebelum menjelaskan lebih dalam mengenai Timer, saya akan mengulas balik tentang sumber pencacah (clock source) pada uC AVR. Sumber pencacah untuk AVR itu seperti detak jantung yang diperlukan untuk mengeksekusi instruksi. Pengeksekusian instruksi umumnya membutuhkan satu sampai tiga siklus pencacah (clock cycles). Satu siklus adalah transisi low-hi-low _|¯|_. Jadi semakin cepat pencacah berjalan (menghitung), maka semakin cepat uC bekerja. Jadi jelas uC AVR 16 MHz akan lebih cepat dari uC AVR 1 MHz. Tapi perlu diperhatikan, kecepatan pencacah tidak begitu berarti banyak. Satu prosesor dapat membutuhkan 1 siklus pencacah untuk memindahkan data dari satu register ke register lainnya, tapi prosesor lain dapat saja membutuhkan 2 sikus pencacah untuk melakukan hal yang sama. Hal ini berarti bahwa prosesor ke-2 harus memiliki kecepatan 2x lipat prosesor ke-1 untuk melakukan instruksi yang sama dalam waktu yang sama. Kuncinya disini adalah sebuah penilaian kasar yang disebut MIPS (Milion of Instructions Per Second), yang dapat digunakan untuk menilai secara kasar berapa instruksi yang dapat dilakukan uC dalam satu detik pada frekuensi pencacah tertentu. Jika pada lembar data (datasheet) tertulis “Up to 16 MIPS Throughput at 16 MHz”, kita bisa mengasumsikan secara kasar bahwa dengan pencacah 4 MHz, kita bisa mencapai 4 MIPS (4 juta instruksi per detiknya atau sekitar 1/4 uS untuk eksekusi 1 instruksi, CMIIW). Sebenarnya penjelasan untuk pencacah dapat lebih panjang, tapi sisanya saya serahkan kepada pembaca untuk mengubeknya di google dan datasheet terkait :).

Kembali ke Timer. Secara prinsip, sebuah timer adalah sebuah counter (penghitung). Tugas Timer hanya menghitung, dia selalu menyimpan hitungannya saat menghitung “satu, dua, tiga, …” hingga 255 (8 bit) atau bahkan bisa sampai 65535 (16 bit). Naiknya hitungan Timer dan berapa lama jeda antar hitungan ini ditentukan dari siklus pencacah uC (seperti saya jelaskan di atas), mode Timer dan settingan Timer. Kita bisa menggunakan Timer sebagai pemicu event. Misal kita bisa menset Timer agar menghitung sampai 255. Dan jika sudah 255 Timer (overflow) akan memberikan sinyal, dan kita bisa mengerjakan sesuatu dalam program (disinilah PWM bekerja) dan menyuruh Timer untuk menghitung lagi dari 0. Demikian seterusnya terjadi jika nilai 255 tercapai, dan Timer hanya akan berhenti jika kita memprogramnya untuk berhenti. Sebagai kemudahan saya akan langsung memberikannya contohnya dengan CodeVisionAVR, menggunakan Timer 0 mode normal yang menghitung sampai 255. Besarnya frekuensi PWM itu bisa Anda hitung secara kasar (tidak akurat) dengan rating MIPS chip. Anda bisa menggunakan CodeWizardAVR untuk menghasilkan kode siap pakai, tanpa perlu mengecek datasheet berapa register terkait Timer harus kita berikan nilai. OK, ini langkahnya:

1. Tentukan mode normal untuk Timer 0 dan check overflow interrupt

1. Setting Timer 0
2. Generate, Save dan Exit. Maka kurang lebih kita mendapatkan deklarasi rutin Timer 0 overflow interrupt seperti berikut:

// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Place your code here
}

3. Nilai register terkait Timer 0 yang telah diset

// 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;

4. Timer 0 diinisialisasi pada interrupt mask dan interrupt diaktifkan

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

// Global enable interrupts
#asm("sei")

Apakah Timer 0 sudah mulai menghitung? Ya belum lah. Kita harus menset register Timer Control 0 (TCCR0) agar Timer 0 mulai bekerja. Untuk menjalankan Timer 0 tanpa prescaler beri nilai 0×01 pada TCCR0.

TCCR0 = 0x01;

Nah loh?! Apalagi tuh prescaler? Lalu apa saja sih bit-bit pada TCCR0 dan TIMSK. Nah itu semua sudah sangat jelas di tulis dalam datasheet, silahkan buka datasheet chip terkait jika merasa bingung.

Kembali ke rutin overflow interrupt Timer 0 (pada langkah 2). Rutin tersebut akan dieksekusi jika Timer 0 sudah menghitung sampai TOP atau dalam hal ini 255 / 0xFF. Bagaimana kalau kita menginginkan Timer 0 memulai dari hmm.., katakanlah 100 / 0×64 dan bukan dari 0 ? Ingat bagaimana cara Timer 0 berhitung “satu, dua, tiga, …” ? Hitungan Timer 0 selalu disimpan dalam register TCNT0. Dengan mengakali jarak hitungan Timer, kita dapat memainkan frekuensi PWM. OK, contoh berikut akan mengubah bagaimana Timer 0 akan mulai menghitung dari 192 / 0xc0:

// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
TCNT0 = 0xC0;

}

...

TCNT0 = 0xC0;
OCR0 = 0x00;
TCCR0 = 0x01; //Timer 0 mulai bekerja tanpa prescaler
TIMSK = 0x01; //pantau Timer 0 overflow interrupt service routine

#asm("sei"); //aktifkan global interrupt

...

Nah sekarang bagaimana kita memanfaatkan mode 0 dari Timer 0 agar dapat bekerja sebagai PWM. Katakanlah kita mempunyai 2 pin enable ke motor kiri dan kanan. Kita buat sebuah penampung nilai PWM yang berperan sebagai duty cycle dari PWM. Anggaplah variabel duty cycle PWM ini mempunyai nilai 0 - 255 (unsigned char). Jika duty cycle-nya 100% maka nilai tersebut adalah 255. Dapatkan gambarannya? Full speed adalah 255, half-speed adalah 127 dan no-speed adalah 0. OK, kita tulis kodenya seperti berikut:

...

#define pwmKi PORTC.0
#define pwmKa PORTC.1
#define mKi_plus PORTC.2
#define mKi_min PORTC.3

#define mKa_plus PORTC.4
#define mKa_min PORTC.5

unsigned char pwmKi_val,pwmKa_val,x = 0;

interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{

TCNT0 = 0xC0;

x++;

if (x>=pwmKa_val) {
pwmKa=0;
} else {
pwmKa=1;
}
if (x>=pwmKi_val) {
pwmKi=0;
} else {
pwmKi=1;
}

}

...

Lalu untuk implementasi nya pada program utama bisa dilakukan dengan testing PWM motor.

...

pwmKi = 1;
pwmKa = 1;

mKi_plus = 1;
mKi_min = 0;
mKa_plus = 1;
mKa_min = 0;

while (1) {
pwmKa_val = 0xff;
pwmKi_val = 0x20;
delay_ms(1500);
pwmKa_val = 0x20;
pwmKi_val = 0xff;
delay_ms(1500);
};

...

Jika motor secara bergantian pelan dan cepat setiap 1.5 detik, maka tidak ada masalah dengan implementasi dengan Timer 0 mode 0 dengan overflow interrupt. Perlu diketahui, beberapa chip AVR menyediakan variasi Timer dan modenya. Akan sangat panjang jika ditulis di sini, untuk itu kawan-kawan bisa RTFD (Read The Fucking Datasheet) :p.

Referensi :

- Datasheet ATMega16 / ATmega8535
- http://www.avrfreaks.net/
- http://gedex.web.id/archives/2008/04/11/pwm-dengan-interrupt-timer/

No comments:

Post a Comment

Terima kasih atas komentar yang anda sampaikan , sehingga dapat menambah wawasan saya sebagai penulis dan membuat blog ini semakin berguna banyak orang