Bagaimana cara kerja komunikasi serial di Arduino?

16

Dengan mengacu pada Arduino Uno, Mega2560, Leonardo dan papan serupa:

  • Bagaimana cara kerja komunikasi serial?
  • Seberapa cepat serial?
  • Bagaimana saya terhubung antara pengirim dan penerima?

Harap dicatat: Ini dimaksudkan sebagai pertanyaan referensi.

Nick Gammon
sumber
Anda mungkin menemukan ini menarik tentang buffer di kedua sisi Nano yang terhubung ke sistem Raspian yang menjalankan pencatat data Python, hanya menggunakan kabel pemrograman USB biasa di antara keduanya: arduino.stackexchange.com/questions/11710/…
SDsolar

Jawaban:

16

Komunikasi serial asinkron (biasanya disebut sebagai serial) digunakan untuk mengirim byte dari satu perangkat ke perangkat lainnya. Perangkat dapat berupa satu atau lebih dari yang berikut:

  • Arduino
  • PC
  • GPS
  • Pembaca kartu RFID
  • layar LCD
  • Modem
  • Lain

Laju jam dan pengambilan sampel data

Tidak seperti komunikasi serial SPI / USB / I2C tidak memiliki sinyal clock. Jam pengambilan sampel adalah laju sampel yang disepakati (dikenal sebagai laju baud). Baik pengirim dan penerima harus dikonfigurasikan untuk menggunakan laju yang sama atau penerima akan menerima data yang tidak berarti (karena bit tidak diambil sampel pada laju yang sama dengan yang dikirim).

Transmisinya asinkron yang pada dasarnya berarti byte dapat dikirim kapan saja, dengan celah yang berbeda di antaranya. Grafik ini menggambarkan satu byte yang dikirim:

Serial comms - mengirim satu byte

Grafik di atas menunjukkan huruf 'F' sedang dikirim. Dalam ASCII ini adalah 0x46 (dalam hex) atau 0b01000110 (dalam biner). The setidaknya signifikan (low-order) bit ditransmisikan pertama, sehingga dalam grafik di atas Anda melihat bit tiba di urutan: 01100010.

Waktu "idle" antara byte ditransmisikan sebagai bit "1" kontinu (secara efektif, jalur transmisi dipegang tinggi terus menerus).

Untuk menunjukkan awal byte, Bit Mulai selalu ditunjukkan dengan menarik garis rendah seperti yang ditunjukkan pada grafik. Setelah penerima melihat bit mulai, itu menunggu 1,5 kali waktu sampel, dan kemudian sampel bit data. Ia menunggu 1,5 kali sehingga:

  • Melewati bit awal
  • Sampel setengah jalan melalui bit berikutnya

Jika laju baud adalah 9600 baud, misalnya, maka laju sampel akan menjadi 1/9600 = 0.00010416detik (104,16 μs).

Jadi, pada 9600 baud, setelah menerima bit mulai penerima menunggu 156,25 μs, dan kemudian sampel setiap 104,16 μs.

Mulai waktu bit

Tujuan dari Bit Stop adalah untuk memastikan bahwa pasti ada 1-bit antara setiap byte. Tanpa bit stop, jika byte berakhir dengan nol, maka tidak mungkin bagi perangkat keras untuk mengetahui perbedaan antara itu dan bit mulai dari byte berikutnya.

Untuk menghasilkan output di atas pada Uno Anda dapat menulis kode ini:

void setup()
  {
      Serial.begin(9600);
      Serial.print("F");
  }

void loop ()
  {
  }

Jumlah bit data

Untuk menghemat waktu transmisi (di masa lalu, heh) Anda diizinkan untuk menentukan jumlah bit data yang berbeda. Perangkat keras AtMega mendukung bit data penomoran dari 5 hingga 9. Jelas semakin sedikit bit data semakin sedikit informasi yang dapat Anda kirim, tetapi semakin cepat akan.


Bit paritas

Anda dapat memiliki sedikit paritas. Ini dihitung, jika diperlukan, dengan menghitung angka 1 pada karakter, dan kemudian memastikan bahwa angka ini ganjil atau genap dengan mengatur bit paritas ke 0 atau 1 seperti yang diperlukan.

