Tuesday, January 12, 2016

Alat Praktikum dengan Interface Komputer




PENGEMBANGAN ALAT PRAKTIKUM GERAK LURUS MENGGUNAKAN INTERFACE KOMPUTER
oleh : Soni Sukendar
         Rita Hartati


A.  Analisis Kebutuhan
Berdasarkan hasil studi awal dan kajian berbagai bahan pustaka diperoleh spesifikasi kebutuhan alat praktikum fisika untuk topik GLBB yang dapat memberi solusi kendala praktikum menggunakan ticker timer, sebagai berikut.
1.      Alat praktikum yang dapat dirangkai dengan cepat, atau kalau memungkinkan sudah berupa alat terpadu (modul).
2.      Alat praktikum yang dapat berkomunikasi dengan komputer.
3.      Data yang dikirim oleh alat praktikum ke komputer dapat diakuisis oleh software pengolah angka dan grafik yang sudah familier di kalangan pengguna, misalnya MS-Excel.
4.      Tampilan grafik harus menarik dan mudah dipahami siswa.
5.      Harga komponen untuk mengembang alat ini harus relatif murah.
6.      Alat praktikum ini harus mudah dipasang dan dibongkar, serta mudah untuk digunakan.
7.      Alat praktikum ini dapat dipadukan penggunaanya dengan alat praktikum yang sudah ada, misalnya KIT fisika.
Dari spesifikasi tersebut, akhirnya diputuskan untuk mengembangkan alat praktikum berbasis microcontroller dengan menggunakan IC utama keluaran Atmega seri 328p. Atmega seri ini sangat mudah untuk diisi program dengan mengunakan lingkungan pemograman berbasis Arduino. Kelebihan lingkungan pemograman ini dari segi bahasa sangat sederhana dan dikembangkan dengaan menggunakan bahasa perograman C. Selain itu cukup banyak tersedia library dengan lisensi freeware, yang dapat digunakan untuk menyingkat proses penulisan script terutama scrip untuk mengakses hardware/ komponen berupa sensor.
   Komponen lain yang diperlukan adalah sensor jarak, dan hasl survey di lapangan dperoleh sensor jarak dengan menggunakan prinsip kerja pemancaran dan pemantulan gelombang ultrasonik dengan harga yang cukup murah yaitu sensor ultrasonik HCSR-04. Harga sensor ini sekitar Rp. 35.000.
Untuk koneksi alat praktikum ke komputer digunakan USB to serial conector keluaran prolific seri hxa. Harganya sekitar Rp. 45.000,-.

B.     Pembuatan Alat Praktikum GLBB berbantuan komputer
Setelah dilakukan analisis kebutuhan, langkah selanjutnya adalah membuat alat prakikum dengan tahapan sebagai berikut
1.      Perancangan rangkaian elektronik
Skema rancangan rangkaian adalah sebagai berikut.



Gambar 1. Skema rangkaian sistem minimum untuk IC Atmega 328p.

2.      Pemilihan komponen
Komponen elektronik yang diperlukan adalah sebagai berikut.
Tabel 1. Kebutuhan komponen
Block Controller
Nama Komponen
Jml
IC Atmega 328P-PU
1
Xtal Resonator 16MHz
1
C ceramic 22pF
2
C ceramic 0.1uF
2
R 1K
1
R 10K
2
Tombol reset
1
led hijau
1
Block Sensor dan Actuator

Nama Komponen
Jml
HC SR04 ultrasonik sensor
1
LCD 1602
1
mini board
2
jumper pelangi 10 warna M/M
1
jumper
6

3.      Menulis script program untuk mikrokontroler
IC Atmega 328p diprogram supaya dapat mengakses data dari sensor ultrasonik HCSR-04 serta mengirimkan data digitalnya ke komputer dan dapat dengan mudah diakuisis oleh program MS-Excel. Listing programnya sebagai berikut.
#include <NewPing.h>

#define TRIGGER_PIN 12 // jumper pin TRIG sensor ke pin 12 arduino
#define ECHO_PIN 11 // jumper pin ECHO sensor ke pin 11 arduino
#define MAX_DISTANCE 200 // jarak maks (cm).

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // bikin class baru

int uS = 0;
int row = 0; //untuk plxdaq

void setup() {
  Serial.begin(9600); // buka port serial, sets kecepatan data ke 9600 bps
  Serial.println("CLEARDATA");  //untuk plxdaq
  Serial.println("LABEL,Waktu,Jarak,Model GLBB oleh Rita Hartati SMKN 13 Bandung");  //untuk plxdaq
}

