Beberapa waktu
yang lalu ada seorang kawan yang minta diajari membuat timer 1 detik
menggunakan mikrokontroler. Kebetulan uC yang dia pakai Atmega8 dengan clock
intenal.
Langsung aja tek
bikinin pake CodeVision AVR dengan memanfaatkan
interrupt overflow pada timer0.
Pada CodeVision
AVR terdapat fasilitas CodeWizard yang mempermudah kita dalam membuat program.
Untuk mengaktifkan interrupt overflow timer0 kita tinggal memilih clock source,
clock value, Overflow interrupt, dan timer value pada tab timer0.
Untuk membuat
timer 1 detik dengan frekuensi clock uC 8MHz internal saya menggunakan
parameter timer0 sebagai berikut.
- Clock source = system clock
- Clock value = 1000,000 kHz
- Timer value = 0x9B
- Memberi centang pada kotak Overflow interrupt.
Pada proses
diatas sebenarnya kita mengubah nilai pada register TCCR0, TCNT0 dan TIMSK.
Tiga bit LSB dari register TCCR0 merupakan bit CS0 (Clock select) yang
digunakan untuk menentukan frekuensi clock dari timer0.
Nilai bit CS02
s/d CS00 sesuai tabel 1 dibawah.
Tabel.1 Timer/Counter0 Prescaler |
Karena
menggunakan clock value 1000,000kHz (1MHz) yang berarti 1/8 dari frekuensi
clock uC, maka register TCCR0 bernilai 0x02.
TCNT0 merupakan
register yang bertugas untuk menghitung pulsa yang masuk kedalam
timer0/counter0. Setelah program berjalan maka niai register ini akan bertambah
satu setiap clock timernya dan akan mengalami overflow setelah mencapai nilai
0xFF. Ketika register TCNT0 mengalami overflow maka bit TOV0(Timer/Counter0
Overflow Flag) pada register TIFR (Timer/Counter Interrupt Flag Register) akan
bernilai 1 dan akan kembali ke-0 jika rutin interrupsi dijalankan. Akan tetapi
jika rutin interrupsi tidak diaktifkan maka nilai TOV0 akan tetap set(1) dan
harus di clear (0) secara manual.
Nilai TCNT0 dapat
diisi dengan nilai 0x00 s/d 0xFF. Nilai ini sebagai inisialisasi nilai
timercounter yang kita buat. Pada proses menggunakan codeWizard diatas, saya memasukan
nilai timer value 0x9B atau senilai dengan 155 desimal. Ini dimaksudkan untuk
mempermudah perhitungan. Jika inisialisasi TCNT0 = 155 dan maksimal nilainya
255 maka nilai TCNT0 hanya akan menghitung sebanyak 100 pulsa. Setiap 100 pulsa
timer/counter0 terjadi Overflow pada TOV0.
Memberi tanda
centang pada Overflow interrup di CodeWizard dialog akan mengubah nilai register
TIMSK(Timer/Counter Interrupt Mask Register). Timer/counter0 hanya memiliki
fasilitas interrupt overflow. Jika ini diaktifkan maka Bit TOIE0(Timer/Counter
Interrupt Enable) akan bernilai 1 (set). Bit TOIE0 ini berada pada bit ke-0
register TIMSK sehingga jika kita akan mengaktifkan timer0 Overflow interrupt
maka nilai register TIMSK adalah 0x01.
Sedangkan untuk
mengaktifkan fungsi interrupt global dengan menambahkan #asm(“sei”) yang
artinya men-set enable interrupt. Dengan menambahkan perintah ini maka nilai
bit I pada register SREG akan bernilai 1. Untuk meng-clear kan bit-I digunakan code #asm(“cli”).
Setelah
di-generate maka akan dibuatkan template program sesuai kebutuhan kita. Untuk
seting parameter timer0 dan interupsi terdapat pada subrutin void main(void) berikut.
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 1000,000 kHz
TCCR0=0x02;
TCNT0=0x9B;
// Timer(s)/Counter(s) Interrupt(s)
initialization
TIMSK=0x01;
#asm("sei")
Sedangkan
subrutin interupsi terdapat pada bagian paling atas program seperti dibawah
ini.
// Timer 0 overflow interrupt
service routine
interrupt
[TIM0_OVF] void timer0_ovf_isr(void)
{
// Reinitialize Timer 0 value
TCNT0
= 0x9B;
// Place your code here
}
Kembali ke permasalahan
adalah membuat timer 1 detik. Yang pertama saya buat adalah variabel counter
dan variabel second yang bertipe integer dan saya tempatkan sebagai variabel
global diatas fungsi interrupt karena kedua variabel ini akan saya tempatkan di
dalam fungsi interrupt sehingga ketika diakses oleh fungsi interrupt kedua
variabel ini telah terdefinisi.
Setelah mendefinisikan
variabel yang diperlukan, langkah selanjutnya adalah menghitung nilai 1 detik.
Sebelumnya kita telah
mengetahui bahwa setiap 100 hitungan pada register TCNT0 akan terjadi overflow,
karena fungsi Overflow interrupt diaktifkan maka setiap100 hitungan terjadi
interrupt. Selang waktu terjadinya interrupt adalah 100x(1/fClock Timer).
Dengan fClocktimer adalah 1MHz maka periode interupsi adalah 100uS. Pada saat
terjadi interupsi saya manfaatkan untuk menambah nilai variabel counter. Dari
sini diperoleh penambahan nilai variabel counter setiap 100uS sehingga dalam 1
detik nilai variabel counter akan bernilai 10000. Ketika variabel counter
berniali 10000 maka variabel second akan bertambah 1. Dari sini argument saya
adalah bahwa variabel second akan berubah setiap 1 detik.
Selengkapnya untuk rutin
interupsi yang saya gunakan untuk membuat timer 1 detik adalah sebagai berikut.
int
counter,second = 0;
// Timer 0 overflow interrupt
service routine
interrupt
[TIM0_OVF] void timer0_ovf_isr(void)
{
// Reinitialize Timer 0 value
TCNT0
= 0x9B;
// Place your code here
Counter++;
If(counter==10000){
Second++;
Counter = 0;
}
}
Metode membuat timer 1
detik ini telah saya gunakan semenjak saya belajar uC hingga beberapa waktu
yang lalu baik untuk aplikasi yang bersifat edukasi maupun aplikasi yang
bersifat komersil.
Akan tetapi beberapa hari
kemarin pikiran bijak datang, “Sudah saatnya saya beralih menggunakan aplikasi
yang freeware, apalagi untuk kepentingan yang bersifat komersil rasanya tidak
etis ketika menggunakan aplikasi bajakan”.
Seperti yang anda ketahui
bahwa CVAVR merupakan aplikasi berbayar dari Hpinfotech. Produsen ini
meluncurkan produk CVAVR yang free hanya untuk versi Evaluation yang memiliki
keterbatasan fitur. Jika ingin yang full version maka harus membayar $XX atau
mencari versi bajakanya.
Sedangkan aplikasi AVR
yang bersifat freeware dan diluncurkan langsung oleh produsen atmel adalah
AVRstudio. Dari situ saya mencoba untuk beralih ke AVRstudio dan WinAVR sebagai
eksternal tool untuk mengakses program avr-gcc.exe dan make.exe.
Pada masa transisi ini
sedikit mengalami kendala, karena terbiasa memanfaatkan tool codeWizard dari CVAVR
sedangkan menggunakan AVRstudio lebih banyak ngetik sendiri.
Tapi berkat AVRstudio
inilah saya menemukan kelemahan beberapa kode yang saya andalkan sejak dulu
termasuk pembuatan timer 1 detik diatas.
Untuk membuat timer 1
detik menggunakan metode seperti diatas saya memanfaatkan library interrupt.h
dari WinAVR. Register yang diatur bernilai sama. Untuk memanfaatkan fungsi
interrupt menggunakan fungsi layanan interupsi berikut.
ISR(interrupt_vector){
//kode program
}
List nama vektor interrupt pada win AVR untuk uC Atmega8
adalah sesuai tabel 2 berikut:
Tabel.2 Vector interrupt pada uC ATmega8 |
Urutan tabel diatas juga
merupakan urutan prioritas interrupt pada uC Atmega8. Dengan variabel yang sama
maka source code pada rutin interrupt untuk menghitung 1 detik adalah sebagai
berikut.
ISR(TIMER0_OVF_vect){
TCNT0 = 0x9B;
Counter++;
If(counter==10000){
Second++;
Counter
= 0;
}
}
Untuk nilai register
TCCR0 dan TIMSK sama seperti halnya menggunakan CVAVR. Sedangkan pada
AVRstudio, untuk mengenable kan fungsi interrupt menggunakan macro intruksi sei () dan untuk meng-clearkan menggunakan cli ().
Berikut perbandingan
source code program timer 1 detik menggunakan CVAVR dengan AVRstudio dimana
nilai variabel second dikeluarkan pada PORTD.
1. Program menggunakan CVAVR
1. Program menggunakan CVAVR
/*****************************************************
This
program was produced by the
CodeWizardAVR
V2.05.3 Standard
Automatic
Program Generator
©
Copyright 1998-2011 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project
:
Version
:
Date : 30/10/2013
Author : Dwi Kurniawan
Company
: Comutech
Comments:
Chip
type : ATmega8
Program
type : Application
AVR Core
Clock frequency: 8,000000 MHz
Memory
model : Small
External
RAM size : 0
Data
Stack size : 256
*****************************************************/
#include <mega8.h>
int count=0;
int second = 0;
// Timer
0 overflow interrupt service routine
interrupt [TIM0_OVF] void
timer0_ovf_isr(void)
{
//
Reinitialize Timer 0 value
TCNT0=0x9B;
// Place
your code here
count++;
if(count==10000){
second++;
count = 0;
}
}
void main(void)
{
// Timer/Counter
0 initialization
// Clock
source: System Clock
// Clock
value: 1000,000 kHz
TCCR0=0x02;
TCNT0=0x9B;
//
Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x01;
//
Global enable interrupts
#asm("sei")
while (1)
{
PORTD=second;
}
}
2. Program menggunakan AVRstudio
#include <avr/interrupt.h>
int count=0;
int second=0;
ISR(TIMER0_OVF_vect){
TCNT0 = 0x9B;
count++;
if(count==10000){
second++;
count=0;
}
}
int main(void){
DDRD=0xff;
TCCR0 = 0x02;
TCNT0 = 0x9B;
TIMSK = 0x01;
sei ();
while(1){
PORTD=second;
}
return (0);
}
Tampak bahwa kedua
program sama persis. Setelah didownload atau disimulasikan menggunakan Proteus
(yang ini belum nemu sainganya yang freeware) program berjalan sama persis.
Akan tetapi, dengan
menggunakan AVRstudio akhirnya saya menemukan kelemahan dari program timer 1
detik yang selama ini saya gunakan. Pada
AVRstudio terdapat fasilitas debug yang jika dijalankan kita dapat melihat isi
dari masing-masing register.
Gambar.1 Tampilan pada saat debuging program |
Untuk program timer 1
detik diatas yang perlu diperhatikan adalah register TCNT0. Sebelumnya, saya
beranggapan ketika memberikan nilai pada timer value saat menggunakan
CodeWizard maka nilai register TCNT0 setelah overflow akan langsung bernilai
sesuai dengan yang kita masukan. Sesuai program diatas bernilai 0x9B sehingga
saya beranggapan setelah TCNT0 overflow nilai TCNT0 akan kembali ke 0x9B.
Ternyata setelah mengamati perilaku register TCNT0 pada debuging AVRstudio
nilai TCNT0 akan kembali ke 0x00 setelah mengalami overflow setelah melalui
inisialisasi ulang pada rutin interupsi baru program ini bernilai sesuai yang
kita masukan. Inisialisasi TCNT0 saya tempatkan pada awal proses intrrupt maka
dalam hal ini saya telah kehilangan akurasi sebesar 1 clock timer yang berarti
1us. Untuk menanggulangi ini maka nilai TCNT0 dtambah 1 pada inisialisasi
sehingga menjadi 0x9C.
Ternyata sampai sini
permasalahan belum selesai, setelah diamati lagi selama perpindahan dari baris
ISR ke baris dibawahnya TCNT0 telah berubah nilai menjadi 0x03.
Gambar 2 Perpindahan dari baris ISR ke baris berikutnya, TCNT0 telah berubah menjadi 0x03 |
Hal ini
dikarenakan ketika meng-eksekusi fungsi ISR diperlukan 4 siklus clock untuk
interrupt acknowledge, 2 siklus clock untuk jump dari vektor interrupt, 2
siklus clock untuk set dan reset TCNT0, 2 siklus clock untuk set dan reset
register I, 4 siklus clock untuk operasi RETI (Interrupt return), 2 siklus
clock untuk clear TOV0.
Dari analisa saya diatas
baru diperoleh 16 siklus clock yang berarti 2 siklus clock timer. Yanng berarti
perpindahan nilai TCNT0 yang dapat dideteksi baru 0x02 sedangkan 1 clock timer
belum dapat saya deteksi.
Hanya saja sampai disini
dapat disimpulkan bahwa metode saya selama ini telah kehilangan akurasi
sebanyak 4 clock timer. Jadi interupsi timer0 terjadi setiap 104 us. Ketika membatasi
nilai counter 10000 berati selang waktu dari counter = 0 hingga 10000 adalah
104uS x 10000 = 1.04 s.
Untuk mengatasi masalah
tersebut dapat dilakukan dengan cara mengubah nilai TCNT0. Jika menghendaki
selang waktu terjadinya interupsi 100us maka nilai TCNT0 = 255 – (100 – 4) =
159 atau 0x9F.
Dengan memodifikasi
program diatas dan hasilnya memang jauh lebih presisi ketika menambahkan 4
clock timer pada register TCNT0.
Jika menggunakan
simulator proteus, dapat diamati perubahan nilai detik dengan waktu eksekusi
program yang ditampilkan distatus bar bawah sebelah status bar message.
GooD JoB
BalasHapusterimakasih tulisannya bang, sangat bermanfaat dan sangat membantu
BalasHapus