Assalamualaikum Sahabat Whitecyber semua ….
Pada perjumpaan sore hari ini Whitecyber team mendapatkan Project dari Customer untuk membangun Perangkat ( Hardware ), kali ini kita beri judul Project Function Generator Menggunakan Analog Devices’ AD9833 – Arduino. Dalam proyek ini kita akan melihat sekilas fungsi komersial /generator bentuk gelombang untuk menentukan fitur apa yang penting untuk versi DIY. Setelah itu saya akan menunjukkan cara membuat generator fungsi sederhana, cara analog dan digital. Pada bagian akhir saya akan menyajikan kepada Anda desain generator fungsi DDS DIY yang (semacam) dapat bertahan hingga versi komersial.
Mari kita mulai!
Cara Membangun Generator Fungsi Anda Sendiri Menggunakan AD9833 Perangkat Analog
Bosan menggunakan PC atau ponsel Anda sebagai pembangkit sinyal? Saatnya mengupgrade ke standalone dengan membuatnya sendiri. Pelajari cara membuat generator bentuk gelombang dengan sesuka Anda sendiri menggunakan ATmega328p, IC generator fungsi DDS, op-amp, beberapa pasif, dan beberapa perangkat keras lainnya.
Memiliki laboratorium elektronik sendiri di rumah adalah hal yang bagus — satu-satunya kelemahan adalah peralatan yang digunakan pun bisa mahal. Merakit perangkat Anda sendiri tidak hanya lebih murah bagi dompet Anda, tetapi juga merupakan cara yang bagus untuk meningkatkan pengetahuan Anda. Oleh karena itu, dalam artikel ini saya akan menjelaskan cara membuat generator gelombang fungsi Anda sendiri.
Apa itu generator fungsi?
Pertama, generator fungsi (juga disebut generator nada) adalah perangkat elektronik yang dapat mengeluarkan bentuk gelombang tertentu pada frekuensi tertentu. Misalnya, seseorang dapat menghasilkan sinyal sinusoidal pada 60Hz. Anda dapat menggunakannya untuk menguji cara kerja amplifier audio, menemukan karakteristik op-amp dan dioda, mengeluarkan suara yang funky—daftar aplikasinya terus bertambah.
Generator fungsi DDS adalah generator bentuk gelombang digital arbitrer, artinya ia menggunakan konverter digital-ke-analog (DAC) untuk membangun sinyal. Ia juga memiliki memori hanya baca (ROM) yang menyimpan nilai amplitudo untuk bentuk gelombang tertentu pada berbagai interval waktu berdasarkan frekuensi pengambilan sampel (Fs).
Katakanlah kita memiliki generator sinyal DDS dengan DAC 8-bit dan menghasilkan sinyal sinusoidal pada 100Hz dengan frekuensi sampling 800Hz. Karena Fs delapan kali frekuensi gelombang sinus, seorang insinyur atau, lebih mungkin, komputer perlu mengekstrak delapan nilai amplitudo dari gelombang sinus nyata dari t = 0 hingga t = 2π. Interval amplitudo ini adalah [0, 255] (1111 1111 dalam biner = 255), yang sesuai dengan interval [-1, 1] dengan gelombang sinus nyata. Karena sebuah gambar dapat menyampaikan ribuan kata, di bawah ini adalah perbandingan antara gelombang sinus nyata dan gelombang sinus yang dihasilkan oleh generator fungsi DDS imajiner (dan berkinerja rendah).
.
.
Membangun Sendiri
Tujuannya adalah untuk membangun generator fungsi andal yang dapat mencapai frekuensi hingga 1MHz, amplitudo hingga 9V, dan memungkinkan Anda memilih antara sinyal sinusoidal, segitiga, dan jam (yaitu, persegi panjang dengan siklus kerja 50%). Untuk membantu Anda memahami mengapa saya memilih komponen yang digunakan, cara kerjanya, dan cara penulisan firmware, saya akan membagi artikel ini menjadi dua bagian besar, perangkat keras dan perangkat lunak.
Perangkat keras
Ada dua bagian utama mengenai aspek perangkat keras dari bangunan ini: catu daya dan PCB utama yang berisi IC generator fungsi dan mikrokontroler.
Catu Daya
PCB utama memerlukan dua rel tegangan: +12V dan -12V. Pasokan simetris diperlukan untuk penguatan akhir sinyal. Rel +5V yang lebih kecil akan dibuat langsung pada PCB utama dengan mengatur rel +12V; diperlukan untuk memberi daya pada mikrokontroler, AD9833, IC generator fungsi, dan terakhir osilator kristal 24MHz. Di bawah ini Anda dapat menemukan skema papan catu daya:
.
.
Untuk memperoleh tegangan tersebut akan digunakan trafo, dari 230V atau 110V (tergantung wilayah Anda) hingga dua jalur AC 12V (pada trafo biasanya akan tertulis sesuatu di sepanjang jalur 12V-0V-12V). Arus keluaran 200mA lebih dari cukup.
Ingatlah bahwa keluaran transformator adalah AC dan kita membutuhkan DC. Untuk ini, kita akan menggunakan jembatan penyearah sederhana. Ini akan mengubah sinusoida menjadi sinyal positif. Ini biasanya datang sebagai komponen yang berdiri sendiri tetapi Anda juga dapat menggunakan empat dioda tujuan umum seperti 1N4001.
Kami tidak akan menggunakannya dalam konfigurasi standar karena kami menginginkan keluaran yang simetris, jadi kami akan menghubungkannya seperti pada skema di atas: ujung trafo dihubungkan ke penyearah, dan keran tengah dihubungkan ke ground. Untuk memuluskan output pertama-tama kita akan menggunakan dua kapasitor besar, masing-masing 1000µF dengan tegangan 35V. Di bawah ini Anda dapat menemukan bentuk gelombang keluaran untuk rel catu daya pada berbagai tahap:
.
.
Selain itu, untuk menghilangkan riak, digunakan dua pengatur tegangan, yaitu LM7812 klasik dan saudaranya LM7912, yang digunakan untuk tegangan negatif. Sebagai tambahan, kami menambahkan kapasitor 100µF dan kapasitor 100nF, keduanya berkekuatan 25V, ke setiap output IC regulator. Kami ingin suplai ini selancar mungkin, karena kami akan menggunakannya untuk mengimbangi tegangan keluaran akhir, dan komponen AC apa pun akan merambat ke keluaran.
Papan AD9833
Mari kita ke PCB utama. Regulator linier LM1117-5V digunakan untuk membuat rel +5V dari suplai +12V. Agar semuanya lancar, kami menambahkan kapasitor ke saluran masukan tegangan (+12V dan -12V) dan keluaran pengatur tegangan.
.
.
Di sisi kiri skema, Anda akan menemukan mikrokontroler ATMega328p-AU, sama seperti yang digunakan pada Arduino Uno tetapi dalam paket SMD. Untuk memprogram MCU, konektor 6-pin yang disebut AVR-ISP ditempatkan di sebelahnya. Ia memiliki dua pin untuk daya (+5V dan GND) dan empat pin lainnya untuk komunikasi: MISO, MOSI, CLK, dan RESET.
Kami hanya akan menggunakan satu komponen masukan pengguna, encoder putar dengan sakelar terintegrasi. Ini akan menjadi elemen kontrol kita untuk mengatur frekuensi, jenis sinyal, dan pengaturan lainnya. Sinyal dari rotary encoder disalurkan ke dua pin interupsi pada mikrokontroler, PD2 dan PD3 (D2 dan D3 pada Uno), dan sakelar menuju ke pin yang tersedia yang dapat bertindak sebagai input; Saya memilih PD1 (D1 di Uno). Kami tidak akan menambahkan resistor pull-up apa pun karena kami akan menggunakan resistor yang terintegrasi ke dalam mikrokontroler. Karena kontak mekanis tidak sempurna, maka ketika kita memutar encoder, alih-alih pulsa ideal, sinyal gelisah akan muncul, tetapi hal ini dapat dengan mudah diperbaiki baik oleh perangkat lunak atau perangkat keras menggunakan kapasitor. Untuk sakelar terintegrasi, kami akan menggunakan metode perangkat lunak, dan untuk kontak berputar, kami akan menggunakan dua kapasitor 100nF.
LED hijau polos dihubungkan ke pin PD7 (D7 pada Uno), untuk tujuan debugging atau untuk menampilkan status. Layar yang digunakan adalah LCD sederhana dengan pengontrol HD44780 yang memiliki 16 baris dan dua kolom serta dilengkapi lampu latar. Untuk mengurangi jumlah kabel penghubung, papan adaptor digunakan sehingga pin yang diperlukan untuk mengontrol LCD hanyalah 2 untuk data melalui I2C dan 2 lagi untuk daya (+5V dan GND). Jalur I2C pada mikrokontroler terdapat pada pin PC4 untuk SDA dan PC5 untuk SCL. Di Arduino Uno, masing-masing diberi nama A4 dan A5.
Terakhir, koneksi terakhir untuk mikrokontroler dibuat antara mikrokontroler dan sirkuit terintegrasi AD9833. Menggunakan bus SPI searah, artinya data hanya dapat mengalir dalam satu arah, dari MCU ke IC. Sinyalnya adalah MOSI (Master Out – Slave In), CLK (Clock), dan CS (Chip Select, disebut FSYNC pada AD9833). Untuk menyimpulkan bagian mikrokontroler, saya harus menyebutkan bahwa kapasitor decoupling dengan nilai 100nF diperlukan untuk setiap pin catu daya.
.
.
Sebelum melanjutkan ke bagian terakhir skema, bagian dengan IC generator fungsi, izinkan saya menunjukkan beberapa fitur utama AD9833. Meskipun saya akan terdengar seperti seorang penjual, mari kita mulai. Merupakan generator gelombang yang dapat diprogram tipe DDS, sehingga dibutuhkan sinyal clock dengan frekuensi maksimum 25MHz (untuk IC khusus ini), yang kemudian dibagi berdasarkan nilai yang diteruskan oleh mikrokontroler (maksimum 2 28 ) melalui bus SPI , dan menggunakan DAC 10-bit, ia mengeluarkan bentuk gelombang yang dipilih oleh mikrokontroler. Outputnya memiliki nilai puncak ke puncak 0,65V-0,038V (VOUT maksimum – VOUT minimum), dan memiliki offset (0,65V-0,038V)/2. Ini berarti VOUT minimum sangat mendekati 0V. Satu catatan penting adalah keluaran jam (sinyal persegi panjang) memiliki amplitudo 2,5V, yang merupakan tegangan yang dihasilkan oleh pengatur tegangan internal perangkat.
Sekarang kita sudah menyelesaikannya, mari kita lihat bagian terakhir dari skema ini. Osilator kristal digunakan untuk memasukkan sinyal clock 24MHz ke input jam master AD9833. FSYNC, SCLK, dan SDATA terhubung ke bus SPI mikrokontroler. Dua kapasitor decoupling digunakan di dekat pin VDD, satu dengan nilai 0,1µF dan satu lagi dengan nilai 10µF. Kapasitor decoupling lain diperlukan untuk pengatur tegangan internal 2.5V; ini dekat pin CAP dan memiliki nilai 0,1µF. Diperlukan satu kapasitor lagi antara pin COMP dan VDD untuk memisahkan tegangan bias DAC; itu memiliki nilai 10nF, seperti yang ditunjukkan oleh lembar data.
.
.
Beberapa paragraf di atas ketika saya berbicara tentang koneksi mikrokontroler, saya juga menulis padanan pin Arduino Uno. Saya melakukan ini karena rangkaian AD9833 yang dijelaskan pada paragraf sebelumnya tersedia di berbagai pabrikan Cina sebagai papan breakout yang dapat Anda sambungkan ke Arduino Uno. Perlu diingat bahwa outputnya langsung dari IC, tanpa rangkaian tambahan yang akan terus saya jelaskan di bawah ini.
Keluaran IC dimasukkan ke masukan non-pembalik op-amp menggunakan resistor 1,3kΩ. Pemangkas POT1 (resistor variabel), yang pin sampingnya terhubung ke -12V dan +12V, memiliki penyapu yang terhubung ke terminal input sakelar untuk memberikan tegangan offset. Terminal masukan lain dari sakelar SPDT dihubungkan langsung ke ground, dan terminal keluaran dihubungkan ke masukan non-pembalik op-amp dengan resistor 1,3kΩ lainnya. Konfigurasi ini dikombinasikan dengan potensiometer 50kΩ memungkinkan kita mendapatkan penguatan variabel antara 1 dan sekitar 20. Hal ini dihitung menggunakan teorema Millman dan fakta bahwa op-amp menjaga inputnya, V+ dan V-, pada tegangan yang sama:
.
.
Ingatlah bahwa penguatan juga diterapkan pada tegangan offset. Saya melakukan ini untuk memiliki tiga konfigurasi:
- Tegangan offset diatur ke -350mV menggunakan pemangkas POT1. Ketika saklar pada posisi 3 (R4 terhubung ke ground), tegangan keluaran adalah keluaran IC, sinyal dengan amplitudo 350mV dan offset 350mV, dikalikan dengan penguatan yang diatur oleh potensiometer POT2. Ketika sakelar berada di posisi 1 (R4 terhubung ke tegangan offset yang diperoleh oleh pemangkas POT1), outputnya sama seperti sebelumnya dikurangi tegangan offset, pada dasarnya sinyal dengan amplitudo 350mV dan tanpa offset, dikalikan dengan penguatan.
Dengan konfigurasi ini, tegangan output dapat berayun dari -7V ke +7V atau dari 0V ke kemampuan op-amp (mendekati +12V). - Tegangan offset diatur ke VOUT minimum AD9833. Ini kemudian dikalikan dengan keuntungan, yang dapat bervariasi dari 1 hingga 20.
- Alih-alih pemangkas POT1, Anda dapat menggunakan potensiometer dan memasangnya di panel depan sehingga Anda dapat memiliki offset variabel. Ingatlah bahwa offset juga dipengaruhi oleh penguatan, jadi sebaiknya atur penguatan terlebih dahulu, pilih tegangan puncak-ke-puncak yang Anda butuhkan, dan setelah itu gunakan potensiometer offset untuk mengatur posisi vertikal sinyal. Saya tidak merekomendasikan pengaturan ini; jika Anda ingin variabel offset, saya sarankan Anda menambahkan op-amp terpisah sebagai penguat penjumlahan dengan gain tetap 2, seperti pada skema di bawah ini.
.
.
Untuk menyelesaikan bagian penguatan rangkaian, saya harus menyebutkan bahwa Anda memerlukan kapasitor decoupling 100nF untuk catu daya positif dan negatif dan Anda harus memilih op-amp yang dapat menahan tegangan catu daya ( +12V dan -12V) dan memiliki laju perubahan tegangan yang sangat baik, sehingga dapat mengimbangi sinyal amplifikasi yang tinggi.
Idealnya, sirkuit ini harus dibangun pada PCB yang dirancang khusus untuk aplikasi ini dan bukan pada papan prototipe. Saya akan memberi Anda PCB yang sudah dirancang di KiCad melalui file di bawah ini. Namun, jika Anda ingin membuatnya sendiri, perhatikan hal-hal berikut:
- Tempatkan kapasitor decoupling untuk semua sirkuit terpadu sedekat mungkin dengan pin catu daya.
- Usahakan menggunakan ground plane (jika hanya menggunakan dua lapisan, buatlah lapisan paling bawah sebagian besar terdiri dari tanah).
- Pisahkan bagian analog rangkaian (output AD9833, rangkaian penguat, dan konektor BNC keluaran) dari bagian digital (mikrokontroler dan sisi komunikasi AD9833). Anda dapat membagi bidang tanah tepat di bawah AD9833. Hal ini dapat dilakukan dengan mudah karena IC memiliki dua pin terpisah untuk ground analog dan digital.
- Seperti yang disarankan dalam lembar data AD9833, hindari menjalankan jejak digital di bawah AD9833.
.
.
Semua komponen yang diperlukan untuk membangun ini dapat ditemukan di BOM yang disertakan dalam file zip di bagian bawah artikel.
Sekarang setelah kita selesai membuat sirkuit, mari selami pemrograman.
Perangkat lunak
Untuk menulis kode yang akan diunggah ke memori mikrokontroler, kita akan menggunakan Arduino IDE. Dalam beberapa paragraf berikut saya akan menjelaskan cara menulis perpustakaan untuk AD9833 dan kemudian perangkat lunak utama yang memungkinkan kita menghubungkan mikrokontroler dengan AD9833, LCD, dan rotary encoder. AD9833 memiliki apa yang disebut register, yang pada dasarnya adalah lokasi memori, tempat kita dapat memasukkan data, dan berdasarkan nilai-nilai ini, sirkuit terpadu mengubah mode operasinya; ia memilih bentuk gelombang apa yang akan dihasilkan, fase, dan faktor pembagian. Kami akan bekerja dengan lima register: register kontrol; register dua fase, PHASE0 dan PHASE1; dan dua register frekuensi, FREQ0 dan FREQ1.
Perpustakaan AD9833
Kami akan membuat dua file, AD9833.cpp, file yang akan berisi kode sumber, dan AD9833.h, headernya.
Di dalam header, kita menentukan nama kelas (AD9833) dan, seperti boneka bersarang Rusia, di dalamnya, variabel dan fungsi yang akan kita panggil untuk berkomunikasi dengan IC generator bentuk gelombang dan untuk mengubah parameternya, seperti frekuensi dan jenis gelombang. Ini bisa berupa salah satu dari dua tipe: publik, yang bisa kita panggil dari luar metode kelas (fungsi), atau privat, yang bisa kita panggil hanya ketika kita berada di dalam metode kelas. Saya telah menambahkan komentar agar Anda memiliki gambaran tentang tujuan setiap fungsi dan variabel; Misalnya:
.
//Initialise the AD9833
//_FYNC is the pin on the uC where FYNC is connected
//_mclk is the frequency of the crystal generator
AD9833(int _FSYNC, unsigned long _mclk);
Lanjut ke file sumber, saat kita menginisialisasi objek baru bertipe AD9833, beberapa hal akan terjadi; ini ada di dalam fungsi “ AD9833::AD9833 (int _FSYNC, unsigned long _mclk) ”. Pertama, kita mencatat pin FSYNC yang telah kita lewati sebagai argumen, dan kita menetapkannya sebagai pin keluaran. Saat menulis ke AD9833, pin ini akan menjadi RENDAH. Di dalam metode ini, kami juga menetapkan beberapa nilai default untuk register sehingga kami mengeluarkan sinyal sinusoidal pada 1kHz menggunakan register FREQ0. Baris terakhir mengatur SPI ke mode2, yaitu pengaturan yang digunakan mikrokontroler dan AD9833 untuk berkomunikasi.
Yang tersisa sekarang hanyalah membaca lembar data dengan cermat dan melihat nilai apa yang harus kita atur di register AD9833 untuk memanipulasi bentuk gelombang keluaran dan pengoperasiannya. Jadi, kita akan menulis fungsi untuk operasi berikut: menulis data, mengatur frekuensi, mengatur fase, tidur, mengatur ulang, mode, dan memilih register frekuensi/fase.
Kami akan bekerja secara langsung pada level bit. Kadang-kadang kita mungkin ingin mengubah nilai seluruh register, misalnya untuk register frekuensi, namun kadang-kadang kita hanya ingin mengubah beberapa bit dari keseluruhan kata. Untuk mencapai hal ini, kami akan menggunakan operasi berikut:
- “&=” Untuk menyetel beberapa bit ke 0 dan membiarkan sisanya tidak terganggu. (0 = setel ke 0, 1 = biarkan apa adanya). Contoh:
controlRegister &= 0xF3FF; // Set D11 and D10 in control register to 0
- “|=” Untuk menyetel beberapa bit ke 1 dan membiarkan sisanya tidak terganggu. (1 = set ke 1, 0 = biarkan apa adanya). Contoh:
controlRegister |= 0x0C00; // Set D11 and D10 in control register to 1
Pada tabel di bawah ini saya telah mengekstrak dari datasheet bit mana yang perlu diatur untuk menjalankan operasi.
.
.
Register memiliki ukuran yang berbeda-beda. Register kontrol panjangnya 16 bit, register fase panjangnya 12 bit, dan register frekuensinya 28 bit. Untuk mengeluarkan data melalui SPI, kami mengirimkan satu byte pada satu waktu, seperti yang dapat dilihat pada fungsi “writeData”, dimulai dengan byte rendah (8 bit pertama dari bilangan bulat data) dan kemudian byte tinggi.
Setting frekuensinya sedikit lebih rumit karena kita tidak langsung mengirimkan frekuensi yang kita inginkan. Menurut lembar data, keluaran analognya adalah f MCLK /2 28 × FREQREG. Dengan demikian, register frekuensi harus diatur ke:
$$FREQREG = \frac{frekuensi \cdot 2^{28}}{f_{mclk}}$$
Karena bilangan yang didapat bisa sepanjang 28 bit, maka kita bagi menjadi dua kata yang masing-masing panjangnya 16 bit, lalu kita kirimkan datanya dimulai dari yang paling bawah.
Metode selanjutnya akan mudah dilakukan jika Anda mengikuti tabel di atas dan indikasi saya di komentar.
Rutinitas Utama
Untuk kode utama yang akan menghubungkan AD9833, LCD, rotary encoder, dan mikrokontroler bersama-sama kita akan melakukan sedikit cheat dan menggunakan beberapa perpustakaan yang ditulis dengan sangat baik untuk LCD , yang menggunakan bus I2C, dan untuk rotary encoder .
Generator bentuk gelombang sewenang-wenang kami tidak memanfaatkan fungsi fase dengan baik; Saya telah memutuskan untuk membiarkan fitur ini tidak tersedia, tetapi Anda dapat menggunakannya dengan menghapus komentar pada perintah “//#define usePhase”. Namun, melakukan hal itu akan menonaktifkan kemampuan untuk memilih antara register FREQ0 dan FREQ1.
Saya mencoba membuat perangkat lunak ini semudah mungkin digunakan. Dari awal pada LCD Anda dapat melihat frekuensi di pojok kiri atas dan mengubahnya digit demi digit, di pojok kanan atas adalah status daya keluaran analog, yang bisa ON atau OFF, artinya Anda dapat menghidupkan mematikan output tanpa mematikan perangkat. Di pojok kiri bawah, Anda dapat memilih register yang digunakan untuk menyimpan frekuensi, baik FREQ0 atau FREQ1. Ini berguna jika Anda ingin beralih dengan mudah di antara dua frekuensi berbeda. Terakhir, di pojok kanan bawah terdapat jenis gelombang yang ingin dihasilkan, baik gelombang sinus, gelombang segitiga, atau gelombang persegi panjang. Ingatlah bahwa keluaran jam akan selalu memiliki amplitudo yang lebih tinggi, karena AD9833 mengeluarkannya pada 2,5V dibandingkan dengan 0,65V untuk sinyal sinusoidal dan segitiga.
.
.
Beginilah cara Anda melakukan perubahan: Menekan encoder akan membuat kursor aktif, lalu Anda memutar encoder untuk “scroll” (bergerak) di antara empat pengaturan (frekuensi, ON/OFF, FREQ0/1, dan tipe gelombang). Setelah memilih pengaturan yang akan diubah, menekan encoder akan mengubah pengaturan (jika ON/OFF atau FREQ0/1 telah dipilih), atau Anda dapat mengubah nilai pengaturan dengan memutar encoder (memutar menambah/mengurangi digit saat ini atau mengubah jenis bentuk gelombang, tergantung pada pengaturan yang dipilih). Menekan lagi akan menerapkan pengaturan (untuk jenis bentuk gelombang) atau berpindah ke digit berikutnya (untuk frekuensi).
Saya telah mencoba membuat kode sesingkat mungkin, dan komentar tersedia untuk membantu Anda memahami proses pemikiran saya. Jika Anda memiliki pertanyaan, jangan ragu untuk meninggalkan komentar atau menghubungi saya.
Saya sangat berharap semuanya berjalan lancar dan Anda dapat menikmati Generator Bentuk Gelombang Sewenang-wenang Anda sendiri.
Berikut adalah beberapa bentuk gelombang keluaran untuk Anda nikmati:
gelombang sinus, 1V puncak-ke-puncak, 1kHz, tanpa offset
.
.
gelombang segitiga, 1V puncak-ke-puncak, 1kHz, tanpa offset
.
.
sinyal jam, 11V puncak-ke-puncak, 500Hz, dengan offset
.
.
.
Sekarang saatnya berburu komponen. Di sini Anda dapat menemukan daftar suku cadang dengan contoh penjual (tautan afiliasi):
Aliexpress:
1x trafo sadap tengah 12V: https://s.click.aliexpress.com/e/_dZzaRwR
1x soket IEC: https://s.click.aliexpress.com/e/_dYQWdHl
1x penyearah jembatan penuh B40C2300: https://s.click.aliexpress.com/e/_dUSXlfZ
1x pengatur LM7812 12V: https://s.click.aliexpress.com/e/_d8GjYYf
1x pengatur LM7912 -12V: https://s.click.aliexpress.com/e/_d6ueEfl
1x pengatur LM7805 5V: https://s.click.aliexpress.com/e/_dWITJXp
1x kit kapasitor: https://s.click.aliexpress.com/e/_d8NcJ0L
1x Arduino Nano: https://s.click.aliexpress.com/e/_d85qR0x
1x Enkoder Putar: https://s.click.aliexpress.com/e/_dXKK3dV
1x IC DDS AD9833: https://s.click.aliexpress.com/e/_dTc4NZH
1x LCD I2C: https://s.click.aliexpress.com/e/_d6HiiMB
1x OpAmp TL071: https://s.click.aliexpress.com/e/_dXd3VYL
1x konektor BNC: https://s.click.aliexpress.com/e/_dZ51v0F
1x10k, 50k Potensiometer: https://s.click.aliexpress.com/e/_dU8s3rt
eBay:
1x trafo sadap tengah 12V: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x soket IEC: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x penyearah jembatan penuh B40C2300: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x pengatur LM7812 12V: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x pengatur LM7912 -12V: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x pengatur LM7805 5V: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x Kit Kapasitor: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x Arduino Nano: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x Enkoder Putar: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x IC DDS AD9833: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x LCD I2C: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x OpAmp TL071: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x konektor BNC: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
1x 10k, Potensiometer 50k: http://rover.ebay.com/rover/1/711-53200-19255-0/1?…
Amazon.de:
1x trafo sadap tengah 12V: –
1x soket IEC: https://amzn.to/2UDXsK1
1x penyearah jembatan penuh B40C2300: https://amzn.to/2HVn8wn
1x pengatur LM7812 12V: https://amzn.to/34vhVoP
1x pengatur LM7912 -12V: https://amzn.to/34vN9vQ
1x pengatur LM7805 5V: https://amzn.to/2N1l4ag
1x kit kapasitor: https://amzn.to/34vpEmB
1x Arduino Nano: https://amzn.to/34yTMgX
1x Enkoder Putar: https://amzn.to/300YJMl
1x IC DDS AD9833: https://amzn.to/2MZm7HP
1x LCD I2C: https://amzn.to/31bhEW8
1x OpAmp TL071: https://amzn.to/2ZXTb53
1x konektor BNC: https://amzn.to/314ktI9
1x 10k, Potensiometer 50k: https://amzn.to/2HOI34j
.
Bangun Sirkuit
Sekarang kita mulai membuat sirkuit nya. Di sini Anda dapat menemukan skema rangkaian serta gambar referensi konstruksi perfboard saya yang telah selesai. Jangan ragu untuk menggunakannya.
.
.
Dengan menggabungkan beberapa komponen, maka akan bisa kita lihat sebagai berikut :
.
.
.
Unggah Kode
Di sini Anda dapat menemukan kode untuk proyek ini. Anda perlu mengunggahnya ke Arduino sebelum generator fungsi Anda dapat bekerja dengan sukses. Terima kasih banyak sekali lagi kepada Cezar Chirila atas karyanya. Kode tersebut cukup banyak dibuat olehnya.
.
Buat Rumahnya
.
.
Jadi Hardwarenya.
.
.
Code Program kami share disini :
https://github.com/whitecybercode/GeneratorGelombang
Code File SignalGenerator.ino
#include "LiquidCrystal_I2C.h"
#include "AD9833.h"
#include "Rotary.h"
//Batalkan komentar pada baris di bawah ini jika Anda ingin mengubah Phase dan bukan register FREQ
//#definisikan Fase Penggunaan
AD9833 sigGen(10, 24000000000); // disini 4 Inisialisasi AD9833 kita dengan pin FSYNC = 10 dan frekuensi clock master 24MHz jadi 24GHz AD9833 sigGen(10, 24000000);
LiquidCrystal_I2C lcd(0x27, 16, 2); // Inisialisasi LCD
Rotary encoder(3, 2);// Inisialisasi encoder pada pin 2 dan 3 (pin interupsi)
// Variabel yang digunakan untuk memasukkan data dan menelusuri menu
unsigned long encValue; // Nilai yang digunakan oleh pembuat enkode
unsigned long lastButtonPress; // Nilai yang digunakan oleh tombol debounce
unsigned char lastButtonState;
unsigned char settingsPos[] = {0, 14, 20, 29};
unsigned char button;
volatile unsigned char cursorPos = 0;
unsigned char lastCursorPos = 0;
unsigned char menuState = 0;
const int buttonPin = 4;
int digitPos = 0;
const unsigned long maxFrequency = 14000000000; // disini 3 : menaikkan max requensi
const unsigned int maxPhase = 4095; // Hanya digunakan jika Anda mengaktifkan pengaturan PHASE dan bukan register FREQ
unsigned long newFrequency = 1000;
volatile bool updateDisplay = false;
unsigned long depressionTime;
int freqRegister = 0; // Daftar FREQ default adalah 0
// LCD constants
const String powerState[] = {" ON", "OFF"};
const String mode[] = {"SIN", "TRI"}; // Disini 1 - const String mode[] = {"SIN", "TRI", "CLK"};
// Variabel yang digunakan untuk menyimpan fase, frekuensi, mode dan daya
unsigned char currentMode = 0;
unsigned long frequency0 = 1000;
unsigned long frequency1 = 1000;
unsigned long frequency = frequency0;
unsigned long currFrequency; // Frekuensi saat ini yang digunakan, baik 0 atau 1
unsigned long phase = 0; // Hanya digunakan jika Anda mengaktifkan pengaturan PHASE dan bukan register FREQ
unsigned char currentPowerState = 0;
// Simbol PHI Yunani untuk pergeseran fasa
// Hanya digunakan jika Anda mengaktifkan pengaturan PHASE dan bukan register FREQ
uint8_t phi[8] = {0b01110, 0b00100, 0b01110, 0b10101,
0b10101, 0b01110, 0b00100, 0b01110
};
void setup() {
// Inisialisasi LCD, nyalakan lampu latar dan cetak pesan "bootup" selama dua detik
lcd.begin();
lcd.backlight();
lcd.createChar(0, phi); // Karakter PHI khusus untuk LCD
lcd.home();
lcd.print("AllAboutCircuits");
lcd.setCursor(0, 1);
lcd.print("Signal Generator");
delay(2000);
// Menampilkan nilai set awal untuk frekuensi, fase, mode, dan daya
lcd.clear();
displayFrequency();
displayMode();
#ifdef usePhase
displayPhase();
#endif
displayPower();
#ifndef usePhase
displayFreqRegister();
#endif
// Inisialisasi AD9833 dengan keluaran sinus 1KHz, tidak ada pergeseran fasa untuk keduanya
// mendaftar dan tetap berada di register FREQ0
// sigGen.lcdDebugInit(&lcd);
sigGen.reset(1);
sigGen.setFreq(frequency0);
sigGen.setPhase(phase);
sigGen.setFPRegister(1);
sigGen.setFreq(frequency1);
sigGen.setPhase(phase);
sigGen.setFPRegister(0);
sigGen.mode(currentMode);
sigGen.reset(0);
// Tetapkan pin A dan B dari encoder sebagai interupsi
attachInterrupt(digitalPinToInterrupt(2), encChange, CHANGE);
attachInterrupt(digitalPinToInterrupt(3), encChange, CHANGE);
// Inisialisasi pin sebagai input dengan pull-up diaktifkan dan variabel debounce untuk
// tombol pembuat enkode
pinMode(4, INPUT_PULLUP);
lastButtonPress = millis();
lastButtonState = 1;
button = 1;
// Atur Kursor ke posisi awal
lcd.setCursor(0, 0);
}
void loop() {
// Periksa apakah tombol telah ditekan
checkButton();
// Perbarui tampilan / display jika diperlukan
if (updateDisplay == true) {
displayFrequency();
#ifdef usePhase
displayPhase();
#endif
displayPower();
#ifndef usePhase
displayFreqRegister();
#endif
displayMode();
updateDisplay = false;
}
// Kita menggunakan variabel menuState untuk mengetahui di mana kita berada dalam menu dan
// apa yang harus dilakukan jika kita menekan tombol atau menambah/mengurangi melalui
// pembuat enkode
// Masuk ke mode pengaturan jika tombol telah ditekan dan tampilan berkedip
// arahkan kursor ke opsi (menuState 0)
// Pilih pengaturan (menuState 1)
// Ubah pengaturan tertentu dan simpan pengaturan (menuState 2-5)
switch (menuState) {
// Default state
case 0: {
lcd.noBlink();
if (button == 0) {
button = 1;
lcd.setCursor(0, 0);
lcd.blink();
menuState = 1;
cursorPos = 0;
}
} break;
// Settings mode
case 1: {
if (button == 0) {
button = 1;
// Jika pengaturan di Power cukup aktifkan dan nonaktifkan
if (cursorPos == 1) {
currentPowerState = abs(1 - currentPowerState);
updateDisplay = true;
menuState = 0;
if (currentPowerState == 1)
sigGen.sleep(3); // DAC dan jam dimatikan
else
sigGen.sleep(0); // DAC dan jam dihidupkan
}
// Jika usePhase belum disetel
#ifndef usePhase
else if (cursorPos == 2) {
updateDisplay = true;
menuState = 0; // kembali ke "menu utama"
if (freqRegister == 0) {
freqRegister = 1;
sigGen.setFPRegister(1);
frequency = frequency1;
} else {
freqRegister = 0;
sigGen.setFPRegister(0);
frequency = frequency0;
}
}
#endif
// Jika tidak, tetapkan status baru
else
menuState = cursorPos + 2;
}
// Pindahkan posisi kursor jika ada perubahan
if (lastCursorPos != cursorPos) {
unsigned char realPosR = 0;
unsigned char realPosC;
if (settingsPos[cursorPos] < 16)
realPosC = settingsPos[cursorPos];
else {
realPosC = settingsPos[cursorPos] - 16;
realPosR = 1;
}
lcd.setCursor(realPosC, realPosR);
lastCursorPos = cursorPos;
}
} break;
// Pengaturan frekuensi// Frequency setting
case 2: {
// Setiap penekanan tombol akan memungkinkan untuk mengubah nilai digit lainnya
// atau jika semua digit telah diubah, untuk menerapkan pengaturan
if (button == 0) {
button = 1;
if (digitPos < 7)
digitPos++;
else {
digitPos = 0;
menuState = 0;
sigGen.setFreq(frequency);
}
} else if (button == 2) {
button = 1;
digitPos = 0;
menuState = 0;
}
// Atur kursor berkedip pada digit yang saat ini dapat Anda ubah
lcd.setCursor(9 - digitPos, 0);
} break;
// Pengaturan fase
case 4: {
if (button == 0) {
button = 1;
if (digitPos < 3)
digitPos++;
else {
digitPos = 0;
menuState = 0;
sigGen.setPhase(phase);
}
}
lcd.setCursor(5 - digitPos, 1);
} break;
// Ubah mode (sinus/segitiga/jam)
case 5: {
if (button == 0) {
button = 1;
menuState = 0;
sigGen.mode(currentMode);
}
lcd.setCursor(13 ,1);
} break;
// Kalau-kalau kita mengacaukan sesuatu
default: {
menuState = 0;
}
}
}
// Berfungsi untuk melakukan debounce tombol
// 0 = ditekan, 1 = tertekan, 2 = ditekan lama
void checkButton() {
if ((millis() - lastButtonPress) > 100) {
if (digitalRead(buttonPin) != lastButtonState) {
button = digitalRead(buttonPin);
lastButtonState = button;
lastButtonPress = millis();
}
}
}
void encChange() {
// Tergantung pada status menu Anda
// encoder akan mengubah nilai pengaturan:
//-+ frekuensi, perubahan antara register FREQ0 dan FREQ1 (atau -+ fase), On/Off, mode
// atau akan mengubah posisi kursor
unsigned char state = encoder.process();
// Arahnya searah jarum jam
if (state == DIR_CW) {
switch (menuState) {
case 1: {
if (cursorPos == 3)
cursorPos = 0;
else
cursorPos++;
} break;
case 2: {
// Di sini kita menginisialisasi dua variabel.
// newFrequency adalah nilai frekuensi setelah kita menaikkannya
// dispDigit adalah digit yang sedang kita modifikasi dan kita peroleh
// dengan sedikit trik yang rapi, menggunakan operator % bersama dengan pembagian
// Kita kemudian membandingkan variabel-variabel ini dengan nilai maksimum untuk variabel kita
// frekuensi, jika semuanya baik, lakukan perubahan
unsigned long newFrequency = frequency + power(10, digitPos);
unsigned char dispDigit =
frequency % power(10, digitPos + 1) / power(10, digitPos);
if (newFrequency <= maxFrequency && dispDigit < 9) {
frequency += power(10, digitPos);
updateDisplay = true;
}
if (freqRegister == 0) {
frequency0 = frequency;
} else if (freqRegister == 1) {
frequency1 = frequency;
}
} break;
case 4: {
// jika usePhase telah ditentukan, perubahan pada encoder akan memvariasikan fasenya
// nilai (hingga 4096)
// Implementasi yang lebih baik adalah dengan menggunakan kenaikan pi/4 atau subkelipatan dari
// pi dimana 2pi = 4096
#ifdef usePhase
unsigned long newPhase = phase + power(10, digitPos);
unsigned char dispDigit =
phase % power(10, digitPos + 1) / power(10, digitPos);
if (newPhase < maxPhase && dispDigit < 9) {
phase += power(10, digitPos);
updateDisplay = true;
}
#endif
} break;
case 5: {
if (currentMode == 2)
currentMode = 0;
else
currentMode++;
updateDisplay = true;
} break;
}
}
// Arahnya berlawanan arah jarum jam
else if (state == DIR_CCW) {
switch (menuState) {
case 1: {
if (cursorPos == 0)
cursorPos = 3;
else
cursorPos--;
} break;
case 2: {
unsigned long newFrequency = frequency + power(10, digitPos);
unsigned char dispDigit =
frequency % power(10, digitPos + 1) / power(10, digitPos);
if (newFrequency > 0 && dispDigit > 0) {
frequency -= power(10, digitPos);
updateDisplay = true;
}
if (freqRegister == 0) {
frequency0 = frequency;
} else if (freqRegister == 1) {
frequency1 = frequency;
}
} break;
case 4: {
// jika usePhase telah ditentukan, perubahan pada encoder akan memvariasikan fasenya
// nilai (hingga 4096)
// Implementasi yang lebih baik adalah dengan menggunakan kenaikan pi/4 atau subkelipatan dari
// pi dimana 2pi = 4096
#ifdef usePhase
unsigned long newPhase = phase + power(10, digitPos);
unsigned char dispDigit =
phase % power(10, digitPos + 1) / power(10, digitPos);
if (newPhase > 0 && dispDigit > 0) {
phase -= power(10, digitPos);
updateDisplay = true;
}
#endif
} break;
case 5: {
if (currentMode == 0)
currentMode = 2;
else
currentMode--;
updateDisplay = true;
} break;
}
}
}
// Berfungsi untuk menampilkan frekuensi saat ini di pojok kiri atas
void displayFrequency() {
unsigned long frequencyToDisplay = frequency;
lcd.setCursor(0, 0);
lcd.print("f=");
for (int i = 7; i >= 0; i--) {
unsigned char dispDigit = frequencyToDisplay / power(10, i);
lcd.print(dispDigit);
frequencyToDisplay -= dispDigit * power(10, i);
}
lcd.print("Hz");
}
// Berfungsi untuk menampilkan status daya (ON/OFF) di pojok kanan atas
void displayPower() {
lcd.setCursor(13, 0);
lcd.print(powerState[currentPowerState]);
}
// Berfungsi untuk menampilkan mode di pojok kanan bawah
void displayMode() {
lcd.setCursor(13, 1);
lcd.print(mode[currentMode]);
}
// Berfungsi untuk menampilkan mode di pojok kiri bawah
// Hanya digunakan jika Anda mengaktifkan pengaturan PHASE dan bukan register FREQ
void displayPhase() {
unsigned int phaseToDisplay = phase;
lcd.setCursor(0, 1);
lcd.write(0);
lcd.print("=");
for (int i = 3; i >= 0; i--) {
unsigned int dispDigit = phaseToDisplay / power(10, i);
lcd.print(dispDigit);
phaseToDisplay -= dispDigit * power(10, i);
}
}
// Berfungsi untuk menampilkan register FREQ (0 atau 1) di kiri bawah
// sudut
void displayFreqRegister() {
lcd.setCursor(0, 1);
lcd.print("FREQ");
lcd.print(freqRegister);
}
unsigned long power(int a, int b) {
unsigned long res;
if (b == 0) {
res = 1;
} else {
res = a;
for (int i = 0; i < (b - 1); i++) {
res *= a;
}
}
return res;
}