Jumat, 30 Agustus 2013

Generator Sinus Menggunakan Mikrokontroler ATmega8

Sebagian besar orang yang hoby mainan mikrokontroler memanfaat DAC melalui PWM. Tidak salah memang karena level tegangan dinyatakan pada persentase duty cycle pada gelombang kotak keluaran port PWM.

Pada kesempatan kali ini saya sedikit mencoba trik DAC yang lain yaitu dengan merubah level 8 bit bilangan pada port mikrokontroler kedalam level tegangan. Rangkaian konverter tegangan utama yaitu rangkaian 2R-R dibawah.
gambar 1. Rangkaian DAC (2R-R)
Langkah berikutnya adalah menyusun data yang akan dikeluarkan melalui rangkaian DAC diatas.
Sesuai dengan judul postingan ini, maka data yang akan dikeluakan merupakan serangkaian data yang akan membentuk gelombang sinus. Kali ini jumlah data yang akan dikeluarkan sebanyak 256 data. Dari 256 data, nilai masing – masing datanya disusun dalam bentuk array. Berikut array untuk gelombang sinus.

const char Sinus[] = { 
            0x80, 0x83, 0x86, 0x89, 0x8c, 0x8f, 0x92, 0x95, 0x98, 0x9c, 0x9f, 0xa2, 0xa5, 0xa8, 0xab, 0xae,
            0xb0, 0xb3, 0xb6, 0xb9, 0xbc, 0xbf, 0xc1, 0xc4, 0xc7, 0xc9, 0xcc, 0xce, 0xd1, 0xd3, 0xd5, 0xd8,
            0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xed, 0xef, 0xf0, 0xf2, 0xf3, 0xf5,
            0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 
            0xf6, 0xf5, 0xf3, 0xf2, 0xf0, 0xef, 0xed, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0, 0xde, 0xdc,
            0xda, 0xd8, 0xd5, 0xd3, 0xd1, 0xce, 0xcc, 0xc9, 0xc7, 0xc4, 0xc1, 0xbf, 0xbc, 0xb9, 0xb6, 0xb3,
            0xb0, 0xae, 0xab, 0xa8, 0xa5, 0xa2, 0x9f, 0x9c, 0x98, 0x95, 0x92, 0x8f, 0x8c, 0x89, 0x86, 0x83,
            0x80, 0x7c, 0x79, 0x76, 0x73, 0x70, 0x6d, 0x6a, 0x67, 0x63, 0x60, 0x5d, 0x5a, 0x57, 0x54, 0x51,
            0x4f, 0x4c, 0x49, 0x46, 0x43, 0x40, 0x3e, 0x3b, 0x38, 0x36, 0x33, 0x31, 0x2e, 0x2c, 0x2a, 0x27,  
            0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x12, 0x10, 0x0f, 0x0d, 0x0c, 0x0a,
            0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
            0x09, 0x0a, 0x0c, 0x0d, 0x0f, 0x10, 0x12, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x21, 0x23,
            0x25, 0x27, 0x2a, 0x2c, 0x2e, 0x31, 0x33, 0x36, 0x38, 0x3b, 0x3e, 0x40, 0x43, 0x46, 0x49, 0x4c,
            0x4f, 0x51, 0x54, 0x57, 0x5a, 0x5d, 0x60, 0x63, 0x67, 0x6a, 0x6d, 0x70, 0x73, 0x76, 0x79, 0x7c
            };

Selanjutnya tinggal menerapkanya kedalam rangkaian percobaan.
Kali ini saya menggunakan simulator proteus 7 dan program mikrokontroler menggunakan CodeVision AVR. Rangkaian percobaan dapat didownload disini.

Komponen yang diperlukan adalah mikrokontroler (saya menggunakan Atmega8) dan resistor
gambar 2. Skema rangkaian percobaan
Rangkaian DAC terhubung dengan PORTD Atmega8, untuk mengaktifkanya maka perlu men-set PORTD sebagai output. Proses ini dapat dilakukan melalui codewizard pada aplikasi CVAVR ata dengan menset DDR dan PORT pada PORTD sebagai berikut.

PORTD = 0x00;
DDRD = 0xFF;

Selanjutnya tinggal mengeluarkandata pada array sinus[] melalui PORTD ATmega8. Cara yang paling gampang adalah dengan memanfaatkan fungsi delay. Agar bisa memanggil fungsi ini terlebih dahulu kita memasukan library delay.h kedalam program.

#include "delay.h"

Selanjutnya tinggal mengeluarkan data satu - persatu melalui PORTD. Saya menggunakan perulangan for(.........) sebagai berikut.

      int i;
      for(i=0;i<=255;i++){
            PORTD=Sinus[i];
            delay_ms(1);
            }     