void loop() {
  //Baca pin input
  uS = sonar.ping(); // kirim ping dan simpan hasilnya di variabel uS (satuannya mikrodetik)

   //Kirim ke komputer melalui serial
   Serial.print("DATA,TIME,"); Serial.println(uS / US_ROUNDTRIP_CM);
 
  row++;    //untuk plxdaq
  uS++;   //untuk plxdaq

  delay(100);
}

Adapun library untuk sensor ultrasonik HCSR-04 diambil dari script yang telah dikembangkan oleh peneliti lain dengan lisensi free dari http://www.gnu.org/licenses/gpl-3.0.html
 // ---------------------------------------------------------------------------
// Created by Tim Eckel - teckel@leethost.com
// Copyright 2012 License: GNU GPL v3 http://www.gnu.org/licenses/gpl-3.0.html
//
// See "NewPing.h" for purpose, syntax, version history, links, and more.
// ---------------------------------------------------------------------------
#include "NewPing.h"
// ---------------------------------------------------------------------------
// NewPing constructor
// ---------------------------------------------------------------------------
NewPing::NewPing(uint8_t trigger_pin, uint8_t echo_pin, int max_cm_distance) {
                _triggerBit = digitalPinToBitMask(trigger_pin); // Get the port register bitmask for the trigger pin.
                _echoBit = digitalPinToBitMask(echo_pin);       // Get the port register bitmask for the echo pin.

                _triggerOutput = portOutputRegister(digitalPinToPort(trigger_pin)); // Get the output port register for the trigger pin.
                _echoInput = portInputRegister(digitalPinToPort(echo_pin));         // Get the input port register for the echo pin.

                _triggerMode = (uint8_t *) portModeRegister(digitalPinToPort(trigger_pin)); // Get the port mode register for the trigger pin.

                _maxEchoTime = min(max_cm_distance, MAX_SENSOR_DISTANCE) * US_ROUNDTRIP_CM + (US_ROUNDTRIP_CM / 2); // Calculate the maximum distance in uS.

#if DISABLE_ONE_PIN == true
                *_triggerMode |= _triggerBit; // Set trigger pin to output.
#endif
}

// ---------------------------------------------------------------------------
// Standard ping methods
// ---------------------------------------------------------------------------

unsigned int NewPing::ping() {
                if (!ping_trigger()) return NO_ECHO;                // Trigger a ping, if it returns false, return NO_ECHO to the calling function.
                while (*_echoInput & _echoBit)                      // Wait for the ping echo.
                                if (micros() > _max_time) return NO_ECHO;       // Stop the loop and return NO_ECHO (false) if we're beyond the set maximum distance.
                return (micros() - (_max_time - _maxEchoTime) - 5); // Calculate ping time, 5uS of overhead.
}

