Mengapa tidak selalu menggunakan DMA yang mendukung interupsi dengan UART pada STM32? [Tutup]

9

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.

Michel Keijzers
sumber
2
Kenapa tidak? Entah itu pertanyaan pendapat, seseorang yang mencari tebakan tentang alasan teknis yang memungkinkan, atau dengan cara yang sama terlalu luas, dan karenanya bukan pertanyaan yang termasuk di sini. Untuk menyebut contoh acak, DMA akan berarti lebih banyak latensi dalam mengklaim data, terutama karena Anda tidak mendapatkan manfaat nyata apa pun kecuali Anda mengizinkannya mengumpulkan banyak karakter. Seringkali itu mungkin baik-baik saja, kadang-kadang tidak.
Chris Stratton
6
Jika mendapatkan interupsi bekerja membutuhkan waktu berminggu-minggu, itu karena Anda mendekati tugas dengan cara yang salah; membuat DMA berfungsi dengan baik bisa memakan waktu lebih lama - sebenarnya ini adalah tugas yang lebih kompleks, jadi kemudahan nyata dari tugas yang lebih kompleks daripada yang lebih sederhana mungkin tergantung pada sumber daya yang Anda gunakan untuk panduan masing-masing, bukan mekanisme itu sendiri.
Chris Stratton
5
Jangan pernah berasumsi bahwa dma membebaskan cpu, kadang-kadang ya, cpu terus berjalan, terkadang tidak ada prosesor yang dibekukan untuk menahan bus untuk mesin dma. Sepele untuk melakukan ini dengan implementasi lengan, jadi tidak bisa hanya mengatakan bahwa semua lengan seperti ini dan semua x86 adalah seperti itu atau apa pun, tidak sesederhana itu, Anda harus selalu memeriksa desain sistem dan mungkin melakukan sedikit peretasan. Chip yang Anda miliki mungkin membebaskan inti lengan, ini hanya komentar di dma. Sejauh pertanyaan Anda, tidak masuk akal Anda tidak bisa mengikuti dan dma + int kemungkinan solusi lengkap jika Anda tidak bisa hanya polling.
old_timer
5
Interupsi cukup sepele pada port serial STM32F. Mengapa Anda tidak mengirim pertanyaan dengan kode Anda sehingga beberapa dari kami dapat mencoba untuk menemukan kesalahan Anda? Tidak pernah merupakan ide yang baik untuk meretas kode sampai berhasil tanpa memahami apa masalah yang mendasarinya.
Jon
7
Menurut pendapat saya yang sederhana (tidak begitu), ini adalah salah satu sisi dari penggunaan Cube yang mengerikan dan kembung. Tulis perangkat lunak dari awal, Anda akan belajar persis bagaimana UART bekerja (karena Anda harus), Anda akan memahami perangkat lebih baik dan dalam jangka panjang itu akan menghemat banyak waktu.
DiBosco

Jawaban:

24

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.

Jonas Schäfer
sumber
Terima kasih atas jawaban terinci Anda. MIDI akan menjadi bagian paling kritis, jadi saya kira DMA cocok untuk itu (meskipun kecepatan rendah: 31250 baud). Saya memiliki cukup saluran DMA, nanti saya akan menggunakan STM32 lain saat menggunakan 4 USART. Saya tidak perlu menunda CPU, karena akan memiliki daya USB 5V, dan saya perlu melakukan pemrosesan antar pesan (untuk memproses pesan di loop utama). Saya memiliki 256 byte membaca dan 256 byte mengirimkan buffer. Saya bisa menambahnya nanti jika perlu. STM32f103c8t6 memiliki 20 KB RAM, akhirnya STM yang akan saya gunakan memiliki 192 KB.
Michel Keijzers
Dan Anda memberi saya ide yang sangat bagus bagaimana meningkatkan. Sejauh ini saya selalu membaca 1 byte dan memeriksa terus menerus ketika pesan lengkap (MIDI) diterima. Tapi saya bisa membaca byte pertama, dan tergantung pada ukuran yang sebagian besar diketahui dan dapat meminta sisanya. Ini membuat saya kehilangan buffer kecil tapi tidak masalah.
Michel Keijzers
Membaca byte tunggal dengan DMA sangat tidak efisien. Untuk latensi yang lebih rendah dan efisiensi yang lebih tinggi, menggunakan interupsi per karakter hingga Anda mengetahui ukurannya dan kemudian beralih ke DMA akan menguntungkan.
Jonas Schäfer
Yah saya punya banyak masalah menggunakan interupsi (tanpa DMA), saya pikir saya akan menggunakan DMA 1 byte terima, dan setelah itu saya tahu berapa banyak byte yang saya harapkan dan melakukan permintaan DMA untuk mendapatkan lebih banyak.
Michel Keijzers
6
Itu mungkin kesalahan - Anda harus memperbaiki kode interupsi sederhana Anda, tanpa DMA.
Chris Stratton
10

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.

