Mohon maaf, saya menuliskan post ini dengan asumsi, Pembaca sudah mengetahui dasar pemrograman C, atau lebih spesifik lagi microcontroller Atmel atau Arduino.
Skematik

AVR Mega2560 port B-6 terkoneksi ke Atmega328p port D-3 dengan pull-down resistor 4.7k ohm (4K7).
Pulse Generator, Atmega 2560
Pulse generator dirancang untuk memberikan simulasi trigger wheel ignition Fiat Uno2 yang menggunakan Magneti Marelli Digiplex2.Trigger wheel ini memiliki 4 buah pole yang tersebar dengan jarak 90° dan sebuah pole yang berjarak kurang lebih 9° dari salah satu pole, yang menandakan bahwa itu adalah spark silinder pertama.
Pulse terjadi pada posisi derajat:
0 -- 9 -- 90 -- 180 -- 270
Langsung saja, kodenya :)
#include <avr/io.h> #include <avr/interrupt.h> #define _OUTPORT 6 // Arduino MEGA 2560 pin 12 #define _INDICATOR 7 // Arduino MEGA 2560 pin 13 // volatile diperlukan karena variabel ini akan digunakan dalam // interrupt volatile unsigned int wheel; // setup, dieksekusi pertama kali, dan hanya sekali void setup() { // set Data Direction port B pada bit 6 dan 7 bernilai "1" // atau "output". Menggunakan operasi logika OR DDRB |= _BV(_OUTPORT)|_BV(_INDICATOR); wheel = 0; // reset konfigurasi register TIMER1 TCCR1A = 0; TCCR1B = 0; // Counter register TIMER1, nilainya akan bertambah 1 setiap // terjadi clock pada TIMER1 TCNT1 = 0; // Interrupt mask, yang menentukan apakah ISR akan dieksekusi // apabila terjadi interrupt. Saat ini di-clear dulu. TIMSK1 = 0; // enable CTC mode TCCR1B |= (1 << WGM12); // set output compare value-nya OCR1A = 65535; // enable interrupt mask Output Compare Interrupt Enable // yang menyebabkan ISR dieksekusi apabila TCNT1 nilainya // sama dengan OCR1A TIMSK1 |= (1 << OCIE1A); // start counter tanpa pre-scaler, // yang berarti TCNT1 akan bertambah // sesuai dengan clock CPU (16Mhz) TCCR1B |= (1 << CS10); } // Interrupt Service Routine ini dieksekusi apabila // TCNT1 bernilai sama dengan OCR1A. // TIMER1_COMPA_vect adalah defined interrupt vector yang akan // dipanggil jika kondisi diatas terjadi dan OCIE1A di set pada // TIMER1 Interrupt Mask, TIMSK1. ISR(TIMER1_COMPA_vect) { // reset counter TCNT1 = 0; wheel++; // tracking putaran 360 derajat. Jika overflow, set menjadi 0. if (wheel > 359) { wheel = 0; } // sesuai pattern asli trigger wheel Fiat Uno 2 // saat posisi wheel sesuai dengan angka2 dibawah ini // _OUTPORT akan ON. if ((wheel == 0)|| (wheel == 9)|| (wheel == 90)|| (wheel == 180)|| (wheel == 270)) { // Untuk mengaktifkan port, set "1" pada register PORTB, // bit ke-6. // Angka 64 ini adalah 2 pangkat 6. Angka 6 adalah _OUTPORT. // Operasi logika menggunakan OR atau tanda "|" PORTB |= 64; } // _OUTPORT akan aktif selama 5 derajat saja, karena itu // saat posisi wheel sesuai dengan angka2 dibawah ini // _OUTPORT akan OFF else if ((wheel==5)|| (wheel==14)|| (wheel==95)|| (wheel==185)|| (wheel==275)) { // Ini untuk mematikan port, set "0" pada register PORTB, // bit ke-6. Operasi logika menggunakan AND dari NOT 64. // 64 = 00100000, NOT 64 atau ~64 = 11011111 PORTB &= ~64; } } // main program loop void loop() { unsigned int V, val; // untuk menyimpan bacaan potensiometer melalui ADC0 unsigned int sensorValue; // membaca input dari potensio meter yang dipasang di port // Analog0 (A0) sensorValue = analogRead(A0); // mapping nilai konversi ADC 0-1023 menjadi RPM 200-12000 V = map(sensorValue, 0, 1023, 200, 12000); // menentukan banyaknya count yang dibutuhkan untuk melakukan // putaran 1 derajad val = ((F_CPU*60)/V)/360; // update Output Compare Register OCR1A = val; // Menyalakan LED indikator. // angka 128 adalah 2 pangkat 7, angka 7 adalah _INDICATOR PORTB |= 128; // tunggu 100ms delay(100); // lalu matikan LED indikator PORTB &= ~128; // tunggu 900ms delay(900); // total menunggu adalah 1000ms atau 1 detik, tujuannya // supaya ADC bisa ambil nafas, sebelum melakukan konversi // ADC berikutnya }
Tooth Logger, Atmega328p
Yak, µC selanjutnya adalah standaloneAtmega328p
dengan bootloader Arduino, yang akan di-test, di-bully interrupt-nya, untuk membuktikan, bahwa clock 16Mhz bisa menghandle interrupt hingga 12.000 RPM!Langsung aja kodenya!
#define pinTriggerWheel 3 // Port D-3, pin 5 pada Arduino Uno #define pinIndikator 5 // Port B-5, pin 19 pada Arduino Uno volatile unsigned long timerOverflow, counter ; volatile boolean trigger ; void setup() { // setup serial, gunakan BAUD rate paling besar, // atau reporting-nya tidak akan bisa mengejar kecepatan // interrupt pada RPM tinggi Serial.begin(115200); // setup timer TCCR1A = 0; TCCR1B = _BV(CS10); // no prescaler // Uups, disini pake macro _BV(), yang memiliki fungsi // sama dengan bit shift operator: (1 << TOIE1) // TIMER1 Overflow Interrupt Enable! TIMSK1 = _BV(TOIE1); // reset TIMER1 Counter TCNT1 = 0; // setup external interrupt-1 triggering EICRA = _BV(ISC10) | _BV(ISC11); // rising edge trigger // atau menggunakan EICRA = _BV(ISC11); untuk falling edge // trigger // enable interrupt mask untuk trigger ISR // ISR INT1_vect akan dieksekusi jika interrupt pada INT1 // terjadi. EIMSK = _BV(INT1); // port setup // PD3 pin mode input -- INT1 // kebalikan dari OUTPUT, INPUT direction menggunakan value // "0" pada Data Direction Register. Operasi logika yang // digunakan adalah AND NOT, penjelasan ada di contoh kode // dibawah. DDRD &= ~_BV(pinTriggerWheel); // PB5 pin mode output -- onboard LED DDRB |= _BV(pinIndikator); } // Main loop, looping forever... void loop() { if (trigger) { trigger = false; Serial.println(counter); } } // TIMER1 overflow, terjadi apabila TCNT1 nilainya mentok // di 65535, lalu kembali ke 0. ISR(TIMER1_OVF_vect) { // Untuk mengakali keterbatasan TCNT1 yang ukurannya // hanya 16-bit (max. 65535) maka setiap terjadi counter // overflow, maka akan dilakukan count-up terhadap variabel // yang ukurannya lebih besar yaitu 32-bit unsigned long. // penulisan dalam bentuk heksa desimal, tiap 2 digit // adalah hexa 00-FF. Empat buah angka 0 itu 16-bit // count-up dilakukan mulai byte ke 5. timerOverflow += 0x10000UL; } // ISR ini dieksekusi saat terjadi interrupt request pada // pin INT1. ISR(INT1_vect) { // disable global interrupt, yang berarti jangan // interupsi kode2 krusial yang akan dieksekusi dibawah ini noInterrupts(); // Nah, disini TCNT1 yang 16-bit ditambahkan ke variable yang // lebih besar supaya bisa menghasilkan hitungan yg besar // hingga 4 miliar (32-bit) counter = timerOverflow + TCNT1; // clear TIMER1 counter TCNT1 = 0; // clear overflow counter timerOverflow = 0; // kasih bendera, bahwa trigger pada INT1 telah terjadi trigger = true; // eksekusi kode yang krusial selesai, // silakan yang lain boleh interupsi! interrupts(); }
Hasilnya
Pola 4 count panjang dan 1 count pendek, terlihat secara teratur pada RPM rendah dan tinggi.