unsigned int NewPing::ping_in() {
                unsigned int echoTime = NewPing::ping();          // Calls the ping method and returns with the ping echo distance in uS.
                return NewPingConvert(echoTime, US_ROUNDTRIP_IN); // Convert uS to inches.
}
unsigned int NewPing::ping_cm() {
                unsigned int echoTime = NewPing::ping();          // Calls the ping method and returns with the ping echo distance in uS.
                return NewPingConvert(echoTime, US_ROUNDTRIP_CM); // Convert uS to centimeters.
}
unsigned int NewPing::ping_median(uint8_t it) {
                unsigned int uS[it], last;
                uint8_t j, i = 0;
                uS[0] = NO_ECHO;
                while (i < it) {
                                last = ping();           // Send ping.
                                if (last == NO_ECHO) {   // Ping out of range.
                                                it--;                // Skip, don't include as part of median.
                                                last = _maxEchoTime; // Adjust "last" variable so delay is correct length.
                                } else {                       // Ping in range, include as part of median.
                                                if (i > 0) {               // Don't start sort till second ping.
                                                                for (j = i; j > 0 && uS[j - 1] < last; j--) // Insertion sort loop.
                                                                                uS[j] = uS[j - 1]; // Shift ping array to correct position for sort insertion.
                                                } else j = 0;              // First ping is starting point for sort.
                                                uS[j] = last;              // Add last ping to array in sorted position.
                                                i++;                       // Move to next ping.
                                }
                                if (i < it) delay(PING_MEDIAN_DELAY - (last >> 10)); // Millisecond delay between pings.
                }
                return (uS[it >> 1]); // Return the ping distance median.
}
// ---------------------------------------------------------------------------
// Standard ping method support functions (not called directly)
// ---------------------------------------------------------------------------
boolean NewPing::ping_trigger() {
#if DISABLE_ONE_PIN != true
                *_triggerMode |= _triggerBit;    // Set trigger pin to output.
#endif
                *_triggerOutput &= ~_triggerBit; // Set the trigger pin low, should already be low, but this will make sure it is.
                delayMicroseconds(4);            // Wait for pin to go low, testing shows it needs 4uS to work every time.
                *_triggerOutput |= _triggerBit;  // Set trigger pin high, this tells the sensor to send out a ping.
                delayMicroseconds(10);           // Wait long enough for the sensor to realize the trigger pin is high. Sensor specs say to wait 10uS.
                *_triggerOutput &= ~_triggerBit; // Set trigger pin back to low.
#if DISABLE_ONE_PIN != true
                *_triggerMode &= ~_triggerBit;   // Set trigger pin to input (when using one Arduino pin this is technically setting the echo pin to input as both are tied to the same Arduino pin).
#endif
                _max_time =  micros() + MAX_SENSOR_DELAY;                  // Set a timeout for the ping to trigger.
                while (*_echoInput & _echoBit && micros() <= _max_time) {} // Wait for echo pin to clear.
                while (!(*_echoInput & _echoBit))                          // Wait for ping to start.
                                if (micros() > _max_time) return false;                // Something went wrong, abort.

                _max_time = micros() + _maxEchoTime; // Ping started, set the timeout.
                return true;                         // Ping started successfully.
}
// ---------------------------------------------------------------------------
// Timer interrupt ping methods (won't work with ATmega8 and ATmega128)
// ---------------------------------------------------------------------------
void NewPing::ping_timer(void (*userFunc)(void)) {
                if (!ping_trigger()) return;         // Trigger a ping, if it returns false, return without starting the echo timer.
                timer_us(ECHO_TIMER_FREQ, userFunc); // Set ping echo timer check every ECHO_TIMER_FREQ uS.
}
boolean NewPing::check_timer() {
                if (micros() > _max_time) { // Outside the timeout limit.
                                timer_stop();           // Disable timer interrupt
                                return false;           // Cancel ping timer.
                }

                if (!(*_echoInput & _echoBit)) { // Ping echo received.
                                timer_stop();                // Disable timer interrupt
                                ping_result = (micros() - (_max_time - _maxEchoTime) - 13); // Calculate ping time, 13uS of overhead.
                                return true;                 // Return ping echo true.
                }

                return false; // Return false because there's no ping echo yet.
}
// ---------------------------------------------------------------------------
// Timer2/Timer4 interrupt methods (can be used for non-ultrasonic needs)
// ---------------------------------------------------------------------------
// Variables used for timer functions
void (*intFunc)();
void (*intFunc2)();
unsigned long _ms_cnt_reset;
volatile unsigned long _ms_cnt;
void NewPing::timer_us(unsigned int frequency, void (*userFunc)(void)) {
                timer_setup();      // Configure the timer interrupt.
                intFunc = userFunc; // User's function to call when there's a timer event.
#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
                OCR4C = min((frequency>>2) - 1, 255); // Every count is 4uS, so divide by 4 (bitwise shift right 2) subtract one, then make sure we don't go over 255 limit.
                TIMSK4 = (1<<TOIE4);                  // Enable Timer4 interrupt.
#else
                OCR2A = min((frequency>>2) - 1, 255); // Every count is 4uS, so divide by 4 (bitwise shift right 2) subtract one, then make sure we don't go over 255 limit.
                TIMSK2 |= (1<<OCIE2A);                // Enable Timer2 interrupt.
#endif
}
void NewPing::timer_ms(unsigned long frequency, void (*userFunc)(void)) {
                timer_setup();                       // Configure the timer interrupt.
                intFunc = NewPing::timer_ms_cntdwn;  // Timer events are sent here once every ms till user's frequency is reached.
                intFunc2 = userFunc;                 // User's function to call when user's frequency is reached.
                _ms_cnt = _ms_cnt_reset = frequency; // Current ms counter and reset value.

#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
                OCR4C = 249;         // Every count is 4uS, so 1ms = 250 counts - 1.
                TIMSK4 = (1<<TOIE4); // Enable Timer4 interrupt.
#else
                OCR2A = 249;           // Every count is 4uS, so 1ms = 250 counts - 1.
                TIMSK2 |= (1<<OCIE2A); // Enable Timer2 interrupt.
#endif
}
void NewPing::timer_stop() { // Disable timer interrupt.
#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
                TIMSK4 = 0;
#else
                TIMSK2 &= ~(1<<OCIE2A);
#endif
}
// ---------------------------------------------------------------------------
// Timer2/Timer4 interrupt method support functions (not called directly)
// ---------------------------------------------------------------------------
void NewPing::timer_setup() {
#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
                timer_stop(); // Disable Timer4 interrupt.
                TCCR4A = TCCR4C = TCCR4D = TCCR4E = 0;
                TCCR4B = (1<<CS42) | (1<<CS41) | (1<<CS40) | (1<<PSR4); // Set Timer4 prescaler to 64 (4uS/count, 4uS-1020uS range).
                TIFR4 = (1<<TOV4);
                TCNT4 = 0;    // Reset Timer4 counter.
#else
                timer_stop();           // Disable Timer2 interrupt.
                ASSR &= ~(1<<AS2);      // Set clock, not pin.
                TCCR2A = (1<<WGM21);    // Set Timer2 to CTC mode.
                TCCR2B = (1<<CS22);     // Set Timer2 prescaler to 64 (4uS/count, 4uS-1020uS range).
                TCNT2 = 0;              // Reset Timer2 counter.
#endif
}
void NewPing::timer_ms_cntdwn() {
                if (!_ms_cnt--) {            // Count down till we reach zero.
                                intFunc2();              // Scheduled time reached, run the main timer event function.
                                _ms_cnt = _ms_cnt_reset; // Reset the ms timer.
                }
}
#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
ISR(TIMER4_OVF_vect) {
#else
ISR(TIMER2_COMPA_vect) {
#endif
                if(intFunc) intFunc(); // If wrapped function is set, call it.
}
// ---------------------------------------------------------------------------
// Conversion methods (rounds result to nearest inch or cm).
// ---------------------------------------------------------------------------
unsigned int NewPing::convert_in(unsigned int echoTime) {
                return NewPingConvert(echoTime, US_ROUNDTRIP_IN); // Convert uS to inches.
}
unsigned int NewPing::convert_cm(unsigned int echoTime) {
                return NewPingConvert(echoTime, US_ROUNDTRIP_CM); // Convert uS to centimeters.
}

4.      Pengisian program
Setelah selesai menulis script program langkah selanjutnya adalah mengisi program ke dalam IC Atmega 328p, dengan desain rangkaian sebagai berikut.
       
                                                        (a)                                            (b)       
Gambar 2. Rangkaian pengisian IC Atmega 328p blank.
IC Atmega 328p yang baru dibeli masih kosong dan tidak dapat diprogram langsung menggunakan lingkungan pemrogram arduino sehingga perlu diisi bootloader. Supaya IC tersebut siap dipakai maka peru diprogram menggunakan model ISP (in-Sircuit Programmer) seperti terlihat pada Gambar 4.2, papan (a) berfungsi sebagai master yang akan mengisi program, dan papan b dipasang IC Atmega 328p baru yang akan diisi program. papan (a) disambungkan ke komputer, dan papan (b) disampungkan ke papan (a).                                       
5.      Membuat templete file excel
Supaya dapat membuat grafik, maka perlu menyiapkan templete file excel berupa tabel untuk menampung data jarak dan waktu gerak kereta dinamika dari sensor, membuat tabel konversi berupa data kecepatan pada waktu tertentu, dan templete grafik untuk menampilkan data kecepatan fungsi waktu. Hasil templetenya sebagai berikut.

Gambar 3. Hasil templete file excel untuk akuisis data dari alat praktikum.     
    
6.      Merangkai alat percobaan
Langkah selanjutnya adalah merangkai alat controller dengan komponen percobaan lain berupa rel presisi dari KIT Optik beserta sampungan rel, balok bertumpuk dari KIT Mekanika, serta kereta dinamika. Hasilnya sebagai berikut.
 


Gambar 4. Rangkaian microcontroller pada alat praktikum GLBB.      

 






Gambar 5. Rangkaian alat praktikum GLBB.

7.      Ujicoba alat
Setelah selesai dirangkai, kemudian alat praktikum ini diuji keberfungsiannya serta diteliti apakah masih ada kendala teknis dalam penggunaannya. Tampilan layar excel hasil ujicoba adalah sebagai berikut.
 

 
Gambar 6. Hasil uji coba alat praktikum GLBB.

No comments:

Post a Comment