Dave Tweed
sumber
Apa yang saya lakukan adalah menerima 1 byte sekaligus (jadi buffer 'DMA' 1 byte) dan setelah setiap panggilan balik DMA satu byte itu, untuk menyimpannya dalam buffer cincin yang saya tangani secara manual. Dalam loop utama saya, saya bermaksud untuk memeriksa pesan MIDI lengkap dan memprosesnya.
Michel Keijzers
3
DMA biasanya digunakan untuk mendapatkan beberapa byte, dan hanya menyela ketika semuanya telah diterima. Mengganggu setelah hanya satu byte adalah normal ketika tidak menggunakan DMA, jadi itu membuat saya bertanya-tanya: apa gunanya komplikasi tambahan menggunakan DMA untuk itu?
Steve Melnikoff
5
@MichelKeijzers Lalu apa yang Anda lakukan adalah persis sama persis dengan yang Anda lakukan dalam implementasi murni interrupt-driven. Karenanya, tidak ada manfaatnya menggunakan DMA dalam kasus ini dan masalah awal Anda mungkin tidak diselesaikan oleh DMA tetapi dengan menulis ulang kode (ISR, pengaturan) Anda.
JimmyB
@ JimmyB ... terima kasih ... namun karena jawaban Jonas di bawah, saya akan membuat peningkatan untuk membaca banyak byte karena pesannya panjang. Saya tahu ini setelah menerima byte pertama (dalam banyak kasus). Daripada itu akan lebih bermanfaat untuk menggunakan DMA atas interupsi.
Michel Keijzers
8

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.

duskwuff -inactive-
sumber
Benar memang, mungkin hanya pada STM32 mekanisme interupsi (murni non DMA) agak canggung dibandingkan dengan DMA langsung.
Michel Keijzers
2
@duskwuff Tidak juga; Anda dapat polling untuk melihat kapan DMA selesai, dan Anda mungkin ingin melakukannya karena salah satu alasan utama untuk menggunakan DMA adalah tidak perlu repot dengan port serial sampai program Anda berada dalam keadaan di mana ia dapat bertindak pada yang diterima data. Atau untuk DMA keluar, Anda hanya dapat polling untuk melihat apakah mungkin untuk menambahkan lebih banyak ke buffer kirim.
Chris Stratton
1
@MichelKeijzers: IDK chip tertentu, tetapi biasanya alternatif untuk DMA tidak benar-benar terputus, itu diprogram-IO (di mana Anda menggunakan instruksi CPU untuk membaca / menulis data dari / ke register I / O). Dalam penangan interrupt, Anda biasanya akan melakukan satu pembacaan, dan kemudian mungkin lain dalam kasus karakter datang saat Anda membaca yang pertama, terutama jika itu tidak akan memicu interupsi yang lain. Atau baca sampai buffer internal kosong jika ada buffer seperti itu. Jelas Anda membutuhkan lebih banyak interupsi untuk PIO, dan mengaturnya secara berbeda.
Peter Cordes
@ ChrisStratton Poin bagus ... sejauh ini saya belum memeriksa apakah mungkin untuk mengirim, saya hanya mengirimkan sesuatu, tidak memeriksa apakah itu ok. Mungkin kalau tidak, saya coba lagi nanti.
Michel Keijzers
@PeterCordes Sepertinya STM32 memiliki cukup interupsi untuk DMA dan saya membaca setiap kali hanya 1 byte. Bahkan STM32 yang paling sederhana (F103c8t6) memiliki cukup port / interupsi DMA.
Michel Keijzers
5

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:

  • kapan saya menyela?
    • DMA hanya benar-benar suka mengganggu ketika mereka telah mentransfer sejumlah data yang telah ditetapkan.
    • Apa yang Anda lakukan jika Anda tidak pernah menerima data yang cukup untuk memicu interupsi DMA?
  • Bagaimana jika Anda hanya menerima sebagian pesan ketika DMA menyela?
  • Seperti apa bentuk buffer RX Anda? Apakah itu linear atau melingkar?
    • DMA dapat menjadi peserta penyangga sirkuler yang tidak dapat diatur dalam arti bahwa ia hanya mematuhi batas alamat, tetapi tidak memiliki masalah yang melintas melewati petunjuk lain dalam sistem buffer sirkular.

