Bagaimana kegagalan-aman adalah \ n sebagai stop byte?

8

Dalam komunikasi UART saya, saya perlu mengetahui byte awal dan byte berhenti dari pesan yang dikirim. Byte awal itu mudah tetapi byte berhenti, tidak begitu banyak. Saya telah mengimplementasikan dua stop byte pada akhir pesan saya, yaitu \ n dan \ r (10 dan 13 desimal). UART hanya bekerja pada nilai byte 0-255 jadi bagaimana gagal-amannya ini? Saya bisa membayangkan, meskipun probabilitasnya rendah, bahwa pesan saya mungkin berisi nilai "10 dan 13" satu sama lain ketika mereka bukan byte berhenti.

Apakah ada cara yang lebih baik untuk mengimplementasikan ini?

CK
sumber
7
Untuk mengirim data sewenang-wenang, Anda harus menggunakan paket atau byte stuffing. Dalam kasus Anda, probabilitas pola yang muncul di lokasi tertentu adalah 1/65536. Yang mencapai 1 jika Anda memiliki aliran data acak yang cukup lama.
Oldfart
4
Bisakah Anda memberikan konteks? Bit henti adalah bagian dari komunikasi UART tetapi hentikan byte? Ini terdengar seperti masalah perangkat lunak murni dan tergantung pada apa yang telah disetujui oleh pengirim dan penerima.
Warren Hill
2
@MariusGulbrandsen jika data Anda benar-benar sewenang-wenang dan tidak hanya teks (pikirkan ASCII) maka penghentian nol tidak akan berfungsi; Anda harus mengimplementasikan paket.
RamblinRose
4
BTW: Itu praktik yang umum adalah untuk menempatkan carriage return sebelum feed line: "\x0D\x0A".
Adrian McCarthy
3
@AdrianMcCarthy Saya pikir tujuan untuk membalikkannya adalah untuk meminimalkan kemungkinan itu menjadi urutan yang valid. Yang mengatakan, dua Windows akhir baris berturut-turut akan memberi Anda \r\n\r\nyang berisi \n\rurutan di tengah ...
Mike Caron

Jawaban:

14

Ada berbagai cara untuk mencegah hal ini:

  • Pastikan Anda tidak pernah mengirim kombinasi 10/13 dalam pesan reguler Anda (jadi hanya sebagai byte berhenti). Misalnya untuk mengirim 20 21 22 23 24 25:

20 21 22 23 24 25 10 13

  • Escape 10 dan 13 (atau semua karakter non ASCII dengan karakter escape mis. Jadi untuk mengirim 20 21 10 13 25 26 kirim: (lihat komentar / kredit untuk: DanW)

20 21 1b 10 1b 13 25 26

  • Tentukan paket saat mengirim pesan. Misalnya jika Anda ingin mengirim pesan 20 21 22 23 24 25 daripada menambahkan jumlah byte yang dikirim, jadi paketnya adalah:

<nr_of_data_bytes> <data>

Jika pesan Anda maksimal 256 byte, kirim:

06 20 21 22 23 24 25

Jadi, Anda tahu setelah menerima 6 byte data yang akhirnya; Anda tidak harus mengirim 10 13 sesudahnya. Dan Anda dapat mengirim 10 13 ke dalam pesan. Jika pesan Anda bisa lebih panjang, Anda dapat menggunakan 2 byte untuk ukuran data.

Pembaruan 1: Cara lain untuk mendefinisikan paket

Alternatif lain adalah mengirim perintah yang memiliki panjang tertentu dan dapat memiliki banyak varian, misalnya

10 20 30 (Perintah 10 yang selalu memiliki 2 byte data)

11 30 40 50 (Perintah 11 yang selalu memiliki 3 byte data)

12 06 10 11 12 13 14 15 (Perintah 12 + 1 byte untuk jumlah byte data yang mengikuti)

13 01 02 01 02 03 ... (Perintah 13 + 2 byte (01 02 untuk 256 + 2 = 258 byte data yang mengikuti)

14 80 90 10 13 (Perintah 14 yang diikuti oleh string ASCII berakhir dengan 10 13)

Pembaruan 2: Koneksi buruk / kehilangan byte

Semua hal di atas hanya berfungsi ketika jalur UART mengirim byte dengan benar. Jika Anda ingin menggunakan cara pengiriman yang lebih andal, ada juga banyak kemungkinan. Berikut beberapa di antaranya:

  1. Mengirim checksum dalam paket (periksa google untuk CRC: Cyclic Redundancy Check). Jika CRC ok, penerima tahu pesan telah dikirim ok (dengan probabilitas tinggi).
  2. Jika Anda memerlukan pesan untuk dikirim ulang, maka mekanisme penerimaan (ACK / balasan) harus digunakan (mis. Pengirim mengirim sesuatu, penerima menerima data yang korup, mengirim NACK (tidak diakui), pengirim dapat mengirim lagi.
  3. Timeout: Jika penerima tidak mendapatkan ACK atau NACK pada waktunya, sebuah pesan perlu dikirim ulang.

Perhatikan bahwa semua mekanisme di atas bisa sederhana atau serumit yang Anda inginkan (atau perlu). Dalam hal mengirim ulang pesan, juga diperlukan mekanisme untuk mengidentifikasi pesan (misalnya menambahkan nomor urut ke dalam paket).

Michel Keijzers
sumber
1
"Pastikan Anda tidak pernah mengirim kombinasi 10/13 dalam pesan reguler Anda (jadi hentikan byte)." - Anda tidak mengatakan bagaimana mengirim data yang tidak mencakup kombinasi 10/13 - Anda perlu untuk melarikan diri itu. Jadi "20 10 13 23 10 13" dapat dikirim sebagai "20 1b 10 1b 13 23" dengan 1b sebagai karakter pelarian Anda.
Dan W
1
Perhatikan bahwa menggunakan bidang panjang seperti yang diusulkan, Anda akan mendapat masalah ketika tautan serial Anda buruk dan kehilangan satu byte. Semuanya akan tidak sinkron.
Jonas Schäfer
@DanW Jika Anda menggunakan yang pertama atau 2 byte sebagai jumlah byte data, tidak masalah jika 10 atau 13 adalah bagian dari data tersebut ... Jadi 20 10 13 23 10 13 dapat dikirim sebagai 06 20 10 13 23 10 13 di mana 06 adalah jumlah byte data yang mengikuti.
Michel Keijzers
@MichelKeijzers - ya, tapi itu solusi kedua yang Anda sebutkan. Solusi pertama Anda tidak memiliki penjelasan tentang urutan pelarian untuk mencegah byte berhenti dikirim.
Dan W
Kedua pendekatan tersebut bekerja, dan umumnya digunakan, tetapi keduanya memiliki kelebihan dan kekurangan yang berbeda, yang dapat Anda tambahkan jika diinginkan, meskipun itu melampaui apa yang diminta OP.
Dan W
13

Bagaimana kegagalan-aman adalah \ n sebagai stop byte?

Jika Anda mengirim mengirim data acak -> mungkin tidak cukup aman.

Solusi umum adalah menggunakan melarikan diri:

Mari kita tentukan bahwa karakter 0x02 (STX - frame start) dan 0x03 (ETX - frame end) harus unik dalam aliran data yang dikirim. Dengan cara ini, awal dan akhir pesan dapat dideteksi dengan aman.

Jika salah satu dari karakter ini harus dikirim dalam bingkai pesan, itu diganti dengan awalan karakter melarikan diri (ESC = 0x1b) dan menambahkan 0x20 ke karakter asli.

Karakter asli digantikan oleh

0x02 -> 0x1b 0x22  
0x03 -> 0x1b 0x23  
0x1b -> 0x1b 0x3b  

Penerima membalikkan proses ini: Setiap kali ia menerima karakter melarikan diri, karakter ini dijatuhkan dan karakter berikutnya dikurangi dengan 0x20.

Ini hanya menambahkan beberapa overhead pemrosesan tetapi 100% dapat diandalkan (dengan asumsi tidak ada kesalahan transmisi yang terjadi, yang dapat / harus Anda verifikasi dengan tambahan menerapkan mekanisme checksum).

Rev1.0
sumber
1
Jawaban bagus. Karakter melarikan diri yang umum digunakan untuk protokol ASCII adalah '\x10'DLE (Data Link Escape). Beberapa halaman Wikipedia menunjukkan bahwa DLE sering digunakan dengan cara yang berlawanan: untuk mengatakan bahwa byte berikutnya adalah karakter kontrol daripada byte data. Dalam pengalaman saya, itu umumnya arti sebaliknya untuk melarikan diri.
Adrian McCarthy
2
Satu hal yang perlu diperhatikan di sini adalah ukuran buffer case terburuk Anda berlipat ganda. Jika ingatannya benar-benar ketat, itu mungkin bukan solusi terbaik.
TechnoSam
1
@Rev Apa alasan untuk menambahkan 0x20 ke karakter asli? Bukankah skema melarikan diri bekerja tanpa itu juga?
Nick Alexeev
1
@NickAlexeev: Lebih mudah / lebih cepat untuk mengidentifikasi batas bingkai aktual jika Anda menghapus kejadian lain dari karakter cadangan dari aliran. Dengan begitu, Anda dapat memisahkan penerimaan frame dan frame parsing (termasuk un-escaping). Ini mungkin sangat relevan, jika Anda memiliki pengontrol yang sangat lambat tanpa FIFO dan / atau kecepatan data yang tinggi. Jadi Anda bisa menyalin byte yang masuk (antara STX / ETX) ke dalam buffer frame saat mereka tiba, tandai frame sebagai selesai dan lakukan pemrosesan dengan prioritas lebih rendah.
Rev1.0
@ TechnoSam: Poin bagus.
Rev1.0
5

Anda tahu, ASCII sudah memiliki byte untuk fungsi-fungsi ini.

  • 0x01: mulai dari heading - byte mulai
  • 0x02: mulai dari teks - header, mulai payload
  • 0x03: akhir teks - muatan akhir
  • 0x04: akhir transmisi - byte berhenti
  • 0x17: akhir blok transmisi - pesan berlanjut di blok berikutnya

Ini juga memiliki kode untuk berbagai kegunaan di dalam payload.

  • 0x1b: escape (escape the next character - gunakan dalam payload untuk menunjukkan karakter selanjutnya bukan salah satu struktur yang menggambarkan kode yang digunakan dalam protokol Anda)
  • 0x1c, 0x1d, 0x1e, 0x1f: file, grup, catatan, dan pemisah unit, masing-masing - digunakan sebagai stop dan start byte secara simultan untuk bagian-bagian data hierarkis

Protokol Anda harus menentukan rincian terbaik dari ACK (0x06) dan NAK (0x15), sehingga data yang diakui negatif dapat dikirim kembali. Turun ke granularity terbaik ini, adalah bijaksana untuk memiliki bidang panjang segera setelah indikator awal (unescaped) dan (seperti yang dijelaskan dalam jawaban lain) adalah bijaksana untuk mengikuti indikator berhenti (unescaped) dengan CRC.

Menara Eric
sumber
Saya akan mengirim data yang berubah-ubah, saya kira mungkin membingungkan untuk menggunakan "\ n \ r" dalam pertanyaan saya ketika saya tidak mengirim data ASCII. Meskipun, saya suka jawaban ini, sangat informatif dalam mengirim ASCII melalui UART
CK
@MariusGulbrandsen: Selama protokol Anda menetapkan di mana payload berada dan kode mana yang harus diloloskan di setiap bagian payload, Anda dapat mengirim apa pun, bukan hanya data teks-ish.
Eric Towers
4

Pada dasarnya, UART tidak aman dari kesalahan - kita berbicara tentang teknologi 1960-an di sini.

Akar masalahnya adalah bahwa UART hanya menyinkronkan sekali per 10 bit, memungkinkan banyak omong kosong untuk melewati antara periode sinkronisasi tersebut. Berbeda dengan misalnya BISA yang sampel setiap individu bit beberapa kali.

Setiap kesalahan bit ganda yang terjadi di dalam data akan merusak bingkai UART dan lulus tidak terdeteksi. Kesalahan bit dalam bit mulai / berhenti mungkin atau mungkin tidak terdeteksi dalam bentuk kesalahan overrun.

Oleh karena itu, tidak masalah jika Anda menggunakan data mentah atau paket, selalu ada kemungkinan bahwa bit yang disebabkan oleh EMI menghasilkan data yang tidak terduga.

Ada banyak cara "perdukunan UART tradisional" untuk memperbaiki situasi sedikit. Anda dapat menambahkan byte sinkronisasi, bit sinkronisasi, paritas, bit stop ganda. Anda bisa menambahkan checksum yang menghitung jumlah semua byte (dan kemudian membalikkannya - karena mengapa tidak) atau Anda bisa menghitung jumlah biner sebagai checksum. Semua ini banyak digunakan, sangat tidak ilmiah dan dengan kemungkinan besar kesalahan yang hilang. Tetapi inilah yang dilakukan orang-orang dari tahun 1960 hingga 1990-an dan banyak hal aneh seperti kehidupan ini pada hari ini.

Cara paling profesional untuk menangani transmisi yang aman melalui UART adalah memiliki checksum CRC 16 bit di akhir paket. Segala sesuatu yang lain tidak terlalu aman dan memiliki kemungkinan besar kesalahan yang hilang.

Kemudian pada tingkat perangkat keras Anda dapat menggunakan diferensial RS-422 / RS-485 untuk secara drastis meningkatkan kekasaran transmisi. Ini adalah suatu keharusan untuk transmisi yang aman jarak jauh. Level TTL UART hanya boleh digunakan untuk komunikasi on-board. RS-232 tidak boleh digunakan untuk tujuan lain tetapi kompatibilitas dengan hal-hal lama.

Secara keseluruhan, semakin dekat ke perangkat keras mekanisme deteksi kesalahan Anda, semakin efektif itu. Dalam hal efektivitas, sinyal diferensial menambah paling banyak, diikuti dengan memeriksa kesalahan pembingkaian / overrun dll CRC16 menambahkan beberapa, dan kemudian "perdukunan UART tradisional" menambahkan sedikit.

Lundin
sumber
7
Saran ini cukup tangensial - Anda belum benar-benar menjawab pertanyaan yang diajukan. Secara khusus, solusi yang Anda usulkan dapat memecahkan masalah lain, tetapi mereka tidak memecahkan masalah dasar dari pertanyaan di halaman ini , yang merupakan kebingungan antara bye framing dan bye payload. Paling-paling, proposal Anda akan menolak data yang valid menyematkan byte pembingkaian karena CRC atau kegagalan serupa, tanpa cara untuk mengomunikasikannya.
Chris Stratton
3
Faktanya, jawaban ini memperburuknya. Yang asli baru saja byte data dan berhenti byte. Ini menambahkan kategori ketiga, CRC byte. Dan seperti yang disajikan di sini, semua itu dapat mengambil nilai apa pun, termasuk {10,13}.
MSalters
1
@ MSalters: CRC dapat berupa ASCII hex yang disandikan untuk mencegah masalah ini. Trik lain yang saya lihat di RS485 adalah mengatur bit 7 pada byte start / address.
Transistor
Re "CAN yang sampel setiap individu bit beberapa kali." : Pengambilan sampel aktual dari nilai bit hanya sekali per bit. Apa yang Anda maksud di sini? Semacam pengecekan kesalahan, seperti oleh pengirim? Sinkronisasi jam?
Peter Mortensen
Pembalikan checksum dilakukan sehingga menjumlahkan seluruh blok data akan menghasilkan nol, yang sedikit lebih mudah untuk dikodekan dan sedikit lebih cepat untuk dieksekusi. Selain itu, CRC jauh lebih baik dari yang Anda bayangkan, lihat di Wikipedia.
toolforger
0

... Saya dapat membayangkan, meskipun probabilitasnya rendah, bahwa pesan saya mungkin berisi nilai "10 dan 13" satu sama lain ketika mereka bukan byte berhenti.

Situasi ketika sebagian data sama dengan terminating sequence harus dipertimbangkan ketika merancang format paket data serial. Hal lain yang perlu dipertimbangkan adalah bahwa karakter apa pun dapat rusak atau hilang selama transmisi. Karakter awal, karakter berhenti, byte payload data, checksum atau CRC byte, byte koreksi kesalahan maju tidak kebal terhadap korupsi. Mekanisme pembingkaian harus dapat mendeteksi ketika suatu paket memiliki data yang rusak.

Ada beberapa cara untuk mendekati semua ini.

Saya membuat asumsi yang berfungsi bahwa paket hanya dibingkai dengan byte serial. Garis jabat tangan tidak digunakan untuk membingkai. Penundaan waktu tidak digunakan untuk membingkai.

Kirim panjang paket

Kirimkan panjang paket di awal, alih-alih [atau sebagai tambahan] karakter penghentian di akhir.

pro: Muatan dikirim dalam format biner yang efisien.

Kontra: Perlu mengetahui panjang paket pada awal transmisi.

Melarikan diri dari karakter khusus

Keluar dari karakter khusus saat mengirim data payload. Ini sudah dijelaskan dalam jawaban sebelumnya .

pro: Pengirim tidak perlu tahu panjang paket di awal pengiriman.

kontra: Agak kurang efisien, tergantung pada berapa banyak byte muatan yang harus diloloskan.

Data payload dikodekan sedemikian sehingga tidak dapat memuat karakter awal dan berhenti

Payload paket dikodekan sedemikian sehingga tidak dapat berisi karakter awal atau berhenti. Biasanya, ini dilakukan dengan mengirimkan nomor sebagai representasi ASCII atau Hex-ASCII mereka.

pro: Dapat dibaca manusia dengan program terminal umum. Tidak perlu kode untuk menangani pelolosan. Tidak perlu tahu panjang paket di awal pengiriman

kontra: efisiensi yang lebih rendah. Untuk satu byte data payload, beberapa byte dikirim.

Nick Alexeev
sumber