Misalnya, untuk huruf "F" (atau 0x46 atau 0b01000110) Anda dapat melihat bahwa ada 3 huruf di sana (pada 01000110). Jadi kita sudah memiliki paritas aneh. Jadi, bit paritasnya adalah sebagai berikut:

  • Tanpa paritas: dihilangkan
  • Paritas genap: a 1 (3 + 1 genap)
  • Paritas ganjil: a 0 (3 + 0 ganjil)

Bit paritas, jika ada, muncul setelah bit data terakhir tetapi sebelum bit berhenti.

Jika penerima tidak mendapatkan bit paritas yang benar, itu disebut "kesalahan paritas". Ini menunjukkan bahwa ada beberapa masalah. Mungkin pengirim dan penerima dikonfigurasikan untuk menggunakan tingkat baud (bit) yang berbeda, atau ada suara di saluran yang mengubah nol menjadi satu atau sebaliknya.

Beberapa sistem awal juga menggunakan paritas "mark" (di mana bit paritas selalu 1 terlepas dari data), atau paritas "spasi" (di mana bit paritas selalu 0 terlepas dari data).


Transmisi 9-bit

Beberapa peralatan komunikasi menggunakan data 9-bit, sehingga dalam kasus ini bit paritas diubah menjadi bit ke-9. Ada teknik khusus untuk mengirim bit ke-9 ini (register adalah register 8-bit sehingga bit ke-9 harus diletakkan di tempat lain).


Jumlah bit stop

Peralatan awal cenderung lebih lambat secara elektronik, sehingga untuk memberi waktu penerima untuk memproses byte yang masuk, kadang-kadang ditentukan bahwa pengirim akan mengirim dua bit penghenti. Ini pada dasarnya menambahkan lebih banyak waktu di mana garis data dijaga tinggi (sekali lagi bit) sebelum bit mulai berikutnya dapat muncul. Waktu bit ekstra ini memberi waktu penerima untuk memproses byte yang masuk terakhir.

Jika penerima tidak mendapatkan 1 logis ketika bit stop seharusnya, itu disebut "kesalahan pembingkaian". Ini menunjukkan bahwa ada beberapa masalah. Sangat mungkin pengirim dan penerima dikonfigurasikan untuk menggunakan tingkat baud (bit) yang berbeda.


Notasi

Umumnya, komunikasi serial ditunjukkan dengan memberi tahu Anda kecepatan, jumlah bit data, jenis paritas, dan jumlah bit stop, seperti ini:

9600/8-N-1

Ini memberitahu kita:

  • 9600 bit per detik
  • 8 bit data
  • Tanpa paritas (Anda mungkin melihat sebaliknya: E = genap, O = ganjil)
  • 1 stop bit

Penting bahwa pengirim dan penerima menyetujui hal di atas, jika tidak komunikasi tidak akan berhasil.


Pin-out

Arduino Uno memiliki pin digital 0 dan 1 yang tersedia untuk serial perangkat keras:

Pin seri Arduino Uno

Untuk menghubungkan dua Arduino Anda menukar Tx dan Rx seperti ini:

Menghubungkan dua Arduino bersama-sama


Kecepatan

Berbagai kecepatan didukung (lihat grafik di bawah). "Standar" kecepatan biasanya kelipatan 300 baud (mis. 300/600/1200/2400 dll.).

Kecepatan "non-standar" lainnya dapat ditangani dengan mengatur register yang sesuai. Kelas HardwareSerial melakukan ini untuk Anda. misalnya.

Serial.begin (115200);  // set speed to 115200 baud

Sebagai aturan umum, dengan asumsi Anda menggunakan data 8-bit, maka Anda dapat memperkirakan jumlah byte yang dapat Anda kirimkan per detik dengan membagi baud rate dengan 10 (karena bit awal dan bit berhenti).

Dengan demikian, pada 9600 baud Anda dapat mengirimkan 960 byte ( 9600 / 10 = 960) per detik.


Kesalahan tingkat Baud

Baud rate pada Atmega dihasilkan dengan membagi jam sistem, dan kemudian menghitung hingga nomor yang telah ditetapkan. Tabel ini dari lembar data menunjukkan nilai register, dan persentase kesalahan, untuk jam 16 MHz (seperti yang ada di Arduino Uno).