Pokoknya, hanya makanan untuk dipikirkan.

pgvoorhees
sumber
Terima kasih atas pertimbangannya. Saat ini saya selalu menerima 1 byte dan menyimpannya di buffer cincin, karena memang pesan saya (MIDI) dapat memiliki panjang yang berbeda dan saya tidak tahu yang mana saya dapatkan selanjutnya. Dalam loop utama saya, saya memeriksa pesan lengkap untuk memprosesnya (dan jika lengkap, saya menghapusnya dari buffer cincin). Jadi saya selalu akan menerima data yang cukup (kecuali saya akan melewatkan byte, saya harus memeriksanya). Buffer RX saya hanya 1 byte, tetapi saya salin ke buffer ring / melingkar. Saya tidak melakukan pengecekan jika sudah penuh (perlu menambahkannya).
Michel Keijzers
Hei, jangan khawatir. Saya yakin aplikasi Anda akan diprogram dengan baik. Seperti yang disebutkan orang lain, DMA itu hebat, tetapi tidak gratis itu saja. Ini memperkenalkan pertimbangan tambahan pada sistem yang tidak ada jika Anda bisa pergi tanpa menggunakannya.
pgvoorhees
baik saya harap, saya masih pemula.
Michel Keijzers
3

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.

Dean Franks
sumber
Saya memang menerima byte demi byte dan menyalinnya secara manual ke buffer cincin untuk memprosesnya nanti.
Michel Keijzers
1

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.

kamu
sumber
Saya melakukan hal yang persis sama (saya pikir). Saya membaca 1 byte pada suatu waktu dan menyimpannya dalam buffer siklik, dan saya bermaksud untuk memeriksa di loop utama untuk pesan lengkap. Dapat ditingkatkan sedikit sekalipun.
Michel Keijzers
Apakah Anda pikir saya mungkin mengalami masalah bahwa pengaturan DMA setiap kali akan membebani prosesor saya / karakter yang hilang pada 31.250 baud?
Michel Keijzers
1
Selama Anda mengatur DMA untuk mentransfer sejumlah karakter sekaligus, ini tidak akan menjadi masalah. Saya memiliki 4 UART yang menjalankan 115200 dan lebih tinggi dan I2C menggunakan DMA tanpa masalah. Transmisi UART semuanya ~ 20 byte atau lebih lama. Masalahnya adalah menggunakan DMA untuk menerima pada UART (prosesor L4 pada 80MHz, 9600baud).
u
Saat ini saya mengaturnya ke 1 byte pada suatu waktu, tetapi saya dapat memperbaikinya (dengan melakukan byte pertama, dan daripada memeriksa berapa banyak byte lebih lanjut yang diperlukan).
Michel Keijzers