delay_ms(1) menyatakan bahwa selang waktu tiap data dikeluarkan adalah sebesar 1 ms, sehingga jika ingin merubah frekuensi keluaran gelombang sinus yang terbentuk adalah dengan cara merubah nilai pada delay_ms(int). Jika selang waktu 1 ms meka dalam satu kali gelombang penuh membutuhkan periode 1ms*256 = 256 ms sehingga frekuensi yang terbentuk 1/256 KHz atau 3.9 Hz. Terlalu kecil kah? jika ingin yang lebih cepat dapat menggunakan fungsi delay_us(int). Dengan fungsi ini 1 peride gelombang maksimal sebesar 1us*256 = 256 us. Sehingga frekuensi maksimal adalah 3.9 KHz. Misalkan kita mau membuat gelombang sinus dengan frekuensi 50 Hz, maka selang waktu antar data adalah sebesar (1/50)/256=0.000078125s atau sebesar 78us. Hasil gelombang sinus yang didapatkan ditunjukan oleh gambar 3.
gambar 3. Gelombang sinus yang terbentuk menggunakan DAC dan ATmega8
Sampai disini kita telah bisa membuat gelombang sinus menggunakan mikrokontroler. Akan tetapi, metode delay yang kita gunakan apabila diterapkan kedalam hardware masih memiliki banyak kekurangan dalam hal ketelitian. Pengaruh lingkungan dan sumber daya yang tidak stabil dapat menyebabkan selang waktu yang dihasilkan fungsi delay tidak stabil. Hal ini dapat diatasi dengan memanfaatkan fasilitas timer/counter yang dimiliki ATmega8. Berikut beberapa cara memanfaatkan timer/counter pada ATmega8.

Cara 1

Opsi pertama adalah menggunakan timer/counter sebagai pewaktu dengan cara memanfaatkan nilai counter sebagai index pada array sinus.
Pertama - tama pilih timer yang akan digunakan. Pada ATmega8 terdapat 3 timer yaitu timer 0, timer 1 dan timer 2. Timer 0 dan timer 2 masing - masing 8 bit sedangkan timer 1 merupakan timer 16 bit.
Array data sinus berjumlah 256 data jadi cukup menggunakan timer 8 bit saja. Untuk kasus ini saya pilih timer 0.
Setelah timer dipilih, langkah berikutnya adalah mengaktifkan timer dan memilih frekuensi clock timer. Hal ini cukup dilakukan dengan men-set register TCCR0. Jika TCCR0 = 0x00 maka timer 0 dalam keadaan tidak aktif (frekuensi clocknya 0). Pilihan frekuensi clock timer adalah sebagai berikut (timer prescaler).

  • FclockIO ==> TCCR0 = 0x01
  • FclockIO/8 ==> TCCR0 = 0x02
  • FclockIO/64 ==> TCCR0 = 0x03
  • FclockIO/256 ==> TCCR0 = 0x04
  • FclockIO/1024 ==> TCCR0 = 0x05

Dalam percobaan ini saya menggunakan frekuensi (FclockIO) internal 8MHz dan TCCR0 = 0x02 sehingga frekuensi timer adalah 1 MHz. Setelah register TCCR0 diberi nilai 0x02 maka timer telah aktif dan akan selalu menghitung dari 0 hingga 255 dan kembali lagi ke 0. Nilai register counter yang berubah nilainya adalah register TCNT0. Pada kondisi ini akan menghasilkan gelombang sinus dengan frekuensi 3.9 KHz.Sama seperti kita menggunakan delay_us(1) hanya saja penggunaan timer lebih stabil.
Kelemahan dari cara 1 ini adalah kita tidak dapat mengubah nilai frekuensi pada gelombang sinus yang terbentuk. Jadi cara 1 ini kita hanya diberikan 5 pilihan frekuensi.

Cara 2

Menggunakan cara 1 memiliki keterbatasan ketika kita hendak membuat generator sinus dengan frekuensi sesuka hati. Lalu bagaimana cara menggunakan timer agar kita bisa membuat gelombang yang frekuensinya bisa kita atur sesuka hati?
Salah satu solusi adalah dengan memanfaatkan CTC mode pada timer 2. Apa itu CTC mode? CTC mode  (Clear Timer On Compare Match mode) merupakan  sebuah penghitung yang akan clear (kembali ke inisialisasi) ketika nilai pada register TCNT2 sama dengan nilai register pembandingnya yaitu register OCR2 (Output Compare Register 2).
Register OCR2 memiliki nilai maksimal 0xff jadi bagaimana caranya membuat counter yang dapat mengitung sampai 0xff jika pembandingnya sendiri memiliki nilai maksimal 0xff. Jika kita men-set OCR2 = 0xff, maka ga ada bedanya dengan cara 1. Solusinya adalah dengan memanfaatkan routin interupsi.
Interupsi yang digunakan adalah interupt on timer 2 compare match. Fungsi interupt timer 2 compare match pada CVAVR yang saya gunakan adalah sebagai berikut.

interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
counter++;
if(counter==255){counter=0;}
}'

variabel counter adalah variabel global yang bertipe char. Pada fungsi ini ketika register TCNT2 nilainya sama dengan register OCR2 maka akan terjadi interupsi yang akan membuat nilai variabel counter bertambah 1. Nilai variabel counter inilah yang akan dijadikan indeks array sinus ketika mengeluarkan data pada tabel sinus ke PORTD.
Agar fungsi interupt diatas dapat berjalan maka harus men-set nilai pada register TIMSK(Timer Interupt Mask) = 0x80 dan meng-enable interupt dengan sintak #asm(""sei"). Untuk mengeluarkan data pada array sinus[] dengan sintak berikut.

PORTD=Sinus[counter];

Untuk merubah frekuensi gelombang yang terbentuk dengan cara mengubah nilai pada OCR2.

Cara 3

Pada cara 3 ini, saya akan menggabungkan fungsi interrupt on compare match dengan fungsi ADC. Parameter ADC yang digunakan adalah sebagai berikut.

  • ADC type = 8 bit
  • ADC ref   = ARef
  • ADC Clock = 1 MHz (FclockIO/8)

Register utama yang digunakan adalah register ADCSRA(ADC Status Register A) dan ADMUX (ADC Multiplexer).
ADMUX merupakan register 8 bit dengan bit 7 dan bit 6 merupakan untuk seting referensi ADC. Untuk tegangan referensi dari pin ARef maka nilai bit 7 = 0 dan bit 6 = 0. Bit ke 5 dari ADMUX adalah bit ADLAR (ADC Left Adjustmen Result). Nilai ADC result disimpan pada register ADCH dan ADCL yang masing - masing bernilai 8 bit. Jika bit ADLAR pada ADMUX bernilai 1 maka ADC result untuk ADC 8 bit pada ADCH. Jadi untuk ADC 8 bit dengan referensi ADC pin ARef maka nilai untuk register ADMUX adalah 0x20.
ADCSRA terdiri dari 8 bit. Untuk keperluan seting parameter diatas yang perlu diperhatikan hanyalah bit 7, bit 2, bit 1, dan bit 0. Bit 7 berfungsi untuk meng-enable kan ADC. Jika bit 7 bernilai 1 maka ADC enable dan sebaliknya. Bit 2 s/d 0 berfungsi untuk seting ADC prescaler. Untuk prescaler FclockIO/8 maka bit 1 dan bit 0 yang bernilai 1. Sehingga untuk percobaan ini nilai ADCSRA bernilai 0x83.
Jika menggunakan CVAVR, ketika mengaktifkan fungsi ADC akan dibuatkan fungsi pembacaan ADC sebagai berikut.

unsigned char read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCH;
}

Untuk memanggil fungsi diatas tinggal memasukan pin ADC yang akan kita baca. misalkan kita akan membaca ADC pada pin ADC0 maka kita tinggal memanggil fungsi read_adc(0).
Setelah dapat mengaktifkan ADC, selanjutnya adalah membuat rangkaian ADC. Rangkaian yang paling gampang adalah dengan menggunakan potensiometer seperti gambar 4 dibawah.
gambar 4. Rangkaian pembagi tegangan sebagai input ADC
Komponen interupsi dan timer yang digunakan sama seperti cara 2 dan perbedaanya ada pada nilai OCR2. Pada percobaan ini OCR2 diisi dari nilai hasil pembacaan ADC0. Program utamanya adalah sebagai berikut.

OCR2=read_adc(0);
PORTD=Sinus[counter];

Dengan cara 3 ini kita dapat mengubah frekuensi gelombang yang terbentuk dengan cara menggeser - geser potensiometer.

OK...sampai disini dulu postingan saya semoga bermanfaat dan silahkan pembaca kembangkan sendiri trik - trik yang lain yang jelas "Banyak jalan menuju Roma". Untuk source code pada percobaan diatas dapat di download disini