Kesalahan tingkat Baud

Bit U2Xn mempengaruhi pembagi laju jam (0 = bagi dengan 16, 1 = bagi dengan 8). Register UBRRn berisi nomor yang dihitung oleh prosesor.

Jadi dari tabel di atas, kita melihat bahwa kita mendapatkan 9600 baud dari clock 16 MHz sebagai berikut:

16000000 / 16 / 104 = 9615

Kami membagi dengan 104 dan bukan 103 karena penghitungnya adalah nol relatif. Jadi kesalahan di sini adalah 15 / 9600 = 0.0016yang dekat dengan apa yang dikatakan tabel di atas (0,02%).

Anda akan melihat bahwa beberapa baud rate memiliki jumlah kesalahan yang lebih tinggi daripada yang lain.

Menurut datasheet, persentase kesalahan maksimum untuk 8 bit data adalah dalam kisaran 1,5% hingga 2,0% (lihat datasheet untuk rincian lebih lanjut).


Arduino Leonardo

Arduino Leonardo dan Micro memiliki pendekatan yang berbeda untuk komunikasi serial, karena mereka terhubung langsung melalui USB ke komputer host, bukan melalui port serial.

Karena itu, Anda harus menunggu Serial menjadi "siap" (karena perangkat lunak membuat koneksi USB), dengan beberapa baris tambahan, seperti ini:

void setup()
  {
      Serial.begin(115200);
      while (!Serial)
      {}  // wait for Serial comms to become ready
      Serial.print("Fab");
  }

void loop ()
  {
  }

Namun, jika Anda ingin benar-benar berkomunikasi melalui pin D0 dan D1, (bukan dengan kabel USB) maka Anda perlu menggunakan Serial1 daripada Serial. Anda melakukannya seperti ini:

void setup()
  {
      Serial1.begin(115200);
      Serial1.print("Fab");
  }

void loop ()
  {
  }

Tingkat tegangan

Perhatikan bahwa Arduino menggunakan level TTL untuk komunikasi serial. Ini berarti bahwa ia mengharapkan:

  • Bit "nol" adalah 0V
  • Bit "satu" adalah + 5V

Peralatan serial yang lebih tua yang dirancang untuk dihubungkan ke port serial PC mungkin menggunakan level tegangan RS232, yaitu:

  • Bit "nol" adalah +3 hingga +15 volt
  • Bit "satu" adalah to3 hingga −15 volt

Tidak hanya ini "terbalik" sehubungan dengan level TTL ("satu" lebih negatif daripada "nol"), Arduino tidak dapat menangani tegangan negatif pada pin inputnya (atau yang positif lebih besar dari 5V).

Dengan demikian Anda memerlukan sirkuit antarmuka untuk berkomunikasi dengan perangkat tersebut. Untuk input (ke Arduino) saja, transistor sederhana, dioda, dan beberapa resistor akan melakukannya:

Pembalik buffer

Untuk komunikasi dua arah Anda harus dapat menghasilkan tegangan negatif, sehingga diperlukan sirkuit yang lebih kompleks. Sebagai contoh, chip MAX232 akan melakukan itu, bersama dengan empat kapasitor 1 μF untuk bertindak sebagai sirkuit pompa pengisian daya.


Serial Perangkat Lunak

Ada perpustakaan yang disebut SoftwareSerial yang memungkinkan Anda melakukan komunikasi serial (sampai titik tertentu) dalam perangkat lunak daripada perangkat keras. Ini memiliki keuntungan bahwa Anda dapat menggunakan konfigurasi pin yang berbeda untuk komunikasi serial. Kerugiannya adalah melakukan serial dalam perangkat lunak lebih intensif prosesor dan lebih rentan terhadap kesalahan. Lihat Serial Perangkat Lunak untuk lebih jelasnya.


Mega2560

Arduino "Mega" memiliki 3 port serial perangkat keras tambahan. Mereka ditandai di papan sebagai Tx1 / Rx1, Tx2 / Rx2, Tx3 / Rx3. Mereka harus digunakan dalam preferensi untuk SoftwareSerial jika memungkinkan. Untuk membuka port lain tersebut Anda menggunakan nama Serial1, Serial2, Serial3, seperti ini:

Serial1.begin (115200);  // start hardware serial port Tx1/Rx1
Serial2.begin (115200);  // start hardware serial port Tx2/Rx2
Serial3.begin (115200);  // start hardware serial port Tx3/Rx3

Terganggu

Baik mengirim dan menerima, menggunakan pustaka HardwareSerial, menggunakan interupsi.

Mengirim

Ketika Anda melakukan Serial.print, data yang Anda coba cetak ditempatkan dalam buffer "transmit" internal. Jika Anda memiliki 1024 byte atau lebih dari RAM (seperti pada Uno) Anda mendapatkan buffer 64-byte, jika tidak, Anda mendapatkan buffer 16-byte. Jika buffer memiliki ruang, maka Serial.printreturn segera, sehingga tidak menunda kode Anda. Jika tidak ada ruang, maka itu "blok" menunggu buffer dikosongkan cukup untuk ada ruang.

Kemudian, karena setiap byte ditransmisikan oleh perangkat keras interupsi disebut ("USART, Data Register Empty" interrupt) dan rutin interupsi mengirimkan byte berikutnya dari buffer keluar dari port serial.

Menerima

Ketika data yang masuk diterima, rutin interupsi disebut (interupsi "USART Rx Complete") dan byte yang masuk ditempatkan ke dalam buffer "terima" (ukuran yang sama dengan buffer transmisi yang disebutkan di atas).

Saat Anda menelepon, Serial.availablecari tahu berapa byte yang tersedia di buffer "terima" itu. Ketika Anda memanggil Serial.readbyte dihapus dari buffer terima dan kembali ke kode Anda.

Pada Arduino dengan 1000 byte atau lebih dari RAM, tidak ada terburu-buru untuk menghapus data dari buffer terima, asalkan Anda tidak membiarkannya mengisi. Jika terisi maka data yang masuk lebih lanjut dibuang.

Perhatikan bahwa karena ukuran buffer ini, tidak ada gunanya menunggu jumlah byte yang sangat besar untuk tiba, misalnya:

while (Serial.available () < 200)
  { }  // wait for 200 bytes to arrive

Ini tidak akan berhasil karena buffer tidak dapat menampung sebanyak itu.


Kiat

  • Sebelum membaca, selalu pastikan data tersedia. Misalnya, ini salah:

    if (Serial.available ())
      {
          char a = Serial.read ();
          char b = Serial.read ();  // may not be available
      }

    The Serial.availabletes hanya memastikan Anda memiliki satu byte tersedia, namun kode mencoba untuk membaca dua. Mungkin berhasil, jika ada dua byte dalam buffer, jika tidak Anda akan mendapatkan -1 yang akan terlihat seperti 'ÿ' jika dicetak.

  • Waspadai berapa lama waktu yang dibutuhkan untuk mengirim data. Seperti disebutkan di atas, pada 9600 baud Anda yang hanya mentransmisikan 960 byte per detik, jadi mencoba mengirim 1000 bacaan dari port analog, pada 9600 baud, tidak akan terlalu berhasil.


Referensi

Nick Gammon
sumber
Dalam grafik 1: dengan panah, sepertinya bit berhenti ditransmisikan pertama kali. Jika Anda menukar Rx / Tx dan arah panah saya pikir itu kurang membingungkan.
ott--
Itu dimaksudkan untuk dibaca dari kiri ke kanan (seperti kalimat ini) dan dengan demikian hal-hal di sebelah kiri terjadi terlebih dahulu. Letakkan seperti ini: pada osiloskop, begitulah Anda akan melihat jejaknya.
Nick Gammon
Ok dengan penjelasan osiloskop saya membelinya. :-)
ott--
Namun saya telah berpikir bahwa maksud Anda sangat masuk akal. Apa yang dipikirkan orang lain? Apakah akan lebih jelas jika panah dibalik, dan saya menukar Rx / Tx?
Nick Gammon
1
@ linhartr22 Saya mengubahnya untuk membaca "data yang tidak berarti" yang mungkin lebih dekat.
Nick Gammon