TeachMeSoft: Arduino
Tampilkan postingan dengan label Arduino. Tampilkan semua postingan
Tampilkan postingan dengan label Arduino. Tampilkan semua postingan

Bit Mask



Bit Mask


Bit Mask (Topeng bit) digunakan untuk mengakses bit tertentu dalam satu byte data. Ini sering berguna sebagai metode iterasi, misalnya saat mengirim byte data secara seri, keluar satu pin. Dalam contoh ini pin harus mengubah statusnya dari tinggi ke rendah untuk setiap bit dalam byte yang dikirimkan. Ini dilakukan dengan menggunakan apa yang dikenal sebagai operasi bitwise dan bit mask.

Operasi bitwise melakukan fungsi logis yang berpengaruh pada level bit. Operasi bitwise standar termasuk AND (&) OR (|) Shift Kiri (<<) dan Shift Kanan (>>).

Operator AND (&) akan menghasilkan 1 pada setiap posisi bit di mana kedua nilai input adalah 1. Misalnya:
x:  10001101
    y:  01010111
x & y:  00000101


Operator OR (|) (juga dikenal sebagai Inclusive Or) akan menghasilkan 1 pada setiap posisi bit di mana nilai inputnya adalah 1. Misalnya:

x :  10001101
    y :  01010111
x | y :  11011111



Operator Left Shift (<<) akan menggeser nilai ke kiri berapa kali yang ditentukan. Sebagai contoh:

y = 1010
        x = y << 1
yields: x = 0100


Semua bit dalam byte digeser satu posisi ke kiri dan bit di ujung kiri turun.

Operator Right Shift (>>) bekerja secara identik ke shift kiri kecuali bahwa itu menggeser nilai ke kanan berapa kali yang ditentukan Misalnya:

y = 1010
            x = y >> 1
yields : x = 0101


Semua bit dalam byte digeser satu posisi ke kanan dan bit di ujung kanan turun.

Sebagai contoh praktis, mari kita ambil nilai 170, biner 10101010. Untuk mengeluarkan nilai ini dari pin 7 kode mungkin terlihat sebagai berikut:

byte transmit = 7; //define our transmit pin
byte data = 170; //value to transmit, binary 10101010
byte mask = 1; //our bitmask
byte bitDelay = 100;

void setup()
{
   pinMode(transmit,OUTPUT);
}

void loop()
{
  for (mask = 00000001; mask>0; mask <<= 1) { //iterate through bit mask
    if (data & mask){ // if bitwise AND resolves to true
      digitalWrite(transmit,HIGH); // send 1
    }
    else{ //if bitwise and resolves to false
      digitalWrite(transmit,LOW); // send 0
    }
    delayMicroseconds(bitDelay); //delay
  }
}


Di sini kita menggunakan loop UNTUK untuk beralih melalui nilai bit mask, menggeser nilai satu posisi yang tersisa setiap kali melalui loop. Dalam contoh ini kita menggunakan operator << = yang persis seperti operator << kecuali bahwa itu memadatkan pernyataan


00000001
& 10101010
  ________
  00000000


Dan pin output kami disetel ke 0. Kedua kalinya ia melingkarkan mask = 00000010, jadi operasi kami terlihat seperti:


00000010
& 10101010
  ________
  00000010


Dan pin output kami disetel ke 1. Loop akan terus beralih melalui setiap bit pada mask sampai 1 digeser ke kiri dari ujung 8 bit dan mask kami = 0. Kemudian semua 8 bit telah dikirim dan loop kita keluar.


