Beberapa hari yang lalu saya kurang bijaksana berjanji untuk memotong posting tentang Milander ... Baiklah, mari kita coba.
Seperti yang mungkin sudah Anda ketahui, ada perusahaan Rusia Milander, yang, antara lain, memproduksi mikrokontroler berbasis pada inti ARM Cortex-M. Dengan kehendak takdir, aku terpaksa mengenal mereka dengan cukup erat, dan aku
tahu sakitnya .
Sebagian kecil dari rasa sakit ini yang disebabkan oleh bekerja dengan RS-485 dijelaskan di bawah ini. Saya minta maaf sebelumnya jika saya terlalu banyak mengunyah konsep-konsep dasar, tetapi saya ingin membuat artikel ini dapat diakses oleh pemahaman khalayak yang lebih luas.
Saya juga akan membuat reservasi terlebih dahulu bahwa saya hanya berurusan dengan 1986 91 dan 1986 ,1, saya tidak bisa dengan percaya diri berbicara tentang orang lain.
TL DRMilandrovsk UART tidak memiliki interupsi "Transmit complete", kruk adalah "mode tes loopback", mis. mode gema. Namun dengan nuansa.
Entri
Antarmuka RS-485 (juga dikenal sebagai EIA-485, meskipun saya belum pernah mendengarnya dalam kehidupan sehari-hari) adalah antarmuka setengah-dupleks asinkron dengan topologi bus. Standar ini hanya menetapkan fisika - yaitu level tegangan dan diagram waktu - tetapi tidak menentukan protokol pertukaran, perlindungan terhadap kesalahan transmisi, arbitrasi, dan sejenisnya.
Faktanya, RS-485 hanyalah UART setengah dupleks dengan level tegangan diferensial tinggi. Kesederhanaan inilah yang memastikan popularitas RS-485.
Untuk mengonversi UART ke RS-485, sirkuit mikro konverter khusus digunakan, seperti MAX485 atau 5559IN10AU (dari Milander yang sama). Mereka bekerja hampir "secara transparan" untuk programmer, yang hanya dapat memilih mode operasi chip yang benar - penerimaan atau transmisi. Ini dilakukan dengan menggunakan kaki nRE (bukan Receiver Output Enable) dan DE (Driver Output Enable), yang, sebagai suatu peraturan, digabungkan dan dikendalikan oleh satu kaki mikrokontroler.
Menaikkan kaki ini akan mengalihkan chip ke transmisi, dan menurunkannya ke penerimaan.
Dengan demikian, semua yang diperlukan oleh programmer adalah untuk meningkatkan leg RE-DE ini, mentransfer jumlah byte yang diinginkan, menurunkan leg dan menunggu jawaban. Kedengarannya cukup sederhana, bukan?
Hehe.
Masalah
Kaki ini harus diturunkan pada saat semua byte yang ditransmisikan sepenuhnya ditransfer ke saluran. Bagaimana cara menangkap momen ini? Untuk melakukan ini, Anda perlu menangkap peristiwa "Transmit complete" (transmisi selesai), yang menghasilkan blok UART dalam mikrokontroler. Sebagian besar, acara diatur sedikit dalam beberapa register atau permintaan interupsi. Untuk mengetahui pengaturan bit dalam register, register harus disurvei, mis. gunakan kode seperti ini:
while( MDR_UART1->FR & UART_FR_BUSY ) {;}
Ini jika kita mampu menghentikan program sampai semua byte ditransfer. Sebagai aturan, kami tidak mampu membayar ini.
Gangguan dalam hal ini jauh lebih nyaman, karena tiba dengan sendirinya, tidak sinkron. Dalam gangguan, kita dapat dengan cepat menghilangkan RE-DE dan seluruh bisnis.
Tentu saja, jika kita bisa melakukan ini, tidak akan ada rasa sakit, dan pos ini juga tidak akan ada.
Faktanya adalah bahwa di blok UART, yang Milander masukkan ke semua mikrokontrolernya di Cortex-M (sejauh yang saya tahu), tidak ada gangguan untuk acara "Transfer Lengkap". Hanya ada bendera. Dan ada interupsi "Buffer pemancar kosong." Dan byte mengganggu, tentu saja.
Masih adasekelompok gangguan lain dan mode FIFO, menurut saya, sama sekali tidak berguna. Jika ada yang mengerti mengapa dia dibutuhkan, tolong beri tahu kami!
Masalahnya adalah “Buffer Transmisi Kosong” - ini sama sekali tidak sama dengan “Transmisi Lengkap”. Sejauh yang saya mengerti perangkat UART internal, acara "Buffer Empty" berarti bahwa setidaknya ada satu ruang kosong di buffer pemancar. Bahkan jika tempat ini hanya satu (mis., Buffer dengan ukuran satu byte), itu hanya berarti bahwa byte yang ditransmisikan disalin ke register geser internal, dari mana byte ini akan merayap keluar ke garis, sedikit demi sedikit.
Singkatnya, acara "buffer transmitter is empty" tidak berarti bahwa semua byte telah sepenuhnya ditransmisikan. Jika kita mengabaikan RE-DE saat ini, kita akan "memotong" paket kita.
Apa yang harus dilakukan
Rebus
Decoding:“Weeding bit fields” adalah meme lokal dari topik pendek tapi menyakitkan di forum
Milander -
forum.milandr.ru/viewtopic.php?f=33&t=626 .
Solusi paling sederhana adalah dengan menandai "gulma" (dari bahasa Inggris "jajak pendapat" - jajak pendapat berkelanjutan) UART_FR_BUSY.
Tentu saja, solusi ini tidak terlalu baik. Jika kami tidak dapat memblokir bendera ini, kami harus memeriksanya secara berkala. Untuk memeriksanya secara berkala, Anda harus memagari seluruh taman (terutama jika Anda ingin menulis modul portabel, dan tidak hanya menyelesaikan masalah ini satu kali).
Jika Anda menggunakan semacam RTOS, maka demi penyiangan ini Anda harus memulai seluruh tugas yang terpisah, membangunkannya dalam gangguan, mengaturnya bukan prioritas terendah, tidak repot, singkatnya.
Tapi, sepertinya, oke, tersiksa sekali, lalu kita gunakan dan bersukacitalah. Tapi tidak.
Sayangnya, tidak cukup bagi kita untuk mengabaikan RE-DE secara ketat setelah semua byte dikirimkan ke bagian akhir. Kita harus menurunkannya
tidak terlambat . Karena kita tidak sendirian di dalam bus. Kemungkinan besar, semacam jawaban dari pelanggan lain harus datang ke pesan kami. Dan jika kita menghilangkan RE-DE terlambat, kita tidak akan beralih untuk menerima mode dan kehilangan beberapa bit respons.
Waktu yang kita mampu untuk "mengekspos berlebihan" kaki RE-DE terutama tergantung pada kecepatan transmisi (baud rate) dan pada kecepatan perangkat yang kita gunakan untuk berkomunikasi di dalam bus.
Dalam kasus saya, kecepatannya relatif rendah (57600 baud), dan perangkatnya cukup lincah. Terkadang jawabannya hilang sedikit atau dua.
Secara keseluruhan, bukan solusi yang baik.
Timer
Opsi kedua yang terlintas dalam pikiran adalah menggunakan pengatur waktu perangkat keras. Kemudian, dalam interupsi "Transmitter Buffer Empty", kita memulai timer dengan batas waktu yang sama dengan waktu transmisi satu byte (waktu ini mudah dihitung dari baudrate), dan dalam interupsi dari timer, turunkan kaki kita.
Bagus, cara andal. Hanya timer yang disayangkan; Secara tradisional tidak banyak dari mereka di Milandra - dua atau tiga potong.
Mode putaran
Jika Anda hati-hati membaca itu. deskripsi tentang UART -
misalnya, untuk 1986 BE91T - Anda dapat melihat paragraf yang sangat singkat ini:
( ) 1 LBE UARTCR.
Jika itu. Jika Anda tidak membaca uraian, maka efek yang hampir sama dapat dicapai dengan mempersingkat kaki perangkat keras RX dan TX.
Pikiran dengan kerasMenariknya, di mana ini semacam lingkaran? Biasanya mode ini disebut "echo", tetapi oh well.
Idenya adalah sebagai berikut - sebelum mengirimkan byte terakhir dalam paket, Anda perlu mengaktifkan mode "loopback". Kemudian Anda bisa mendapatkan interupsi karena menerima byte terakhir kami sendiri pada saat itu sepenuhnya merangkak keluar ke bus! Yah, hampir.
Dalam prakteknya, ternyata interupsi pada penerimaan dipicu
sedikit lebih awal dari seharusnya, sekitar sepertiga dari interval bit. Saya tidak tahu apa hubungannya ini; ada kemungkinan bahwa dalam mode tes loop tidak ada sampling nyata dari garis terjadi, mungkin mode loop tidak memperhitungkan bit perhentian terakhir. Saya tidak tahu. Bagaimanapun, kita tidak dapat menghilangkan RE-DE segera setelah memasuki interupsi ini, karena ini adalah bagaimana kita "memotong" bit stop atau bagian dari bit stop dari byte terakhir kami.
Sebenarnya, kita bisa atau tidak bisa bergantung pada rasio kecepatan antarmuka (mis., Durasi satu interval bit) dan frekuensi mikrokontroler, tetapi saya tidak bisa mendapatkan frekuensi 80 MHz dengan kecepatan baud 57600.
Opsi lebih lanjut dimungkinkan.
Jika Anda dapat melakukan polling pada flag UART_FR_BUSY untuk satu interval bit - pada kenyataannya, bahkan sedikit kurang, karena memasukkan interupsi dan pemeriksaan pendahuluan juga membutuhkan waktu - maka solusinya ditemukan. Untuk kecepatan 57600, waktu pemungutan suara maksimum akan ~ 18 mikrodetik (interval satu bit), dalam praktiknya - sekitar 5 mikrodetik.
Bagi mereka yang tertarik, saya mengutip seluruh kode interrupt handler. void Handle :: irqHandler(void) { UMBA_ASSERT( m_isInited ); m_irqCounter++;
Jika Anda mampu membeli jumper (dikendalikan secara ideal) antara kaki-kaki RX dan TX, maka semuanya baik-baik saja.
Sayangnya, hari ini saya tidak bisa menawarkan opsi lain.
Itu semua untuk saya. Jika ada yang tahu cara lain untuk menyelesaikan masalah ini, silakan bagikan di komentar.
Juga, mengambil kesempatan dan mengubah aturan Habr, saya ingin mempromosikan situs web StartMilandr, yang merupakan kumpulan artikel tentang mikrokontroler Milander. Untuk alasan yang tidak jelas, Anda dapat menggunakan google hanya secara tidak sengaja.
Dan, tentu saja, ingat keberadaan
garpu perpustakaan periferal standar, di mana, tidak seperti perpustakaan resmi, bug diperbaiki dan ada dukungan gcc.