9 komentar:

  1. Keren! Maximum Frekuensi keluaran DAC nya brp? utk Atmega8-PU ( crsytal 16MHz )

    BalasHapus
  2. Untuk crystal 16MHz berarti periode clocknya 0.0625 us
    Untuk menghasilkan i gelombang penuh diperlukan 256 clock sehingga dibutuhkan waktu 0.0625*256 us = 16usfrekuensi = 1/periode sehingga frekuensi maksimal adalah 1/16us = 0.0625 MHz.

    CMIIW

    BalasHapus
  3. Sy ingin membuat sinyal sinus dengan frekuensi 40 - 120 kali persekon dengan cara 2. Bagaimana coding dan setting di cv avr nya mas ? Sy masih bingunh terima kasih.

    BalasHapus
    Balasan
    1. Maaf jika respon lama, lama ga buka blog juga.
      untuk frekuensi 40 Hz maka periode nya adalah 25 ms sedangkan frekuensi 120 Hz periode nya adalah 8.33 ms.
      Berarti periode tercepat untuk mengeluarkan 256 data pada array sinus adalah 8.33 ms.
      Periode 1 gelombang penuh adalah periode terjadinya 256 kali interupsi.
      Dari sini kita dapat menghitung periode terjadinya interrupt yaitu dengan cara (periode gelombang/256).
      Jadi periode terjadinya interrupt untuk frekuensi 40Hz = (25 ms/256) = 0.09765 ms = 97.6 us
      Sedangkan periode interrupt untuk frekuensi 120 Hz = (8,33 ms/256) = 0.03255 ms = 32.55 us

      Disini anda akan menemukan masalah baru yaitu adanya angka dibelakang koma pada periode interrupt. Karena register-register timer (TIMSK,TCNT, TCCR,OCR) pada mikrokontroler bertipe byte.

      Saya asumsikan menggunakan frekuensi clock io persis seperti contoh pada posting diatas( Fclock timer = 1Mhz) dan CTC mode pada timer2. Jadi register TCNT2 akan bertambah 1 tiap 1us.
      Nilai OCR2 inilah yang akan kita gunakan untuk membatasi hitungan pada register TCNT2.
      ketika nilai TCNT2 = OCR2 maka TCNT2 akan re-set(kembali ke 0) dan akan menge-set flag OCF2 yang akan membangkitkan interrupsi. Ketika terjadi interrupsi ini, variabel counter akan bertambah dan PORTD akan diisi nilai pada array sinus[counter]

      Periode interrupt yang anda harapkan :
      1. 97.6 us
      Jika diambil 97 => OCR2 = 97 maka frekuensi gelombang yang terbentuk adalah (1 / (OCR2*256)) = (1/24832 us)= 40.27 Hz
      Jika diambil 98 => OCR2 = 98 maka frekuensi gelombang yang terbentuk adalah (1 /(OCR2*256)) =(1/25088 us)= 39.85 Hz

      2. 32.55 us
      Jika diambil 32 maka frekuensi yang terbentuk 122.07 Hz
      Jika diambil 33 maka frekuensi yang terbentuk 118.37 Hz

      Jadi nilai OCR2 yang dapat mencakup frekuensi 40Hz - 120 Hz adalah 32 - 98

      Hapus
    2. Untuk setting di CV AVR bisa menggunakan codewizard, pada tab Timer pilih Timer2.
      Clock Source => System Clock
      Clock Value => 1.000.000 Hz
      Mode => CTCtop=OCR2
      Trus beri tanda centang pada compare match interrupt
      pada block compare masukan nilai untuk register OCR2 (dalam heksadesimal).

      Jika setingan benar pada template hasil generate codewizard akan terbentuk fungsi interrupt seperti dibawah.

      // Timer2 output compare interrupt service routine
      interrupt [TIM2_COMP] void timer2_comp_isr(void)
      {
      // Place your code here

      }

      dan pada blok main()

      // Timer/Counter 2 initialization
      // Clock source: System Clock
      // Clock value: 1000.000 kHz
      // Mode: CTC top=OCR2
      // OC2 output: Disconnected
      ASSR=0x00;
      TCCR2=0x0A;
      TCNT2=0x00;
      OCR2=0x20;

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

      Hapus
  4. // Timer/Counter 0 initialization
    // Clock source: System Clock
    // Clock value: 125.000 kHz
    // Mode: Normal top=0xFF
    // OC0 output: Disconnected
    TCCR0=0x03;
    TCNT0=0x83;
    OCR0=0x00;

    ini maksudnya bagaimana? helpme
    masih belum paham gan..

    BalasHapus
  5. Mohon bimbingnya om saya ingin membuat imverter gelombang sinus dengan rangkaian ini supaya outputnya trafo bisa sinus murni gmna om kalo masuk transistor berubah jadi kotak dan gg beraturan

    BalasHapus