SUMBER

  • [1] https://www.arduino.cc/en/Tutorial/BitMask


    Capacitance Meter

    Capacitance Meter

    Capacitance Meter and RC Time Constants (Meteran Kapasitansi dan Konstanta Waktu RC)



    Ikhtisar: Sebuah resistor akan mengisi kapasitor dalam detik TC, di mana
    • TC = R * C
    • TC = konstanta waktu dalam detik
    • R = resistensi dalam ohm
    • C = kapasitansi dalam farad (1 mikrofarad [ufd] = .0000001 farad = 10 ^ -6 farad)

    Tegangan pada Konstan 1 Waktu sama dengan 63,2% dari tegangan pengisian.

    Contoh: 1 megohm * 1 mikrofarad = 1 detik
    Contoh: 10k ohm * 100 mikrofarad = 1 detik




    Experimental Setup (Pengaturan eksperimen)
    Sketsa ini berfungsi karena pin Arduino dapat berada di salah satu dari dua keadaan, yang secara elektrik sangat berbeda.
    • Status Input (diatur dengan pinMode (pin, INPUT);)
      • Impedansi Tinggi (resistansi) - Membuat sangat sedikit permintaan pada rangkaian yang dijadikan sampel
      • Bagus untuk membaca sensor tetapi tidak menyalakan LED
    • Status Output (diatur dengan pinMode (pin, OUTPUT);)
      • Impedansi rendah - Dapat menyediakan sumber 40 mA (tegangan positif), atau tenggelam (tegangan negatif)
      • Bagus untuk penerangan LED, menggerakkan sirkuit lain - tidak berguna untuk membaca sensor.
    Selain itu pin dapat TINGGI (+5 volt), untuk mengisi kapasitor; atau LOW (ground) untuk melepaskan kapasitor



    Alogrithm for capacitance meter sketch (Alogrithm untuk sketsa meter kapasitansi)
    • Setel pin pelepasan ke INPUT (sehingga tidak bisa melepaskan kapasitor)
    • Catat waktu mulai dengan millis ()
    • Setel pin biaya ke OUTPUT dan buat TINGGI
    • Periksa tegangan berulang kali dalam satu lingkaran hingga mencapai 63,2% dari total tegangan.
    • Setelah tutup diisi, kurangi waktu saat ini dari waktu mulai untuk mencari tahu berapa lama kapasitor mengambil daya.
    • Membagi Waktu dalam hitungan detik dengan Perlawanan pengisian dalam ohm untuk menemukan Kapasitansi.
    • Laporkan nilai dengan serial.print
    • Lepaskan kapasitor. Untuk melakukan ini:
      • Setel pin biaya ke Input
      • Atur pin debit ke OUTPUT dan buat itu RENDAH
      • Baca voltase untuk memastikan kapasitor kosong sepenuhnya
    • Ulangi dan lakukan lagi


    Sketsa Arduino
    /*  RCTiming_capacitance_meter
     *   Paul Badger 2008
     *  Demonstrates use of RC time constants to measure the value of a capacitor
     *
     * Theory   A capcitor will charge, through a resistor, in one time constant, defined as T seconds where
     *    TC = R * C
     *
     *    TC = time constant period in seconds
     *    R = resistance in ohms
     *    C = capacitance in farads (1 microfarad (ufd) = .0000001 farad = 10^-6 farads )
     *
     *    The capacitor's voltage at one time constant is defined as 63.2% of the charging voltage.
     *
     *  Hardware setup:
     *  Test Capacitor between common point and ground (positive side of an electrolytic capacitor  to common)
     *  Test Resistor between chargePin and common point
     *  220 ohm resistor between dischargePin and common point
     *  Wire between common point and analogPin (A/D input)
     */

    #define analogPin      0          // analog pin for measuring capacitor voltage
    #define chargePin      13         // pin to charge the capacitor - connected to one end of the charging resistor
    #define dischargePin   11         // pin to discharge the capacitor
    #define resistorValue  10000.0F   // change this to whatever resistor value you are using
                                      // F formatter tells compliler it's a floating point value

    unsigned long startTime;
    unsigned long elapsedTime;
    float microFarads;                // floating point variable to preserve precision, make calculations
    float nanoFarads;

    void setup(){
      pinMode(chargePin, OUTPUT);     // set chargePin to output
      digitalWrite(chargePin, LOW);  

      Serial.begin(9600);             // initialize serial transmission for debugging
    }

    void loop(){
      digitalWrite(chargePin, HIGH);  // set chargePin HIGH and capacitor charging
      startTime = millis();

      while(analogRead(analogPin) < 648){       // 647 is 63.2% of 1023, which corresponds to full-scale voltage
      }

      elapsedTime= millis() - startTime;
     // convert milliseconds to seconds ( 10^-3 ) and Farads to microFarads ( 10^6 ),  net 10^3 (1000)  
      microFarads = ((float)elapsedTime / resistorValue) * 1000;  
      Serial.print(elapsedTime);       // print the value to serial port
      Serial.print(" mS    ");         // print units and carriage return


      if (microFarads > 1){
        Serial.print((long)microFarads);       // print the value to serial port
        Serial.println(" microFarads");         // print units and carriage return
      }
      else
      {
        // if value is smaller than one microFarad, convert to nanoFarads (10^-9 Farad).
        // This is  a workaround because Serial.print will not print floats

        nanoFarads = microFarads * 1000.0;      // multiply by 1000 to convert to nanoFarads (10^-9 Farads)
        Serial.print((long)nanoFarads);         // print the value to serial port
        Serial.println(" nanoFarads");          // print units and carriage return
      }

      /* dicharge the capacitor  */
      digitalWrite(chargePin, LOW);             // set charge pin to  LOW
      pinMode(dischargePin, OUTPUT);            // set discharge pin to output
      digitalWrite(dischargePin, LOW);          // set discharge pin LOW
      while(analogRead(analogPin) > 0){         // wait until capacitor is completely discharged
      }

      pinMode(dischargePin, INPUT);            // set discharge pin back to input
      }


    Further things to try (Hal-hal lebih lanjut untuk dicoba)
    • Ganti resistor yang lebih besar jika waktu pengisian terlalu pendek, resistor lebih kecil jika waktu pengisian terlalu lama.
    • Ukur kapasitor secara paralel dan seri, periksa untuk melihat apakah pengamatan Anda sesuai dengan teori elektronik
    • Rata-rata bersama sekelompok pembacaan untuk akurasi lebih
    • Tukar beberapa resistor pengisian daya pada pin yang berbeda untuk membuat kapasitansi meter "rentang otomatis"
    • Memodifikasi sketsa agar resistor pengisian juga mengeluarkan kapasitor. Perhatikan bahwa nilai yang dilaporkan tidak dua kali lipat dari nilai yang dilaporkan saat hanya mengisi daya kapasitor ini. Jelaskan ini. Petunjuk - pelajari kurva perubahan pada grafik.

    SUMBER

    • [1] https://www.arduino.cc/en/Tutorial/CapacitanceMeter


      Serial to Serial Conversion







      Serial to Parallel Shifting-Out dengan 74HC595


      Shifting Out (Menggeser Keluar) & chip 595
      Terkadang Anda mungkin kehabisan pin pada papan Arduino Anda dan perlu memperpanjangnya dengan register geser. Contoh ini didasarkan pada 74HC595. Lembar data merujuk ke 74HC595 sebagai "8-bit register geser serial, paralel atau keluar dengan kait keluaran; 3-negara." Dengan kata lain, Anda dapat menggunakannya untuk mengontrol 8 output sekaligus sekaligus hanya mengambil beberapa pin pada mikrokontroler Anda. Anda dapat menautkan beberapa register secara bersamaan untuk lebih meningkatkan hasil Anda. (Pengguna juga mungkin ingin mencari chip driver lain dengan "595" atau "596" di nomor bagian mereka, ada banyak. STP16C596 misalnya akan menggerakkan 16 LED dan menghilangkan resistor seri dengan built-in sumber arus konstan.)

      Cara ini semua bekerja adalah melalui sesuatu yang disebut "komunikasi serial sinkron," yaitu Anda dapat menggerakkan satu pin ke atas dan ke bawah dengan demikian mengkomunikasikan byte data ke register sedikit demi sedikit. Ini dengan menggandakan pin kedua, pin jam, yang Anda gambarkan di antara bit. Ini berbeda dengan menggunakan "komunikasi serial asinkron" dari fungsi Serial.begin () yang bergantung pada pengirim dan penerima untuk diatur secara independen ke kecepatan data yang ditentukan yang disepakati. Setelah seluruh byte ditransmisikan ke register, pesan TINGGI atau RENDAH yang disimpan dalam setiap bit dibagikan ke masing-masing pin output individual. Ini adalah bagian "output paralel", membuat semua pin melakukan apa yang Anda inginkan sekaligus.

      Bagian "keluaran serial" dari komponen ini berasal dari pin ekstra yang dapat meneruskan informasi serial yang diterima dari mikrokontroler keluar lagi tidak berubah. Ini berarti Anda dapat mengirimkan 16 bit berturut-turut (2 byte) dan 8 bit pertama akan mengalir melalui register pertama ke register kedua dan diekspresikan di sana. Anda dapat belajar melakukannya dari contoh kedua.

      "3 status" mengacu pada fakta bahwa Anda dapat mengatur pin output sebagai tinggi, rendah atau " impedansi tinggi ." Tidak seperti status TINGGI dan RENDAH, Anda tidak dapat mengatur pin ke status impedansinya yang tinggi secara individual. Anda hanya dapat mengatur keseluruhan chip. Ini adalah hal yang cukup khusus untuk dilakukan - Pikirkan array LED yang mungkin perlu dikendalikan oleh mikrokontroler yang benar-benar berbeda tergantung pada pengaturan mode khusus yang dibangun ke dalam proyek Anda. Tidak ada contoh mengambil keuntungan dari fitur ini dan Anda tidak akan perlu khawatir tentang mendapatkan chip yang memilikinya.

      Berikut adalah tabel yang menjelaskan pin-out yang diadaptasi dari lembar data Phillip .

      PIN 1-7, 15Q0 "Q7Pin Output
      PIN 8GNDTanah, Vss
      PIN 9Q7 "Serial Out
      PIN 10PAKMaster Reclear, aktif rendah
      PIN 11SH_CPGeser pin pin register
      PIN 12ST_CPPin jam register penyimpanan (pin kait)
      PIN 13OEOutput aktif, aktif rendah
      PIN 14DSInput data serial
      PIN 16VccTegangan suplai positif


      Contoh 1


      Langkah pertama adalah memperpanjang Arduino Anda dengan satu register geser.


      Sirkuit
      1. Hidupkan
      Buat koneksi berikut:
      • GND (pin 8) ke ground,
      • Vcc (pin 16) ke 5V
      • OE (pin 13) ke ground
      • MR (pin 10) ke 5V

      Pengaturan ini membuat semua pin output aktif dan dapat dialamatkan setiap saat. Satu kekurangan dari pengaturan ini adalah bahwa Anda berakhir dengan lampu menyala ke keadaan terakhir mereka atau sesuatu yang sewenang-wenang setiap kali Anda pertama kali menyalakan sirkuit sebelum program mulai berjalan. Anda dapat menyiasatinya dengan mengendalikan pin MR dan OE dari papan Arduino Anda juga, tetapi cara ini akan berhasil dan membuat Anda memiliki lebih banyak pin yang terbuka.



      2. Hubungkan ke Arduino
      • DS (pin 14) ke Ardunio DigitalPin 11 (kabel biru)
      • SH_CP (pin 11) ke ke Ardunio DigitalPin 12 (kabel kuning)
      • ST_CP (pin 12) ke Ardunio DigitalPin 8 (kabel hijau)

      Mulai sekarang mereka akan disebut sebagai dataPin, clockPin dan latchPin masing-masing. Perhatikan kapasitor 0,1 "pada latchPin, jika Anda memiliki sedikit kedipan ketika pin kait Anda dapat menggunakan kapasitor untuk meratakannya.






      3. Tambahkan 8 LED .
      Dalam hal ini Anda harus menghubungkan katoda (pin pendek) dari setiap LED ke landasan bersama, dan anoda (pin panjang) dari masing-masing LED ke pin output register geser masing-masing. Menggunakan register geser untuk memasok daya seperti ini disebut sumber arus. Beberapa register shift tidak dapat sumber arus, mereka hanya dapat melakukan apa yang disebut tenggelam saat ini. Jika Anda memilikinya, itu artinya Anda harus membalik arah LED , mengarahkan anoda langsung ke daya dan katoda (pin ground) ke output register geser. Anda harus memeriksa lembar data spesifik Anda jika Anda tidak menggunakan chip seri 595. Jangan lupa untuk menambahkan resistor 470-ohm secara seri untuk melindungi LED dari kelebihan beban.


      Diagram Sirkuit



      Kode
      Berikut adalah tiga contoh kode. Yang pertama hanyalah beberapa kode "hello world" yang hanya menampilkan nilai byte dari 0 hingga 255. Program kedua menyalakan satu LED pada satu waktu. Siklus ketiga melalui array.

      tabel logika
      595 Diagram Waktu
      tabel logika
      595 Tabel Logika
      Kode didasarkan pada dua bagian informasi dalam lembar data: diagram waktu dan tabel logika. Tabel logika adalah apa yang memberi tahu Anda bahwa pada dasarnya segala sesuatu yang penting terjadi pada ketukan. Ketika clockPin beralih dari rendah ke tinggi, register geser membaca status pin data. Saat data dipindahkan, data disimpan dalam register memori internal. Ketika latchPin beralih dari rendah ke tinggi, data yang dikirim dipindahkan dari register geser, register memori yang disebutkan sebelumnya ke dalam pin output, menyalakan LED .

      Contoh Kode 1.1 Sampel Kode Hello World
      Contoh Kode 1.2 Satu per Satu
      Contoh Kode 1.3 Menggunakan array

      Contoh 2






      Sirkuit
      1. Tambahkan register shift kedua
      Mulai dari contoh sebelumnya, Anda harus meletakkan register shift kedua di papan tulis. Itu harus memiliki mengarah yang sama ke kekuasaan dan tanah.





      2. Hubungkan 2 register
      Dua koneksi ini hanya memperpanjang sinyal jam dan kait yang sama dari Arduino ke register shift kedua (kabel kuning dan hijau). Kabel biru bergerak dari pin keluar seri (pin 9) dari register shift pertama ke input data serial (pin 14) dari register kedua.


      3. Tambahkan set LED kedua.
      Dalam hal ini saya menambahkan yang hijau jadi ketika membaca kode jelas byte mana yang akan pergi ke set LED


      Diagram Sirkuit



      Kode

      Di sini sekali lagi ada tiga contoh kode. Jika Anda penasaran, Anda mungkin ingin mencoba sampel dari contoh pertama dengan rangkaian ini diatur hanya untuk melihat apa yang terjadi.

      Contoh Kode 2.1 Penghitung Biner Ganda.
      Hanya ada satu baris kode tambahan dibandingkan dengan sampel kode pertama dari Contoh 1. Ia mengirimkan byte kedua. Ini memaksa register geser pertama, yang langsung terhubung ke Arduino, untuk melewatkan byte pertama yang dikirim ke register kedua, menyalakan LED hijau . Byte kedua akan muncul di LED merah .

      Contoh Kode 2.2 2 Byte One By One
      Membandingkan kode ini dengan kode yang sama dari Contoh 1 Anda melihat bahwa sedikit lebih banyak harus berubah. Fungsi blinkAll () telah diubah menjadi fungsi blinkAll_2Bytes () untuk mencerminkan fakta bahwa sekarang ada 16 LED untuk dikendalikan. Selain itu, dalam versi 1, denyut latchPin terletak di dalam subfungsi lightShiftPinA dan lightShiftPinB (). Di sini mereka perlu dipindahkan kembali ke loop utama untuk mengakomodasi perlu menjalankan setiap subfungsi dua kali berturut-turut, sekali untuk LED hijau dan sekali untuk yang merah.

      Contoh Kode 2.3 - Dual Defined Array
      Seperti sampel 2.2, sampel 2.3 juga memanfaatkan fungsi blinkAll_2bytes () yang baru. Perbedaan besar 2.3 dari sampel 1.3 hanya bahwa alih-alih hanya variabel tunggal yang disebut "data" dan array tunggal yang disebut "dataArray" Anda harus memiliki dataRED, dataGREEN, dataArrayRED, dataArrayGREEN yang ditentukan di depan. Ini berarti garis itu

      data = dataArray[j];

      Menjadi
      dataRED = dataArrayRED[j];
      dataGREEN = dataArrayGREEN[j];

      dan
      shiftOut(dataPin, clockPin, data);

      Menjadi
      shiftOut(dataPin, clockPin, dataGREEN);
      shiftOut(dataPin, clockPin, dataRED);


      SUMBER

      • [1] https://www.arduino.cc/en/Tutorial/ShiftOut

      Parallel to Serial Conversion



      Paralel ke Serial Shifting-In dengan CD4021BE


      Shifting In (Menggeser Masuk) & the CD4021B
      Terkadang Anda akan membutuhkan lebih banyak input digital daripada pin di Arduino Anda. Menggunakan register geser paralel ke serial, Anda dapat mengumpulkan informasi dari 8 switch atau lebih saat hanya menggunakan 3 pin pada Arduino Anda.

      Contoh register paralel ke seri adalah CD4021B , kadang-kadang disebut sebagai 8-Stage Static Shift Register. Ini berarti Anda dapat membaca status hingga 8 input digital yang terpasang pada register sekaligus. Ini disebut Asynchronous Parallel Input. "Input" karena Anda mengumpulkan informasi. "Paralel" karena semuanya sekaligus, seperti mendengar kabel musik. "Asinkron" karena CD4021B melakukan semua pengumpulan data ini dengan langkahnya sendiri tanpa berkoordinasi dengan Arduino.

      8 input diterjemahkan ke dalam serangkaian pulsa TINGGI dan RENDAH pada pin seri-keluar dari register geser. Pin ini harus dihubungkan ke pin input pada Arduino Board Anda, disebut sebagai pin data . Transfer informasi pada pin data disebut "Output Serial Sinkron" karena register geser menunggu untuk mengirimkan urutan data linier ke Arduino hingga Arduino memintanya. Synchronous Serial communication, baik input maupun output, sangat bergantung pada apa yang disebut sebagai pin jam. Pin jam adalah metronom percakapan antara register geser dan Arduino, inilah yang menjaga kedua sistem tetap sinkron. Setiap kali Arduino mengubah pin jam dari RENDAH ke TINGGI register geser mengubah status pin Output Serial, yang menunjukkan nilai sakelar berikutnya.

      Pin ketiga yang terpasang pada Arduino adalah pin "Parallel to Serial Control". Ini disebut sebagai pin kait . Ketika pin kait tinggi, register geser mendengarkan 8 input paralelnya. Ketika pin kait RENDAH, ia mendengarkan pin jam dan meneruskan informasi secara seri. Itu berarti setiap kali latch pin bertransisi dari TINGGI ke RENDAH register geser akan mulai menyampaikan informasi sakelar terbarunya.

      Kode pseudo untuk mengoordinasikan ini semua terlihat seperti ini:
      1. Pastikan register memiliki informasi terbaru dari input paralelnya (yaitu bahwa pin kait HIGH)
      2. Katakan pada register, Arduino siap untuk mendapatkan informasi secara serial (latch pin LOW)
      3. Untuk setiap input yang akan dibaca, nyalakan clockPin dan periksa untuk melihat apakah pin data LOW atau HIGH
      4. Ini adalah diagram dasar.

      Ini adalah diagram dasar.

                 _______  
      switch ->  |     |
      switch ->  |  C  |
      switch ->  |  D  |
      switch ->  |  4  | -> Serial Data to Arduino
      switch ->  |  0  | 
      switch ->  |  2  | 
      switch ->  |  1  | <- Clock Data from Arduino
      switch ->  |_____| <- Latch Data from Arduino
      
      
      Ada informasi lebih lanjut tentang pengalihan dalam tutorial ShiftOut .

      Sebelum Anda mulai memasang papan Anda di sini adalah diagram pin CD4021 dari Texas Instruments Datasheet



      PIN 1,4-7, 13-15P1, P8 (Pin 0-7)Input Paralel
      PIN 2, 12, 3Q6, Q7, Q8Pin Output Serial dari berbagai langkah dalam urutan. Q7 adalah pulsa di belakang Q8 dan Q6 adalah pulsa di belakang Q7. Q8 adalah satu-satunya yang digunakan dalam contoh ini.
      PIN 8VssGND
      PIN 9P / SCKontrol Paralel / Serial (pin kait)
      PIN 10JAMGeser pin pin register
      PIN 11SERIAL-INInput data serial
      PIN 16VDDTegangan pasokan DC



      Contoh 1: One Shift Register


      Langkah pertama adalah memperpanjang Arduino Anda dengan satu register geser.


      Sirkuit
      1. Koneksi Daya
      Buat koneksi berikut:
      • GND (pin 8) ke ground,
      • VDD (pin 16) ke 5V



      2. Hubungkan ke Arduino
      • Q8 (pin 3) ke Ardunio DigitalPin 9 (kabel biru)
      • CLOCK (pin 10) ke ke Ardunio DigitalPin 7 (kabel kuning)
      • P / SC (pin 9) ke Ardunio DigitalPin 8 (kabel hijau)

      Mulai sekarang mereka akan disebut sebagai dataPin, clockPin dan latchPin masing-masing.



      3. Tambahkan 8 Switch


      Diagram



      Kode
      Contoh Kode 1.1 Hello World
      Contoh Kode 1.2 Apa Yang Ditekan?
      Contoh Kode 1.3 Kombinasi Tombol Periksa
      Contoh Kode 1.4 Apakah ditekan? (subfungsi)


      Contoh 2: Multiple Shift Register


      Dalam contoh ini Anda akan menambahkan register geser kedua, menggandakan jumlah pin input sementara masih menggunakan jumlah pin yang sama pada Arduino.

      Jika menambah Arduino Anda dengan tambahan 8 input digital tidak akan cukup untuk proyek Anda, Anda dapat memiliki CD4021 kedua meneruskan informasinya ke CD4021 lain yang akan mengalirkan semua 16 bit informasi ke Arduino. Jika Anda tahu Anda perlu menggunakan beberapa register geser seperti ini, periksa apakah register shift yang Anda beli dapat menangani Input Serial Sinkron serta kemampuan Output Serial Sinkron standar. Synchronous Serial Input adalah fitur yang memungkinkan register shift pertama menerima dan mengirimkan output serial dari yang kedua. Contoh di bawah ini menjelaskan cara menggunakan sistem ini. Dengan alasan tertentu, Anda dapat terus memperpanjang rantai daisy register geser ini hingga Anda memiliki semua input yang Anda butuhkan.



      1. Tambahkan register shift kedua.



      2. Hubungkan 2 register.
      Dua koneksi ini hanya memperpanjang sinyal jam dan kait yang sama dari Arduino ke register shift kedua (kabel kuning dan hijau). Kabel biru bergerak dari pin keluar seri (pin 9) dari register shift pertama ke input data serial (pin 14) dari register kedua.



      3. Tambahkan satu set Switch yang kedua.
      Perhatikan bahwa ada satu saklar sesaat dan sisanya adalah sakelar sakelar. Ini karena contoh kode akan menggunakan sakelar yang terpasang pada register shift kedua sebagai pengaturan, seperti file preferensi, bukan sebagai pemicu acara. Saklar sesaat akan memberi tahu mikrokontroler bahwa sakelar pengaturan sedang diubah.



      Diagram


      Kode
      Contoh Kode 2.1 Hello World
      Contoh Kode 2.2 Menggunakan byte kedua untuk pengaturan, Cetak semua
      Sampel Kode 2.3 Menggunakan byte kedua untuk pengaturan, Mencetak hanya (menggunakan subfungsi)


      SUMBER

      • [1] https://www.arduino.cc/en/Tutorial/ShiftIn


        Dasar-dasar FPGA HDL



        Array Gerbang Programmable Lapangan, singkatnya, FPGA adalah cara yang relatif lama untuk membuat perangkat keras khusus yang menghilangkan biaya yang terkait dengan pengecoran silikon. Sayangnya sebagian besar kompleksitas desain chip masih ada dan ini adalah alasan mengapa kebanyakan orang lebih suka menggunakan chip rak, sering menerima keterbatasan mereka, daripada mengambil tantangan untuk memiliki desain yang dioptimalkan, efisien dengan perangkat keras yang mereka butuhkan. .

        Seperti yang terjadi pada Perangkat Lunak, di mana ada banyak perpustakaan yang dapat Anda mulai, juga untuk FPGA ada "perpustakaan" yang disebut blok IP, namun ini biasanya cukup mahal dan tidak memiliki antarmuka "plug and play" standar yang menyebabkan sakit kepala saat mengintegrasikan semuanya dalam suatu sistem. Apa yang Arduino coba lakukan dengan memperkenalkan FPGA dalam lini produknya adalah untuk mengambil keuntungan dari fleksibilitas perangkat keras yang dapat diprogram khususnya untuk menyediakan serangkaian periferal yang dapat diperpanjang untuk mikrokontroler yang menghilangkan sebagian besar kerumitan. Tentu saja untuk mencapai ini, perlu menerapkan beberapa batasan dan menetapkan cara standar untuk menghubungkan blok sehingga dapat dilakukan secara otomatis.

        Langkah pertama adalah mendefinisikan satu set antarmuka standar yang harus benar-benar menanggapi seperangkat aturan yang diberikan tetapi sebelum menyelami ini, penting untuk menentukan antarmuka apa yang mungkin kita butuhkan. Karena kita berinteraksi dengan mikrokontroler, port pertama yang perlu kita tentukan adalah bus untuk menghubungkan prosesor dengan periferal. Bus semacam itu setidaknya harus ada dalam citarasa master dan slave di mana sinyalnya sama tetapi dengan arah terbalik. Untuk beberapa detail tambahan tentang bus dan arsitektur master / slave silakan periksa dokumen ini .

        Antarmuka kedua, yang penting tetapi tidak dapat distandarisasi adalah sinyal input / output yang terhubung ke dunia eksternal. Di sini kita tidak dapat mendefinisikan standar karena setiap blok akan menyediakan set sinyal sendiri namun kita hanya dapat menggabungkan satu set sinyal dalam kelompok yang kita sebut saluran.

        Akhirnya ada antarmuka kelas tiga yang mungkin berguna yang membawa data streaming. Dalam hal ini kami ingin mentransfer aliran data yang berkelanjutan tetapi juga ingin dapat menghentikan sementara aliran jika blok penerima tidak dapat memprosesnya, maka bersama dengan data kami juga memerlukan beberapa jenis sinyal kontrol aliran yang cukup mirip terjadi pada UART.

        Karena kami ingin menstandarisasi sedikit juga pada keterbacaan kami juga ingin menetapkan beberapa konvensi pengkodean. Di sini tentu saja ada banyak agama yang berbeda yang mengatur spasi / tab, notasi dan sebagainya, jadi kami memilih yang kami sukai ...

        Berbicara tentang agama, pada akhirnya kami juga berbicara tentang bahasa ... kami lebih suka (Sistem) Verilog daripada VHDL dan sebagian besar blok IP kami diberi kode. Alasan pilihan kami adalah bahwa Verilog secara umum lebih mirip dengan C dan juga memungkinkan konstruksi yang sangat bagus yang memfasilitasi pembuatan blok parametrik.

        Daftar Isi

        • Konvensi Pengkodean
        • Prototipe antarmuka
          • Lightweight Bus (Bus Ringan)
          • Bus Pipelined
          • Antarmuka streaming
        • Struktur modul Verilog (Sistem)
        • Paralelisme dan diutamakan
        • Contoh dunia nyata
        • Contoh sederhana lainnya

        Konvensi Pengkodean


        Kami menggunakan awalan di depan setiap entitas yang dideklarasikan sehingga mengidentifikasi jenisnya, nama variabel sepenuhnya huruf besar dan beberapa kata dipisahkan oleh garis bawah. Khususnya:


          AwalanDeskripsi
          wKawat, untuk semua sinyal kombinatorial, misalnya wDATA. Biasanya ditentukan dengan arahan kawat
          rReg, untuk semua sinyal berurutan, misalnya rSHIFTER. Biasanya ditentukan dengan arahan reg
          iInput, untuk semua sinyal input dalam deklarasi modul, misalnya iCLK. Biasanya ditentukan dengan arahan input
          oOutput, untuk semua sinyal output dalam deklarasi modul, misalnya OREAD. Biasanya ditentukan dengan arahan keluaran
          bDua arah, untuk semua sinyal keluar dalam deklarasi modul, misalnya bSDA. Biasanya didefinisikan dengan InOut direktif
          pParameter, untuk semua parameter yang dapat digunakan untuk parametrize blok, misalnya pCHANNELS. Biasanya didefinisikan dengan arahan param
          cKonstan, untuk semua definisi yang konstan atau merupakan nilai turunan dan tidak dapat langsung digunakan untuk menentukan parameter blok. Misalnya cCHANNEL_BITS. Biasanya didefinisikan dengan arahan localparam
          eDihitung, untuk semua kemungkinan nilai konstan yang digunakan oleh satu atau lebih sinyal atau register. Misalnya keadaan mesin negara dapat didefinisikan sebagai eSTATE. Biasanya ditentukan dengan arahan enum

          • Kami lebih suka spasi daripada tab! Alasannya adalah bahwa terlepas dari kode ukuran tab selalu terlihat bagus.
          • Lekukan diatur ke dua spasi.
          • Blok pernyataan bersyarat harus selalu memiliki konstruksi awal / akhir bahkan jika mereka memiliki satu pernyataan di dalamnya dan mulai / berakhir harus berada pada baris yang sama dari if / else
          • Sinyal milik kelompok yang sama harus berbagi awalan yang sama


          Prototipe antarmuka


          Lightweight Bus (Bus Ringan)
          Bus untuk menyambungkan periferal. Dengan konvensi, bus data adalah 32 bit sedangkan bus alamat lebar variabel, berdasarkan jumlah register yang diekspos. Bus membutuhkan serangkaian sinyal berikut:


          SinyalDesciptionLebarDeskripsi
          MasterSlave
          ALAMATHAIsayavar.Mendaftar alamat, menentukan lebar
          BACAHAIsaya1Baca strobo
          READ_DATAsayaHAI32Data sedang dibaca
          MENULISHAIsaya1Menulis strobo
          WRITE_DATAHAIsaya32Data untuk ditulis di alamat yang diberikan
          BYTE_ENABLEHAIsaya4Sinyal opsional untuk menandai byte kata 32 bit mana yang sebenarnya akan ditulis
          WAIT_REQUESTsayaHAI1Sinyal opsional untuk menandai periferal sedang sibuk. Membaca dan menulis strobe akan dianggap sah hanya jika sinyal ini tidak ditegaskan.
          Dengan konvensi dalam siklus tulis, ADDRESS dan WRITE_DATA terkunci dalam siklus clock yang sama dari strobo WRITE. Sebaliknya, dalam siklus baca READ_DATA disajikan oleh perangkat pada siklus segera setelah strobo READ, yang juga akan menunjukkan ADDRESS sedang dibaca.


          Pipelined Bus
          Bus untuk menghubungkan blok kompleks yang dapat menangani lebih dari satu perintah pada waktu dan merespons dalam waktu variabel untuk permintaan. Bus ini memperluas bus Ringan dengan menambahkan sinyal berikut:

          Perilaku ini juga disebut sebagai latensi pembacaan 1 jam dan pada dasarnya berarti bahwa sementara periferal masih dapat memiliki jumlah jam variabel untuk merespons operasi BACA atau MENULIS menggunakan sinyal WAIT_REQUEST opsional, ini akan mengunci master mencegahnya melakukan operasi lain. Dengan cara ini dapat dianggap mirip dengan menggunakan loop sibuk dalam pemrograman versus penundaan yang menghasilkan OS untuk melakukan multitasking.


          SinyalDescriptionLebarDeskripsi
          MasterSlave
          BURST_COUNT0Ivar.Jumlah operasi berurutan yang harus dilakukan
          READ_DATAVALIDI01Periferal menggunakan sinyal ini untuk menandai ketika data disediakan untuk dikuasai. Dapat ditegaskan dengan penundaan apa pun dan tidak ada jaminan tentang keberlangsungan. Operasi baca dengan ukuran burst 4 akan menyatakan 4 kali READ_DATAVALID per setiap strobo BACA
          Keuntungan utama dari pendekatan ini adalah bahwa master dapat berkomunikasi dengan budak maksud membaca atau menulis beberapa data untuk setiap transaksi. Baik untuk membaca maupun menulis, sebenarnya sinyal BURST_COUNT memberi tahu perangkat berapa lama transaksi akan berlangsung.

          Budak akan menyatakan WAIT_REQUEST hingga siap menerima operasi. Dalam hal penulisan, BURST_COUNT dan ADDRESS disampel hanya pada strobo pertama, setelah itu periferal akan mengharapkan strobo WRITE ditegaskan untuk jumlah kata yang diminta dan secara otomatis akan menambah alamat. Untuk operasi baca, satu strobo READ, dilakukan ketika WAIT_REQUEST tidak ditegaskan, akan memberi tahu periferal untuk membaca BURST_COUNT kata yang akan dikembalikan dengan menyatakan READ_DATAVALID untuk jumlah kata yang diminta. Setelah operasi baca telah dimulai, tergantung pada periferal untuk menerima atau tidak lebih banyak operasi, tetapi secara umum seharusnya dimungkinkan untuk memiliki setidaknya dua operasi bersamaan untuk mendapat manfaat dari Pipelined Bus.


          Antarmuka streaming
          Segera akan datang



          Struktur modul Verilog (Sistem)


          Sebuah SystemVerilog modul deklarasi dapat dilakukan dengan beberapa cara tapi yang kita kebanyakan lebih suka adalah bentuk di mana Anda dapat menggunakan parameter sehingga input blok dapat disesuaikan pada waktu kompilasi. Ini akan terlihat seperti:

          module COUNTER #(
            pWIDTH=8
          ) (
            input                   iCLK,
            input                   iRESET,
            output reg [pWIDTH-1:0] oCOUNTER
          );
          
          endmodule
          
          Di sini kita hanya mendefinisikan prototipe modul dan mendefinisikan port input / output, sekarang kita harus menambahkan beberapa logika yang berguna dengan menambahkan beberapa kode antara header modul dan pernyataan endmodule.

          Karena kita mulai dengan contoh penghitung, mari kita lanjutkan dengan itu dan menulis beberapa kode yang benar-benar mengimplementasikannya:

          module COUNTER #(
            pWIDTH=8
          ) (
            input               iCLK,
            input               iRESET,
            output [pWIDTH-1:0] oCOUNTER
          );
          
          always @(posedge iCLK)
          begin
            if (iRESET) begin
              oCOUNTER<=0;
            end else begin
              oCOUNTER<= oCOUNTER+1;
            end
          end
          
          endmodule
          
          Kode di atas cukup jelas ... di setiap tepi jam positif, jika kita melihat input tinggi iRESET kita mereset penghitung, jika tidak kita menambahnya dengan satu ... perhatikan bahwa memiliki sinyal reset mengembalikan blok kita ke keadaan yang diketahui sering berguna tetapi tidak selalu dibutuhkan.

          Sekarang ... ini menarik tetapi kami melakukan sesuatu yang agak rumit ... kami menyatakan oCOUNTER sebagai keluaran reg, yang berarti kami mengatakan ini bukan hanya sekelompok kabel tetapi memiliki memori. Dengan cara ini kita dapat menggunakan <= tugas yang "terdaftar" yang berarti bahwa tugas tersebut akan disimpan selama siklus jam berikutnya.

          Cara lain yang bisa kita lakukan adalah menghapus pernyataan reg dalam deklarasi modul dan mendefinisikan penghitung sebagai berikut:

          module COUNTER #(
            pWIDTH=8
          ) (
            input               iCLK,
            input               iRESET,
            output [pWIDTH-1:0] oCOUNTER
          );
          
          reg [pWIDTH-1:0] rCOUNTER;
          
          always @(posedge iCLK)
          begin
            if (iRESET) begin
              rCOUNTER<=0;
            end else begin
              rCOUNTER<= rCOUNTER+1;
            end
          end
          
          assign oCOUNTER=rCOUNTER;
          
          endmodule
          
          Ini pada dasarnya adalah hal yang sama tetapi kami mendefinisikan register, mengerjakannya dan kemudian ditugaskan dengan "kontinu" = menugaskannya ke sinyal output. Perbedaannya di sini adalah bahwa sementara <= berarti sinyal hanya berubah pada clock edge = memberikan nilai terus menerus sehingga sinyal pada akhirnya akan berubah setiap saat, namun jika kita menetapkannya seperti yang kita lakukan dalam contoh ke register yang hanya berubah pada Jam tepi sinyal yang dihasilkan pada dasarnya hanya sebuah alias.

          Tugas yang menarik, seperti pernyataan lain dalam bahasa deskripsi perangkat keras adalah paralel, yang berarti bahwa urutannya dalam kode tidak begitu relevan karena semuanya dieksekusi secara paralel sehingga kami dapat menugaskan oCOUNTER ke rCOUNTER juga sebelum blok selalu. Kami akan kembali ke sini karena tidak sepenuhnya benar bahwa pesanan tidak masalah ...

          Penggunaan lain yang menarik dari penugasan kontinu adalah kemungkinan untuk membuat persamaan logika. Misalnya kita bisa menulis ulang penghitung dengan cara berikut:

          module COUNTER #(
            pWIDTH=8
          ) (
            input               iCLK,
            input               iRESET,
            output [pWIDTH-1:0] oCOUNTER
          );
          
          reg [pWIDTH-1:0] rCOUNTER;
          wire [pWIDTH-1:0] wNEXT_COUNTER;
          
          assign wNEXT_COUNTER = rCOUNTER+1;
          assign oCOUNTER = rCOUNTER;
          
          always @(posedge iCLK)
          begin
            if (iRESET) begin
              rCOUNTER<=0;
            end else begin
              rCOUNTER<= wNEXT_COUNTER;
            end
          end
          
          endmodule
          
          Kami pada dasarnya masih melakukan hal yang sama tetapi kami telah melakukannya dengan cara yang membuatnya sedikit lebih jelas secara logis. Pada dasarnya kami menetapkan secara kontinyu sinyal wNEXT_COUNTER dengan nilai rCOUNTER plus satu. Ini berarti bahwa wNEXT_COUNTER akan (hampir) segera berubah segera setelah rCOUNTER mengubah nilai namun rCOUNTER akan diperbarui hanya pada edge clock positif berikutnya (karena memiliki <= penugasan) sehingga hasilnya tetap bahwa rCOUNTER hanya berubah pada edge clock.


          Paralelisme dan diutamakan


          Seperti yang kami tulis pada bab sebelumnya, semua bahasa deskripsi perangkat keras memiliki konsep pernyataan paralel yang berarti bahwa berbeda dengan bahasa pemrograman perangkat lunak yang mengeksekusi instruksi secara berurutan, di sini semua instruksi dieksekusi pada waktu yang sama. Sebagai contoh jika kita menulis sebuah blok yang memiliki kode di bawah ini kita akan melihat register berubah bersama di tepi jam tertentu:

          reg [pWIDTH-1:0] rCOUNT_UP, rCOUNT_DOWN;
          
          always @(posedge iCLK)
          begin
            if (iRESET) begin
              rCOUNT_UP<=0;
              rCOUNT_DOWN<=0;
            end else begin
              rCOUNT_UP<= rCOUNT_UP+1;
              rCOUNT_DOWN<= rCOUNT_DOWN-1;
            end
          end
          
          Tentu saja jika semuanya dieksekusi secara paralel, kita perlu memiliki cara untuk mengurutkan pernyataan yang dapat dilakukan dengan membuat mesin keadaan sederhana. Mesin keadaan adalah sistem yang menghasilkan keluaran berdasarkan input DAN keadaan internalnya. Dalam arti counter kami sudah menjadi mesin negara karena kami memiliki output (oCOUNTER) yang berubah berdasarkan keadaan mesin sebelumnya (rCOUNTER), namun mari kita lakukan sesuatu yang lebih menarik dan buat mesin negara yang menciptakan denyut nadi yang diberikan panjang ketika kita memulainya. Mesin akan memiliki tiga status: eST_IDLE, eST_PULSE_HIGH dan eST_PULSE_LOW. Di eST_IDLE kami akan mencicipi perintah input dan ketika itu diterima, kami beralih ke eST_PULSE_HIGH, tempat kami akan tinggal selama jumlah jam yang diberikan, yang akan kami parametrize dengan pHIGH_COUNT,


          module PULSE_GEN #(
            pWIDTH=8,
            pHIGH_COUNT=240,
            pLOW_COUNT=40 
          ) (
            input       iCLK,
            input       iRESET,
            input       iPULSE_REQ,
            output reg  oPULSE
          );
          
          reg [pWIDTH-1:0] rCOUNTER;
          enum reg [1:0] {
            eST_IDLE,
            eST_PULSE_HIGH,
            eST_PULSE_LOW
          } rSTATE;
          
          always @(posedge iCLK)
          begin
            if (iRESET) begin
              rSTATE<=eST_IDLE;
            end else begin
              case (rSTATE)
                eST_IDLE: begin
                  if (iPULSE_REQ) begin
                    rSTATE<= eST_PULSE_HIGH;
                    oPULSE<= 1;
                    rCOUNTER <= pHIGH_COUNT-1;
                  end
                end
                eST_PULSE_HIGH: begin
                  rCOUNTER<= rCOUNTER-1;
                  if (rCOUNTER==0) begin
                    rSTATE<= eST_PULSE_LOW;
                    oPULSE<= 0;
                    rCOUNTER<= pLOW_COUNT-1;
                  end
                end
                eST_PULSE_LOW: begin
                  rCOUNTER<= rCOUNTER-1;
                  if (rCOUNTER==0) begin
                    rSTATE<= eST_IDLE;
                  end
                end
              endcase
            end
          end
          
          endmodule
          
          Di sini kita melihat sejumlah hal baru yang perlu kita bicarakan. Pertama-tama kita mendefinisikan variabel rSTATE menggunakan enum. Ini membantu dalam menetapkan status ke nilai-nilai yang mudah dimengerti daripada angka-angka yang dikode keras dan memiliki keuntungan bahwa Anda dapat memasukkan status dengan mudah tanpa perlu menulis ulang semua mesin status Anda.

          Kedua kami memperkenalkan blok case / endcase yang memungkinkan kami untuk mendefinisikan perilaku yang berbeda tergantung pada keadaan sinyal. Sintaksnya sangat mirip dengan C sehingga harus akrab bagi sebagian besar pembaca. Penting untuk dicatat bahwa pernyataan dalam berbagai blok kasus masih akan dieksekusi secara paralel tetapi karena mereka dikondisikan oleh nilai-nilai berbeda dari variabel yang diperiksa, hanya satu per satu waktu yang akan diaktifkan. Melihat kasing eST_IDLE kita melihat bahwa kita tetap berada dalam keadaan selamanya sampai kita merasakan iPULSE_REQ menjadi tinggi, dalam hal ini kita mengubah keadaan, mengatur ulang penghitung ke periode keadaan tinggi dan mulai mengeluarkan pulsa.

          Perhatikan bahwa sejak oPULSE terdaftar, ia akan mempertahankan statusnya hingga ditetapkan lagi. Dalam keadaan selanjutnya, hal-hal menjadi sedikit lebih rumit ... pada setiap jam kita mengurangi penghitung, maka jika penghitung mencapai 0 kita juga mengubah keadaan, ubah oPULSE menjadi 0 DAN kita tetapkan rCOUNTER lagi. Karena kedua penugasan dieksekusi secara paralel, kita perlu tahu apa artinya ini dan cukup beruntung semua mandat HDL bahwa jika dua pernyataan paralel dieksekusi pada register yang sama hanya yang terakhir akan benar-benar dieksekusi sehingga makna dari apa yang baru saja kita tulis adalah bahwa biasanya kami mengurangi penghitung tetapi ketika penghitung mencapai 0 kami mengubah status dan menginisialisasi ulang ke pLOW_COUNT.

          Pada titik ini apa yang terjadi di eST_PULSE_LOW menjadi cukup jelas karena kami baru saja mengurangi penghitung dan kembali ke eST_IDLE segera setelah mencapai 0. Perhatikan bahwa ketika kami kembali ke eST_IDLE rCOUNTER dikurangi lagi sehingga hasilnya adalah rCOUNTER akan menjadi 0xff ( atau -1) di eST_IDLE tetapi kami tidak terlalu peduli karena kami akan mengatur ulang ke nilai yang tepat ketika kami menerima iPULSE_REQ.

          Meskipun kami juga dapat mengatur ulang rCOUNTER ketika keluar dari eST_PULSE_LOW, dalam HDL selalu lebih baik untuk melakukan hanya apa yang benar-benar diperlukan karena apa pun yang lebih banyak akan menghabiskan sumber daya dan membuat perangkat keras kami lebih lambat. Pada awalnya ini mungkin tampak berbahaya tetapi dengan beberapa pengalaman akan menjadi mudah untuk melihat bagaimana ini dapat membantu. Konsep yang sama ini berlaku untuk mereset logika. Kecuali jika itu benar-benar diperlukan, tergantung bagaimana penerapannya, ia dapat menghabiskan sumber daya dan memperburuk kecepatan sistem sehingga harus digunakan dengan hati-hati.


          Contoh dunia nyata


          Sekarang mari kita selami contoh dunia nyata dari perangkat sederhana yang kita gunakan di Vidor, PWM. Apa yang ingin kami capai adalah membuat blok kecil dengan beberapa output PWM dengan kemungkinan untuk menentukan fase relatif dari masing-masing saluran PWM.

          Untuk melakukannya kita perlu penghitung dan beberapa pembanding yang memberi tahu kita ketika penghitung di atas diberi nilai sehingga kita dapat beralih keluaran. Karena kita juga ingin agar frekuensi PWM dapat diprogram, kita perlu memiliki penghitung yang beroperasi pada frekuensi yang berbeda dari basis yang kita gunakan untuk sistem kita sehingga periode itu persis seperti yang kita butuhkan. Untuk melakukannya, kami menggunakan prescaler yang pada dasarnya adalah penghitung lain yang membagi jam dasar ke nilai yang lebih rendah dengan cara yang mirip dengan generator baud rate yang digunakan dalam UART .

          Sekarang mari kita lihat kodenya:

          module PWM #(
            parameter pCHANNELS=16,
            parameter pPRESCALER_BITS=32,
            parameter pMATCH_BITS=32
          
          ) 
          (
            input                              iCLK,
            input                              iRESET,
          
            input [$clog2(2*pCHANNELS+2)-1:0]  iADDRESS,
            input [31:0]                       iWRITE_DATA,
            input                              iWRITE,
          
            output reg [pCHANNELS-1:0]         oPWM
          );
          
          // register declaration  
          reg [pPRESCALER_BITS-1:0] rPRESCALER_CNT;
          reg [pPRESCALER_BITS-1:0] rPRESCALER_MAX;
          
          reg [pMATCH_BITS-1:0] rPERIOD_CNT;
          reg [pMATCH_BITS-1:0] rPERIOD_MAX;
          reg [pMATCH_BITS-1:0] rMATCH_H [pCHANNELS-1:0];
          reg [pMATCH_BITS-1:0] rMATCH_L [pCHANNELS-1:0];
          reg rTICK;
          
          integer i;
          
          always @(posedge iCLK)
          begin
            // logic to interface with bus.
            // register map is as follows:
            // 0: prescaler value
            // 1: PWM period
            // even registers >=2: value at which PWM output is set high
            // odd registers >=2: value at which PWM output is set low
            if (iWRITE) begin
              // the following statement is executed only if address is >=2. case on iADDRESS[0]
              // selects if address is odd (iADDRESS[0]=1) or even (iADDRESS[0]=0)
              if (iADDRESS>=2) case (iADDRESS[0])
                0: rMATCH_H[iADDRESS[CLogB2(pCHANNELS):1]-1]<= iWRITE_DATA;
                1: rMATCH_L[iADDRESS[CLogB2(pCHANNELS):1]-1]<= iWRITE_DATA;
              endcase
              else begin
                // we get here if iADDRESS<2
               case (iADDRESS[0])
                0: rPRESCALER_MAX<=iWRITE_DATA;
                1: rPERIOD_MAX<=iWRITE_DATA;
              endcase
              end
            end
          
            // prescaler is always incrementing       
            rPRESCALER_CNT<=rPRESCALER_CNT+1;
            rTICK<=0;
            if (rPRESCALER_CNT>= rPRESCALER_MAX) begin
              // if prescaler is equal or greater than the max value
              // we reset it and set the tick flag which will trigger the rest of the logic
              // note that tick lasts only one clock cycle as it is reset by the rTICK<= 0 above
              rPRESCALER_CNT<=0;
              rTICK <=1;
            end
            if (rTICK) begin
              // we get here each time rPRESCALER_CNT is reset. from here we increment the PWM
              // counter which is then clocked at a lower frequency.
              rPERIOD_CNT<=rPERIOD_CNT+1;
              if (rPERIOD_CNT>=rPERIOD_MAX) begin
                // and of course we reset the counter when we reach the max period.
                rPERIOD_CNT<=0;
              end
            end
          
            // this block implements the parallel comparators that actually generate the PWM outputs
            // the for loop actually generates an array of logic that compares the counter with
            // the high and low match values for each channel and set the output accordingly.
            for (i=0;i<pCHANNELS;i=i+1) begin
              if (rMATCH_H[i]==rPERIOD_CNT)
                oPWM[i] <=1;
              if (rMATCH_L[i]==rPERIOD_CNT)
                oPWM[i] <=0;
            end
          end
          
          endmodule
          
          Ada beberapa hal baru di sini untuk dipelajari, jadi mari kita mulai dari deklarasi modul. Di sini kita menggunakan fungsi bawaan untuk menetapkan lebar bit yang diperlukan dari bus alamat. Tujuannya adalah untuk membatasi rentang alamat ke minimum yang diperlukan untuk register, jadi misalnya jika kita ingin 10 saluran, kita membutuhkan total 22 alamat. Karena setiap bit alamat menggandakan jumlah alamat yang dapat kita gunakan, kita membutuhkan total 5 bit yang menghasilkan 32 alamat total. Untuk membuat parametrik ini, kami mendefinisikan lebar iADDRESS sebagai $ clog2 (2 * pCHANNELS + 2) dan kami mendefinisikan register sebagai array 2 dimensi.

          Sebenarnya ada dua cara untuk membuat array multidimensi dan di sini kita menggunakan array "unpacked", yang pada dasarnya mendefinisikan register sebagai entitas terpisah dengan menambahkan indeks di sisi kiri deklarasi register. Cara lain, kita tidak menggunakan dalam contoh ini adalah yang "dikemas" di mana indeks berada di sisi kiri deklarasi dan hasilnya adalah bahwa array 2D juga dapat dilihat sebagai register besar tunggal yang berisi rangkaian dari semua register.

          Trik lain yang menarik di sini adalah bagaimana kita mendefinisikan logika yang menangani register. Pertama-tama kami hanya menerapkan register tulis saja sehingga Anda tidak akan menemukan sinyal iREAD dan iREAD_DATA Kedua, kami ingin memiliki set register parametrik di mana hanya dua register pertama yang selalu ada sedangkan sisanya didefinisikan dan ditangani secara dinamis berdasarkan pada jumlah saluran yang ingin kami implementasikan. Untuk melakukannya, kita perhatikan bahwa dalam bilangan biner, bit paling tidak penting menentukan apakah bilangan tersebut ganjil atau genap. Karena kami memiliki dua register per saluran, ini berguna karena kami dapat membedakan perilaku kami tergantung pada apakah kami di bawah alamat 2 atau tidak.

          Jika kami di bawah alamat 2, kami menerapkan register umum yang merupakan hitungan prescaler dan periode penghitung. Jika kita di atas 2 kita menggunakan LSB untuk memutuskan apakah kita menulis nilai untuk komparator tinggi atau rendah.


          Contoh sederhana lainnya


          Contoh sederhana lain yang dapat kita pelajari adalah encoder quadrature. Meskipun mungkin tampak lebih sederhana daripada PWM itu juga mengatasi beberapa tantangan yang tidak sepele. Masalah pertama yang kami temui berurusan dengan sinyal dari dunia eksternal adalah bahwa kami tidak memiliki jaminan mereka sinkron dengan jam internal kami sehingga kami dapat menemukan fenomena yang disebut metastabilitas yang menyebabkan data dalam register tidak terdefinisi dan berpotensi berubah selama siklus clock. Alasan untuk ini adalah bahwa jika data berubah pada input register ketika kita mengunci itu register mungkin masuk ke keadaan tidak stabil yang dapat "membusuk" menjadi 1 atau 0 setiap saat.

          Hal lain yang menarik yang kami lakukan di sini adalah bahwa kami menggunakan penugasan berkelanjutan untuk menentukan strobo dan arah dari sinyal quadrature keluar dari enkoder. Dalam kode ada grafik sederhana yang menunjukkan bagaimana bentuk gelombang, walaupun untuk memahami sepenuhnya bagaimana bentuk gelombang, Anda harus mempertimbangkan bahwa persamaan menggunakan sinyal pada titik yang berbeda dalam waktu. Hal ini dilakukan dengan hanya menggunakan register geser yang digunakan untuk menyinkronkan input asinkron juga untuk menunda mereka sehingga mengetuk ke titik yang berbeda dari register geser kita dapat melihat bagaimana sinyal adalah jam sebelumnya. Secara khusus, jika kita bergerak menuju input register geser, kita mendapatkan data "yang lebih baru" sedangkan jika kita bergerak ke arah akhir kita mendapatkan data "yang lebih tua".

          Melihat bentuk gelombang kita melihat bahwa strobo menghasilkan pulsa setiap kali A atau B memiliki tepi dan ini dilakukan dengan hanya xoring setiap sinyal dengan versi tertunda. Sinyal arahnya agak sedikit lebih rumit tetapi kami melihat bahwa itu adalah konstan baik 0 atau 1 ketika sinyal strobo tinggi tergantung pada arah yang berputar encoder. Sebenarnya kita melihat ada pulsa pada sinyal arah tetapi ini tidak bersamaan dengan strobo sehingga mereka akan diabaikan.

          Satu hal yang mungkin tidak terlihat jelas pada pandangan pertama adalah bahwa persamaannya secara parsial menghitung logika yang sama untuk semua input, pada kenyataannya register rRESYNC_ENCODER dikemas dengan susunan dua dimensi yang diatur sehingga indeks pertama mengidentifikasi ketukan pada register geser dan yang kedua. index adalah saluran encoder. Ini berarti bahwa setiap kali kami mereferensikan rRESYNC_ENCODER dengan indeks tertentu, kami memilih array mono-dimensi yang berisi semua input encoder sekaligus ditunda oleh jumlah jam yang ditentukan oleh indeks. Ini juga berarti bahwa ketika kita melakukan operasi logika bitwise pada array kita sebenarnya instantiating beberapa persamaan logika paralel pada saat yang sama.

          Seperti yang kami lakukan untuk contoh lain, blok mengimplementasikan beberapa input dan melakukan ini dengan menggunakan for loop yang memeriksa sinyal aktif (yang lagi-lagi adalah array selebar jumlah saluran) dan ketika itu tinggi ia memeriksa arah dan berdasarkan itu menambah atau mengurangi penghitung untuk saluran itu. Ini mudah dilakukan dengan menggunakan? : operator (ekspresi kondisional) yang bekerja persis seperti di C.

          Akhirnya antarmuka bus cukup sederhana karena satu-satunya register yang kita miliki adalah read-only counters dan kita dapat mengimplementasikannya hanya dengan memeriksa sinyal read dan menugaskan data output dengan array counter yang diindeks oleh alamat, cukup mirip dengan RAM. .

          module QUAD_ENCODER #(
              pENCODERS=2,
              pENCODER_PRECISION=32
            )(
              input                             iCLK,
              input                             iRESET,
              // AVALON SLAVE INTERFACE
              input  [$clog2(pENCODERS)-1:0]   iAVL_ADDRESS,
              input                             iAVL_READ,
              output reg [31:0]                 oAVL_READ_DATA,
              // ENCODER INPUTS
              input [pENCODERS-1:0]             iENCODER_A,
              input [pENCODERS-1:0]             iENCODER_B
            );
          
          
          // bidimensional arrays containing encoder input states at 4 different points in time
          // the first two delay taps are used to synchronize inputs with the internal clocks
          // while the other two are used to compare two points in time of those signals.
          reg [3:0][pENCODERS-1:0] rRESYNC_ENCODER_A,rRESYNC_ENCODER_B;
          
          // bidimensional arrays containing the counters for each channel
          reg [pENCODERS-1:0][pENCODER_PRECISION-1:0] rSTEPS;
          
          // encoder decrementing
          // A       __----____----__
          // B       ____----____----
          // ENABLE  __-_-_-_-_-_-_-_
          // DIR     __---_---_---_--
          //     
          // encoder incrementing
          // A       ____----____----
          // B       __----____----__
          // ENABLE  __-_-_-_-_-_-_-_
          // DIR     ___-___-___-___-
          
          wire [pENCODERS-1:0] wENABLE =  rRESYNC_ENCODER_A[2]^rRESYNC_ENCODER_A[3]^rRESYNC_ENCODER_B[2]^rRESYNC_ENCODER_B[3];
          wire [pENCODERS-1:0] wDIRECTION = rRESYNC_ENCODER_A[2]^rRESYNC_ENCODER_B[3];
          
          integer i;
          
          initial rSTEPS <=0;
          
          always @(posedge iCLK)
          begin
            if (iRESET) begin
              rSTEPS<=0;
              rRESYNC_ENCODER_A<=0;
              rRESYNC_ENCODER_B<=0;
            end
            else begin
              // implement shift registers for each channel. since arrays are packed we can treat that as a monodimensional array
              // and by adding inputs at the bottom we are effectively shifting data by one bit
              rRESYNC_ENCODER_A<={rRESYNC_ENCODER_A,iENCODER_A};
              rRESYNC_ENCODER_B<={rRESYNC_ENCODER_B,iENCODER_B};
          
              for (i=0;i<pENCODERS;i=i+1)
              begin
                // if strobe is high..
                if (wENABLE[i])
                  // increment or decrement based on direction
                  rSTEPS[i] <= rSTEPS[i]+ ((wDIRECTION[i]) ? 1 : -1);
              end
              // if slave interface is being read...
              if (iAVL_READ)
              begin
                // return the value of the counter indexed by the address
                oAVL_READ_DATA<= rSTEPS[iAVL_ADDRESS];
              end
            end
          end
          
          endmodule
          
          Ini mungkin merupakan contoh yang bagus tentang bagaimana elegan dan ringkas dapat menjadi deskripsi perangkat keras untuk desain seperti saat kami mendeskripsikan desain yang sangat parametrik di mana kami dapat mengubah kedalaman penghitung dan jumlah saluran dan kode meningkat sesuai dengan itu menghasilkan semua logika terkait dalam sangat mudah dibaca cara. Tentu saja ada beberapa cara berbeda untuk melakukan hal yang sama dan ini salah satu yang paling ringkas bahwa pada saat yang sama membutuhkan sedikit lebih banyak pemahaman tentang kemampuan (sistem) Verilog.


          SUMBER

          • [1] https://www.arduino.cc/en/Tutorial/VidorHDL