Uploaded by User54785

Interupsi pada Arduino

advertisement
Interupsi pada Arduino
Interupsi adalah sinyal yang memberhentikan sementara pekerjaan microcontroller saat ini dan
pergi ke rutin interupsi. Selesai mengerjakan rutin interupsi, microcontroller kembali melanjutkan
pekerjaan awalnya. Tulisan ini membahas perihal interupsi secara teknis. Selamat menikmati. :)
Pengenalan Interupsi Arduino
Umumnya sketch dasar Arduino terdiri atas fungsi setup() dan loop() :
void setup() {
}
void loop() {
}
Secara prosedural, fungsi setup() akan dijalankan terlebih dahulu daripada fungsi loop().
Setelah fungsi setup() selesai dikerjakan, selanjutnya microcontroller akan mengerjakan fungsi
loop() berulang-ulang.
Saat menggunakan interupsi pada pemrograman Arduino, harus ada fungsi lain yang menangani
interupsi, fungsi ini disebut dengan Interrupt Service Routine (ISR). Nama fungsi ISR dapat
digunakan sembarang nama, dengan syarat harus memenuhi kaidah penamaan fungsi.
void setup() {
}
void loop() {
}
void interupsi() {
}
Pertanyaannya, kapan fungsi interupsi() di atas akan dieksekusi? Pada Arduino, fungsi
interupsi() akan dijalankan saat terjadi interupsi, saat pin-pin tertentu berubah nilainya.
Misalnya dalam kondisi normal pin interupsi bernilai HIGH, maka saat nilai tersebut berubah LOW
fungsi interupsi akan dieksekusi.
Pin-pin berapa saja yang dapat men-trigger interupsi? Jawabannya, harus dilihat dari jenis
Arduino yang digunakan, seperti terlihat pada tabel 1.
Tabel 1, Pin Interupsi Arduino
Board
UNO, Nano, Mini
Mega, Mega 2560, MegaADK
Micro, Leonardo
Pin-pin Interupsi
2, 3
2, 3, 18, 19, 20, 21
0, 1, 2, 3, 7
Tulisan ini merujuk pada penggunaan Arduino UNO, dengan pin-pin interupsi 2 dan 3.
Pada contoh di bawah ini, pin 2 Arduino dihubungkan ke arah tombol menuju ground/GND,
berikut contoh kode sketch yang telah dilengkapi dengan interupsi:
#define PinLED 13
#define PinInterupsi 2
volatile byte status = LOW;
void setup() {
pinMode(PinLED, OUTPUT);
pinMode(PinInterupsi, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PinInterupsi),
dipkedip, CHANGE);
}
void loop() {
digitalWrite(PinLED, status);
}
void dipkedip() {
status = !status;
}
Fungsi dipkedip() merupakan ISR, dieksekusi saat tombol pada pin 2 ditekan. Saat tidak
ditekan, microcontroller akan terus menjalankan fungsi loop().
Pada bagian setup() terdapat perintah attachInterrupt, perintah ini memiliki 3 (tiga)
parameter:
1. Pin yang dikonversi menjadi interupsi
2. Fungsi ISR, pada contoh diberi nama dipkedip().
3. Mode, saat kapan interupsi di-trigger.
- CHANGE : interupsi di-trigger saat terjadi perubahan status pada pin
- LOW : interupsi di-trigger saat nilai pin LOW
- RISING : interupsi di-trigger saat nilai pin beralih dari LOW ke HIGH
- FALLING : interupsi di-trigger saat nilai pin beralih dari HIGH ke LOW
- HIGH : interupsi di-trigger saat nilai pin HIGH (khusus pada Arduino ARM-based
seperti Due, Zero, MKR1000)
Interupsi Pin Lain
Pada Arduino UNO, interupsi hanya dibatasi pada 2 (dua) pin saja yakni pin 2 dan 3. Bagaimana
bila kebutuhan akan interupsi lebih dari dua? Jawabannya, harus menggunakan metoda interupsi
lain agar dapat mengubah pin-pin lainnya sebagai pin interupsi. Tekniknya menggunakan
pemrograman AVR, memrogram langsung ke microcontroller.
Berikut gambaran microcontroller ATMega328p :
Gambar 1, Peta ATMega 328/168 ke Arduino (arduino.cc)
Pada gambar 1 terlihat bahwa hampir semua pin bertuliskan PCINT, singkatan dari Pin Change
Interrupt, artinya pin-pin D0.. D13 dan A0..A5 dapat dijadikan pin-pin interupsi, namun hanya tiga
interupsi yang dapat dijalankan secara bersamaan. Jadi untuk pin D8..D13 satu interupsi, A0..A5
satu interupsi dan D0..D7 satu interupsi.
Untuk meng-enable-kan pin menjadi pin interupsi, register PCICR harus dimanipulasi:
Tiga bit pertama, sebelah kanan, merupakan control bit untuk suatu grup PCINT. Saat PCIE0 (bit
0) di set (bernilai 1), maka PCINT pin 0..7 ATMega menjadi enable. Sesuai peta, pin tersebut
adalah pin D8..D13 Arduino, tidak termasuk pin-pin kristal. PCIE1 untuk pin 8..14 yang merupakan
pin A0..A5 pada Arduino. PCIE2 untuk pin 16..23 yang merupakan pin D0..D7 pada Arduino.
Saat interupsi di-trigger, bit-bit flag yang berhubungan akan ter-set juga. Bit-bit flag terdapat
pada register PCIFR.
Saat PCIE0 enable dan interupsi di-trigger, maka PCIF0 akan set, PCIE1 enable maka PCIF1 set dan
PCIE2 enable maka PCIF2 set.
Untuk meng-enable-kan pin-pin tertentu pada grup digunakan register PCMSK. Ada tiga register
PCMSK pada masing-masing grup, PCMSK0, PCMSK1 dan PCMSK2. Masing-masing bit pada
register PCMSK berhubungan ke satu pin PCINT:
PCMSK0:
PCMSK1:
PCMSK2:
PCMSK0 digunakan untuk meng-enable-kan pin pada PCINT pin 0..7. PCMSK1 untuk pin 8..14 dan
PCMSK2 untuk pin 16..23.
Sebagai contoh, akan diubah pin A0 menjadi pin interupsi.
Langkah 1, pada peta gambar 1 sebelumnya terlihat bahwa pin A0 adalah pin PCINT8, yang
merupakan bagian dari PCMSK1. Tambahkan pada sketch :
PCMSK1=B00000001 //enable-kan PCINT8
Langkah 2, bersihkan bit-bit flag, A0 adalah PCINT8 dan berhubungan dengan PCIF1. Clear-kan
semua flag interupsi:
PCIFR = B00000000 //clear-kan semua flag
Langkah 3, enable-kan grup interupsi yang berhubungan dengan A0 (PCIE1):
PCICR = B00000010 //enable-kan grup PCIE1
Selesai, pin A0 telah menjadi pin interupsi.
Bagaimana dengan fungsi ISR-nya? AVR memiliki fungsi ISR() yang diterima sebagai vektor
interupsi masing-masing grup. Grup PCIE0 memiliki vektor PCINT0_vect, grup PCIE1 memiliki
PCINT1_vect dan grup PCIE2 memiliki PCINT2_vect. Untuk A0 maka ISR-nya adalah PCINT1_vect.
ISR(PCINT1_vect){
//kode rutin di sini..
}
Berikut kodenya, tambahkan tombol pada pin A0 ke arah GND:
#define PinLED 13
volatile byte status = LOW;
void setup() {
pinMode(PinLED, OUTPUT);
pinMode(A0, INPUT_PULLUP);
PCMSK1=B00000001; //enable-kan PCINT8
PCIFR=B00000000; //clear-kan bit flag
PCICR=B00000010; //enable-kan grup PCIE1
}
void loop() {
digitalWrite(PinLED, status);
}
ISR(PCINT1_vect) {
status = !status;
}
Timer pada Arduino
Pada bagian ini akan dibahas perihal Timer1 dan Timer2 saja, Timer0 sudah dimanfaatkan oleh
millis(). Timer2 digunakan untuk timer overlow interrupt serta timer compare interrupt,
dan Timer1 untuk timer capture interrupt.
Timer Overflow Interrupt
Timer Overflow Interrupt bekerja dengan cara melakukan pengujian apakah timer mengalami
overflow. Overflow pada timer adalah kondisi di mana timer menghitung melebihi angka
maksimumnya. Untuk Timer2, overflow akan terjadi saat perhitungan penambahan berubah dari
255 kembali ke 0.
Setting bit TOIE pada masing-masing register timer interrupt mask, TIMSKx, selanjutnya enablekan timer overflow interrupt. Untuk Timer2, registernya adalah TIMSK2.
Saat kapan terjadinya overflow tergantung pada frekuensi osilator dan pembagi clock. Berikut
rumus time overflow untuk Timer2:
𝑇𝑜𝑣𝑒𝑟𝑓𝑙𝑜𝑤 =
1
∗ 2𝑏𝑖𝑡𝑠 ∗ 𝑐𝑙𝑜𝑐𝑘𝑑𝑖𝑣
𝐹𝑜𝑠𝑐
Board Arduino UNO memiliki osilator 16 MHz dan nilai bawaan pembagi clock 64. Dengan begitu
untuk Timer2, overflow akan terjadi saat :
𝑇𝑜𝑣𝑒𝑟𝑓𝑙𝑜𝑤 =
1
∗ 28 ∗ 64 = 0.001024 𝑑𝑒𝑡𝑖𝑘 = 1.024 𝑚𝑖𝑙𝑖𝑑𝑒𝑡𝑖𝑘
16000000
Itu artinya bahwa trigger interupsi terjadi sekitar 1 milidetik atau sekitar frekuensi 1 kHz.
Agar dapat mengetahui kapan saat terjadinya interupsi, tambahkan ISR pada sketch.
ISR(TIMER2_OVF_vect){
//tambahkan kode lain
}
Timer Overflow pada Sketch Arduino
Bagaimana membuat LED bawaan Arduino pada pin 13 berkedip dengan menggunakan timer
overflow? Timer overflow nilai bawaannya 1 milidetik, bila digunakan sebagai delay, maka LED
seperti terlihat ON terus-menerus, penyebabnya karena mata tidak mampu melihat perubahan
ON OFF-nya LED yang terjadi begitu cepat. Untuk itu diperlukan mengubah timer menjadi lebih
lambat dengan cara merubah nilai pembaginya (divisor value) pada register TCCR2B.
Clock divisor dapat diubah dengan cara memanipulasi nilai bit-bit 2, 1 dan 0, berikut daftar
nilainya:
CS2[2:0]
001 = 0x01
010 = 0x02
011 = 0x03
100 = 0x04
101 = 0x05
110 = 0x06
111 = 0x07
Divisor
1
8
32
64
128
256
1024
Apabila ingin memanipulasi divisor menjadi 1024, maka nilai register TCCR2B harus di-set
menjadi 0x07, sebab untuk 1024 nilai CS2-nya adalah 0x07.
TCCR2B = (TCCR2B & B11111000) | 0x07
Selanjutnya enable-kan timer overflow dengan cara men-set bit TOIE pada register TIMSK2:
TIMSK2 = (TIMSK2 & B11111110) | 0x01
Berikut sketch lengkapnya:
#define PinLED 13
volatile byte status = LOW;
void setup() {
pinMode(PinLED, OUTPUT);
TIMSK2 = (TIMSK2 & B11111110) | 0x01;
TCCR2B = (TCCR2B & B11111000) | 0x07;
}
void loop() {
digitalWrite(PinLED, status);
}
ISR(TIMER2_OVF_vect) {
status = !status;
}
Dari kode tersebut dihasilkan LED berkedip dengan delay 16 mili detik, berikut hitungannya:
𝑇𝑜𝑣𝑒𝑟𝑓𝑙𝑜𝑤 =
1
∗ 28 ∗ 1024 = 0.016384 𝑑𝑒𝑡𝑖𝑘 = 16.384 𝑚𝑖𝑙𝑖𝑑𝑒𝑡𝑖𝑘
16000000
Timer Compare Interrupt
Cara berikutnya menggunakan timer interrupt adalah dengan membandingkan nilai timer dengan
nilai tertentu. Interupsi akan dihasilkan bila nilai timer sama dengan suatu nilai yang telah
ditetapkan, ini disebut Timer Compare Interrupt (TCI).
Saat menggunakan Timer Overflow Interrupt (TOI) sebelumnya, interupsi di-trigger apabila
hitungan melewati nilai 255. Pada mode TCI, interupsi di-trigger sesuai nilai yang telah
ditentukan. Bahkan dapat dibandingkan dengan 2 nilai timer yang berbeda.
Ada dua kemungkinan komparasi, A dan B. Untuk CompareA, nilai timer dibandingkan dengan
register OCRxA dimana ‘x’ adalah nomor timer. Timer2 registernya adalah OCR2A.
Sebagai contoh, bila diinginkan interupsi di-trigger setelah hitungan 128, maka nilai OCR2A harus
diisi nilai 128.
OCR2A = 128;
Untuk komparasi B, nilai harus diberikan ke register OCR2B, misalnya interupsi di-trigger setelah
hitungan 200, maka:
OCR2B = 200;
Untuk mengetahui CompareA atau CompareB yang di-trigger, lihat pada vector masing-masing:
TIMER2_COMPA dan TIMER2_COMPB.
Jangan lupa juga untuk meng-enable-kan interupsi compare. Setting nilai OCIEA dan OCIEB pada
register TIMSK2:
Saat mengunakan TCI, nilai TIMSK2 harusnya seperti berikut:
TIMSK2 = (TIMSK2 & B1111001) | 0x06
Implementasi Timer Compare Interrupt
Berikut modifikasi sketch sebelumnya untuk diimplementasi pada Timer Compare Interrupt:
#define PinLED 13
volatile byte status = LOW;
void setup() {
pinMode(PinLED, OUTPUT);
TIMSK2 = (TIMSK2 & B11111001) | 0x06;
TCCR2B = (TCCR2B & B11111000) | 0x07;
OCR2A = 128;
OCR2B = 200;
}
void loop() {
digitalWrite(PinLED, status);
}
ISR(TIMER2_COMPA_vect) {
status = LOW;
}
ISR(TIMER2_COMPB_vect) {
status = HIGH;
}
Hasilnya LED akan OFF pada saat Timer2 bernilai 128 dan LED akan ON pada saat nilai 200.
Timer Capture Interrupt
Timer Capture Interrupt hanya dapat diimplementasikan pada Timer1 Arduino UNO. Interupsi ini
akan terjadi apabila suatu pulsa dibaca dari ICP1 atau pin D8 Arduino UNO.
Interupsi jenis Timer Capture ini dapat dispesifikasikan saat pulsa menaik atau menurun pada
TCCR1B:
Bila bit ICES1 di-set, interupsi akan terjadi saat pulsa menaik, bila di-reset interupsi terjadi bila
pulsa menurun.
Hal menarik lainnya dari TCCR1B adalah tiga bit awal: CS12, CS11 dan CS10. Tiga bit ini digunakan
sebagai divisor untuk counter Timer1, dengan daftar nilai sebagai berikut:
CS12, CS11, CS10
001 = 0x01
010 = 0x02
011 = 0x03
100 = 0x04
101 = 0x05
110 = 0x06
111 = 0x07
Divisor
1
8
64
256
1024
Sumber clock luar pada T1, menurun
Sumber clock luar pada T1, menaik
Bila divisor tidak digunakan, isi tiga bit ini dengan nilai 0x01.
Bila ingin menangkap (capture) kejadian saat pulsa menaik, berikut nilai TCCR1B:
TCCR1B = (TCCR1B & B10111000) | 0x41;
Register TCNT1 menghitung setiap pulsa. Untuk mengakomodasi jumlah pulsa yang besar, TCNT1
terdiri atas dua register 8 bit, TCNT1L dan TCNT1H. Dengan begitu hitungan pulsa maksimum
pada pin D8 Arduino menjadi 65535.
Nilai TCNT1 secara otomatis akan disalin ke register ICR1 dan flag yang berlokasi pada TIFR1
(ICF1) akan di-set. Flag ini akan dibersihkan (cleared/reset) secara otomatis.
Bila bit ICE1 pada register TIMSK1 dalam keadaan set, vector TIMER1_CAPT akan dibaca oleh CPU
pada setiap kejadian tangkapan/capture:
ISR() -nya seperti berikut:
ISR(TIMER1_CAPT_vect) {
//kode di sini
}
Timer Compare pada Sketch Arduino
Timer Capture Interrupt dapat disimulasikan dengan menyambungkan tombol pada pin D8
menuju ground, beri resistor pull-up 10K. Penekanan dan pelepasan tombol mensimulasikan
seolah-olah terjadi perubahan pulsa pada pin ini. Manfaatkan LED bawaan pada pin 13 sebagai
indikator.
Berikut sketch-nya:
#define PinLED 13
volatile byte status = LOW;
void setup() {
pinMode(PinLED, OUTPUT);
pinMode(8, INPUT);
Serial.begin(9600);
TIMSK1 = (TIMSK1 & B11011111) | 0x20; //enable-kan timer
capture interrupt
TCCR1B = (TCCR1B & B10111000) | 0x41; //falling edge, tanpa
prescale (divisor 1)
}
void loop() {
digitalWrite(PinLED, status);
}
ISR(TIMER1_CAPT_vect){
int t = ICR1; //harus dibaca dulu
Serial.println(t);
status = !status;
}
Perhatikan LED saat tombol ditekan dan ditekan lagi, nilai counter ICR1 dapat dilihat pada ‘Serial
Monitor’. Teknik ini dapat dimanfaatkan untuk menghitung frekuensi dengan cara mengirimkan
pulsa ke pin D8 selama periode tertentu.
Demikian sekilas cerita tentang interupsi pada Arduino, semoga bermanfaat.
Terima kasih
Medan, ujung Desember 2018
Stephanus Priyowidodo, M.Kom
Download