Saya menghabiskan bulan lalu banyak waktu untuk mendapatkan UART (untuk MIDI) untuk bekerja dengan STM (STM32F103C8T6) menggunakan interupsi, tanpa terlalu banyak keberhasilan.
Namun, malam ini menggunakan DMA ternyata bekerja cukup cepat.
Karena sejauh yang saya baca DMA lebih cepat dan mengurangi CPU, mengapa tidak selalu menggunakan DMA yang mendukung interupsi? Terutama karena pada STM32 tampaknya ada beberapa masalah.
Saya menggunakan STM32CubeMx / HAL.
uart
dma
stm32cubemx
stm32f103c8t6
Michel Keijzers
sumber
sumber
Jawaban:
Sementara DMA mengurangi CPU dan dengan demikian dapat mengurangi latensi aplikasi-aplikasi berbasis-interupsi yang berjalan pada inti yang sama, ada biaya yang terkait dengannya:
Hanya ada jumlah saluran DMA yang terbatas dan ada batasan tentang bagaimana saluran-saluran itu dapat berinteraksi dengan periferal yang berbeda. Perangkat lain pada saluran yang sama mungkin lebih cocok untuk penggunaan DMA.
Misalnya, jika Anda memiliki transfer I2C massal setiap 5ms, ini sepertinya kandidat yang lebih baik untuk DMA daripada perintah debug sesekali yang tiba di UART2.
Menyiapkan dan memelihara DMA adalah biaya dengan sendirinya. (Biasanya, pengaturan DMA dianggap lebih kompleks daripada pengaturan transfer antar-drive normal per-karakter, karena manajemen memori, lebih banyak periferal yang terlibat, DMA menggunakan interupsi sendiri dan kemungkinan Anda perlu mengurai beberapa karakter pertama di luar DMA Bagaimanapun, lihat di bawah.)
DMA dapat menggunakan daya tambahan , karena itu adalah domain lain dari inti yang perlu di-clock. Di sisi lain, Anda dapat menangguhkan CPU saat transfer DMA sedang berlangsung, jika inti mendukungnya.
DMA membutuhkan buffer memori untuk bekerja dengan (kecuali Anda melakukan DMA periferal), sehingga ada beberapa biaya memori yang terkait dengannya.
(Biaya memori mungkin juga ada ketika menggunakan interupsi per karakter, tetapi mungkin juga saya jauh lebih kecil atau lenyap sama sekali jika pesan ditafsirkan langsung di dalam interupsi.)
DMA menghasilkan latensi karena CPU hanya akan diberitahukan ketika transfer selesai / setengah selesai (lihat jawaban lainnya).
Kecuali saat mengalirkan data ke / dari buffer cincin, Anda harus tahu sebelumnya berapa banyak data yang akan Anda terima / kirim.
Ini mungkin berarti bahwa diperlukan untuk memproses karakter pertama dari pesan menggunakan interupsi per karakter: misalnya, ketika berinteraksi dengan XBee, Anda pertama-tama akan membaca jenis dan ukuran paket dan kemudian memicu transfer DMA ke buffer yang dialokasikan.
Untuk protokol lain, ini mungkin tidak mungkin sama sekali, jika mereka hanya menggunakan pembatas akhir pesan: misalnya, protokol berbasis teks yang digunakan
'\n'
sebagai pembatas. (Kecuali jika perangkat DMA mendukung pencocokan pada karakter.)Seperti yang Anda lihat, ada banyak pertukaran untuk dipertimbangkan di sini. Beberapa terkait dengan keterbatasan perangkat keras (jumlah saluran, konflik dengan periferal lain, pencocokan karakter), beberapa didasarkan pada protokol yang digunakan (pembatas, panjang yang diketahui, buffer memori).
Untuk menambahkan beberapa bukti anekdotal, saya telah menghadapi semua pertukaran ini dalam sebuah proyek hobi yang menggunakan banyak perangkat berbeda dengan protokol yang sangat berbeda. Ada beberapa trade-off yang harus dibuat, sebagian besar didasarkan pada pertanyaan "berapa banyak data yang saya transfer dan seberapa sering saya akan melakukan itu?". Ini pada dasarnya memberi Anda perkiraan kasar tentang dampak transfer interrupt-driven sederhana pada CPU. Dengan demikian saya memberikan prioritas pada transfer I2C tersebut di atas setiap 5ms selama transfer UART setiap beberapa detik yang menggunakan saluran DMA yang sama. Transfer UART lain terjadi lebih sering dan dengan lebih banyak data di sisi lain mendapat prioritas daripada transfer I2C lain yang lebih jarang terjadi. Ini semua kompromi.
Tentu saja, menggunakan DMA juga memiliki kelebihan, tapi bukan itu yang Anda minta.
sumber
Menggunakan DMA biasanya berarti Anda tidak lagi mengambil interupsi pada setiap karakter, tetapi hanya setelah "buffer penuh" karakter diterima (atau ditransmisikan). Ini meningkatkan latensi pemrosesan karakter-karakter tersebut - karakter pertama tidak diproses hingga setelah karakter terakhir dalam buffer diterima.
Latensi ini dapat menjadi hal yang buruk, terutama dalam aplikasi yang sensitif terhadap latensi seperti MIDI, di mana beberapa ms di sana-sini dapat menambah masalah pemutaran serius untuk pertunjukan live.
sumber
DMA bukan pengganti interupsi - mereka biasanya digunakan bersama-sama! Jika Anda menggunakan DMA untuk mengirim data melalui UART, misalnya, Anda masih perlu interupsi untuk memberi tahu Anda ketika pengiriman selesai.
sumber
Menggunakan DMA memperkenalkan beberapa pertanyaan dan tantangan menarik di luar semua pertimbangan lain tentang penggunaan perangkat UART. Saya akan memberi Anda beberapa contoh: Asumsikan bahwa UC Anda duduk di bus RS485 (atau apa pun) dengan perangkat lain. Ada banyak pesan di bus, beberapa ditujukan untuk UC Anda, beberapa tidak. Selain itu berasumsi bahwa tetangga bus ini semua berbicara protokol data yang berbeda, yang menyiratkan panjang pesan berbeda.
Beberapa pertanyaan yang hanya muncul saat menggunakan DMA adalah:
Pokoknya, hanya makanan untuk dipikirkan.
sumber
Di sisi penerimaan (seingat saya) DMA berakhir baik pada pertandingan karakter atau pada hitungan terminal. Beberapa protokol dan banyak aplikasi interaktif tidak mudah masuk ke dalam model ini dan Anda benar-benar perlu menangani berbagai hal karakter demi karakter. Teknik DMA juga bisa rapuh jika tautan komunikasi tidak dapat diandalkan, kehilangan satu karakter dalam aliran dapat dengan mudah mengacaukan mesin status DMA Anda.
sumber
Saya telah menggunakan STM32CubeMx / HAL pada beberapa proyek sekarang dan telah menemukan bahwa perangkat lunak penanganan UART yang dihasilkannya memiliki kekurangan yang pasti di sisi penerima.
Saat mengirim, Anda biasanya ingin mengirim blok data atau baris teks. Dalam hal ini Anda tahu di muka berapa lama transfer data dan menggunakan DMA adalah solusi yang jelas. Anda mendapatkan interupsi setelah transfer selesai dan dapat menggunakan fungsi callback UART TX lengkap untuk menunjukkan kode utama Anda bahwa transmisi selesai dan Anda dapat mengirim blok data lain.
Dalam hal penerimaan data, fungsi-fungsi yang disediakan oleh ST semuanya berasumsi bahwa Anda tahu berapa banyak karakter yang akan diberikan perangkat pengirim sebelum mulai mengirim. Biasanya ini tidak diketahui. Fungsi interupsi menempatkan data yang diterima ke dalam buffer dan hanya menunjukkan bahwa ada data yang tersedia ketika jumlah karakter yang telah ditentukan telah diterima. Jika Anda mencoba menggunakan DMA atau fungsi interupsi untuk menerima data dengan mengatur transfer karakter tunggal berurutan maka waktu setup untuk masing-masing ini akan berarti bahwa Anda akan kehilangan karakter pada apa pun selain kecepatan data paling lambat (kecepatan baud yang akan Anda gunakan). mulai kehilangan data akan tergantung pada kecepatan jam prosesor Anda) dan akan memuat prosesor secara berlebihan, tanpa meninggalkan siklus instruksi untuk pemrosesan lainnya
Untuk mengatasinya, saya telah menulis fungsi interrupt handler saya sendiri yang menyimpan data dalam buffer bundar lokal kecil dan menetapkan hitungan yang dibaca oleh kode utama (penghitungan RTOS semaphore) untuk menunjukkan bahwa ada data yang diterima siap. Kode utama kemudian dapat mengumpulkan data dari buffer ini pada waktu luang, tidak masalah jika ada beberapa keterlambatan dalam mengumpulkan data dengan ketentuan bahwa buffer lokal tidak meluap sebelum data dikumpulkan.
sumber