Dlaczego nie zawsze używać DMA na rzecz przerw w UART na STM32? [Zamknięte]

9

W zeszłym miesiącu spędziłem dużo czasu, zmuszając UART (dla MIDI) do pracy z STM (STM32F103C8T6) przy użyciu przerwań, bez większego powodzenia.

Jednak tego wieczoru przy użyciu DMA działało dość szybko.

Ponieważ o ile czytam DMA jest szybszy i odciąża procesor, dlaczego nie zawsze używać DMA na rzecz przerwań? Zwłaszcza, że ​​na STM32 wydają się być pewne problemy.

Używam STM32CubeMx / HAL.

Michel Keijzers
źródło
2
Dlaczego nie? Jest to albo kwestia opinii, szukająca zgadnięcia, który możliwy powód techniczny, albo w ten sam sposób zbyt szeroki, a zatem nie pytanie, które tu należy. Aby nazwać przypadkowy przykład, DMA będzie oznaczać większe opóźnienie w żądaniu danych, zwłaszcza, że ​​nie uzyskasz żadnej realnej korzyści, chyba że pozwolisz, aby zgromadziło wiele znaków. Często może być w porządku, a czasem może nie.
Chris Stratton
6
Jeśli uzyskiwanie przerw w pracy trwało tygodnie, to dlatego, że podeszliście do zadania w niewłaściwy sposób; uruchomienie DMA może zająć więcej czasu - jest to w rzeczywistości bardziej złożone zadanie, więc pozorna łatwość bardziej złożonego zadania w porównaniu z prostszym przypuszczalnie sprowadza się do zasobów, z których korzystałeś przy prowadzeniu każdego z nich, a nie samego mechanizmu.
Chris Stratton
5
Nigdy nie zakładaj, że dma zwalnia procesor, czasami tak, procesor działa dalej, czasem żaden procesor nie jest zamrożony, aby utrzymać magistralę dla silnika dma. Trywialne, aby to zrobić za pomocą implementacji uzbrojenia, więc nie mogę po prostu powiedzieć, że wszystkie ramiona są w ten sposób, a wszystkie x86 są w ten sposób, czy cokolwiek innego, nie jest to takie proste, zawsze musisz sprawdzić projekt systemu i być może zrobić trochę hakowania. Chip, który masz, może bardzo zwolnić rdzeń ramienia, to tylko komentarz na temat dma. Jeśli chodzi o twoje pytanie, nie ma sensu nie nadążać, a dma + int jest prawdopodobnie pełnym rozwiązaniem, jeśli nie możesz po prostu sondować.
old_timer
5
Przerwania są dość trywialne na porcie szeregowym STM32F. Dlaczego nie opublikujesz pytania za pomocą swojego kodu, aby niektórzy z nas mogli spróbować dowiedzieć się, gdzie popełnisz błąd? Hakowanie kodu nigdy nie jest dobrym pomysłem, dopóki nie zadziała bez zrozumienia, na czym polega problem.
Jon
7
Moim (nie tak skromnym) zdaniem jest to jedna z wad korzystania z okropnej, rozdętej Kostki. Napisz oprogramowanie od zera, nauczysz się dokładnie, jak działa UART (bo musisz), zrozumiesz znacznie urządzenia peryferyjne, a na dłuższą metę pozwoli ci to zaoszczędzić dużo czasu.
DiBosco,

Odpowiedzi:

24

Chociaż DMA odciąża procesor, a tym samym może zmniejszyć opóźnienia innych aplikacji sterowanych przerwaniami działających na tym samym rdzeniu, wiążą się z tym koszty:

  • Liczba kanałów DMA jest ograniczona i istnieją ograniczenia w interakcji tych kanałów z różnymi urządzeniami peryferyjnymi. Inne urządzenie peryferyjne na tym samym kanale może być bardziej odpowiednie do użytku w DMA.

    Na przykład, jeśli masz masowy transfer I2C co 5 ms, wydaje się to lepszym kandydatem do DMA niż okazjonalne polecenie debugowania przychodzące na UART2.

  • Założenie i utrzymanie DMA jest kosztem samo w sobie. (Zwykle konfigurowanie DMA jest uważane za bardziej skomplikowane niż konfigurowanie normalnego transferu sterowanego przerwaniami na znak, ze względu na zarządzanie pamięcią, więcej zaangażowanych urządzeń peryferyjnych, używanie samego przerwania przez DMA i możliwość przeanalizowania kilku pierwszych znaków poza DMA tak czy inaczej, patrz poniżej.)

  • DMA może wykorzystywać dodatkową moc , ponieważ jest to kolejna dziedzina rdzenia, która wymaga taktowania. Z drugiej strony możesz zawiesić procesor podczas transferu DMA, jeśli rdzeń to obsługuje.

  • DMA wymaga współpracy buforów pamięci (chyba że wykonujesz DMA na urządzenia peryferyjne), więc wiąże się z tym pewien koszt pamięci.

    (Koszt pamięci może również występować przy stosowaniu przerwań dla poszczególnych znaków, ale może też być znacznie mniejszy lub w ogóle zniknąć, jeśli wiadomości zostaną natychmiast zinterpretowane w przerwaniu).

  • DMA powoduje opóźnienie, ponieważ procesor jest powiadamiany tylko wtedy, gdy transfer jest zakończony / w połowie ukończony (zobacz pozostałe odpowiedzi).

  • Z wyjątkiem przesyłania strumieniowego danych do / z bufora pierścieniowego, musisz wcześniej wiedzieć, ile danych będziesz otrzymywać / wysyłać.

    • Może to oznaczać, że konieczne jest przetworzenie pierwszych znaków wiadomości przy użyciu przerwań dla poszczególnych znaków: na przykład podczas łączenia się z XBee najpierw należy odczytać typ i rozmiar pakietu, a następnie uruchomić transfer DMA do przydzielonego bufora.

    • W przypadku innych protokołów może to wcale nie być możliwe, jeśli używają one tylko ograniczników końca wiadomości: na przykład protokołów tekstowych, które używają '\n'jako separatora. (Chyba że urządzenie peryferyjne DMA obsługuje dopasowanie znaku).

Jak widać, istnieje tutaj wiele kompromisów do rozważenia. Niektóre są związane z ograniczeniami sprzętowymi (liczba kanałów, konflikty z innymi urządzeniami peryferyjnymi, dopasowanie do znaków), niektóre oparte są na stosowanym protokole (ograniczniki, znana długość, bufory pamięci).

Aby dodać trochę niepotwierdzonych dowodów, spotkałem się z tymi wszystkimi kompromisami w projekcie hobbystycznym, który wykorzystywał wiele różnych urządzeń peryferyjnych z bardzo różnymi protokołami. Były pewne kompromisy, w większości oparte na pytaniu „ile danych przesyłam i jak często to robię?”. Zasadniczo daje to przybliżoną ocenę wpływu prostego transferu sterowanego przerwaniami na procesor. W ten sposób nadałem priorytet wyżej wymienionemu transferowi I2C co 5 ms w stosunku do transferu UART co kilka sekund, które korzystały z tego samego kanału DMA. Kolejny transfer UART, który ma miejsce częściej i przy większej ilości danych, ma priorytet w stosunku do kolejnego transferu I2C, co zdarza się rzadziej. To wszystko kompromisy.

Oczywiście korzystanie z DMA ma również zalety, ale nie o to prosiłeś.

Jonas Schäfer
źródło
Dziękuję za szczegółową odpowiedź. MIDI będzie najważniejszą częścią, więc myślę, że DMA jest do tego odpowiedni (chociaż prędkość jest niska: 31250 bodów). Mam wystarczającą liczbę kanałów DMA, później użyję innego STM32 przy użyciu 4 USART. Nie muszę zawieszać procesora, ponieważ będzie miał zasilanie USB 5 V, i muszę wykonać przetwarzanie między wiadomościami (w celu przetworzenia wiadomości w głównej pętli). Mam 256 bajtów odczytu i 256 bajtów bufora transmisji. W razie potrzeby mogę go później zwiększyć. STM32f103c8t6 ma 20 KB pamięci RAM, docelowy STM, którego użyję, ma 192 KB.
Michel Keijzers,
I dajesz mi bardzo dobry pomysł, jak poprawić. Do tej pory zawsze czytam 1 bajt i ciągle sprawdzam, kiedy otrzymano kompletny komunikat (MIDI). Ale potrafię odczytać pierwszy bajt i zależnie od tego w większości znany jest rozmiar i mogę poprosić o resztę. Kosztowało mnie to kolejny mały bufor, ale to w porządku.
Michel Keijzers
Odczytywanie pojedynczych bajtów za pomocą DMA jest bardzo nieefektywne. Dla mniejszych opóźnień i wyższej wydajności korzystniejsze będzie używanie przerwań dla poszczególnych znaków, dopóki nie poznasz rozmiaru, a następnie przejście na DMA.
Jonas Schäfer
Cóż, miałem wiele problemów z używaniem przerwań (bez DMA), myślę, że użyję 1-bajtowego odbioru DMA, a potem wiem, ile bajtów się spodziewam i wykonuję żądanie DMA, aby uzyskać więcej.
Michel Keijzers,
6
To prawdopodobnie błąd - powinieneś naprawić swój prosty kod przerwania, bez DMA.
Chris Stratton,
10

Korzystanie z DMA zwykle oznacza, że ​​nie przestajesz już przerywać każdej postaci, ale dopiero po otrzymaniu (lub przesłaniu) „pełnego buforu” znaków. Zwiększa to opóźnienie przetwarzania tych znaków - pierwszy znak jest przetwarzany dopiero po otrzymaniu ostatniego znaku w buforze.

Opóźnienie to może być złą rzeczą, szczególnie w aplikacjach wrażliwych na opóźnienia, takich jak MIDI, gdzie kilka ms tutaj i tam może przyczynić się do poważnych problemów z odtwarzaniem występów na żywo.

Dave Tweed
źródło
To, co robię, to otrzymywanie 1 bajtu na raz (czyli 1-bajtowy bufor „DMA”) i po każdym wywołaniu zwrotnym DMA tego jednego bajtu, aby zapisać go w buforze pierścieniowym, który obsługuję ręcznie. W mojej głównej pętli zamierzam sprawdzać kompletne komunikaty MIDI i przetwarzać je.
Michel Keijzers,
3
DMA jest zwykle używany do pobierania wielu bajtów i przerywa tylko, gdy wszystkie zostaną odebrane. Przerwanie po jednym bajcie jest normalne, gdy nie używa się DMA, więc zastanawiam się: po co jest dodatkowa komplikacja używania DMA do tego?
Steve Melnikoff,
5
@MichelKeijzers To, co robisz, jest dokładnie takie samo, jak w implementacjach opartych na przerwaniu. Dlatego korzystanie z DMA w tym przypadku nie przynosi żadnych korzyści, a Twój pierwotny problem prawdopodobnie nie został rozwiązany przez DMA, ale przez przepisanie kodu (ISR, konfiguracja).
JimmyB,
@ JimmyB ... dzięki ... jednak z powodu odpowiedzi Jonasa poniżej, poprawię czytanie tak wielu bajtów, ponieważ wiadomość jest długa. Wiem to po otrzymaniu pierwszego bajtu (w większości przypadków). Dzięki temu korzystanie z DMA będzie bardziej korzystne niż przerwań.
Michel Keijzers,
8

DMA nie zastępuje przerwań - zwykle są używane razem! Jeśli na przykład używasz DMA do wysyłania danych przez UART, nadal potrzebujesz przerwy, aby poinformować Cię, kiedy wysyłanie jest zakończone.

Duskwuff -inactive-
źródło
To prawda, że ​​może tylko na STM32 mechanizm przerwań (czysto nie DMA) jest nieco niezdarny w porównaniu do bezpośredniego DMA.
Michel Keijzers,
2
@duskwuff Nie bardzo; możesz sondować, aby zobaczyć, kiedy DMA jest gotowe, i możesz chcieć, ponieważ jednym z kluczowych powodów korzystania z DMA jest to, że nie musisz martwić się portem szeregowym, dopóki program nie znajdzie się w stanie, w którym będzie mógł działać na odebranym dane. Lub w przypadku wychodzącej DMA możesz po prostu sondować, czy możliwe jest dodanie więcej do bufora wysyłania.
Chris Stratton,
1
@MichelKeijzers: IDK konkretny układ, ale zwykle alternatywą dla DMA nie jest dosłownie przerwanie, to programowane IO (gdzie używasz instrukcji procesora do odczytu / zapisu danych z / do rejestru I / O). W module obsługi przerwań zwykle robiłbyś jeden odczyt, a potem może inny, na wypadek gdyby znak pojawił się podczas czytania pierwszego, szczególnie jeśli nie spowoduje to kolejnego przerwania. Lub czytaj, dopóki wewnętrzny bufor nie będzie pusty, jeśli taki bufor istnieje. Oczywiście potrzebujesz więcej przerwań dla PIO i skonfiguruj je inaczej.
Peter Cordes,
@ChrisStratton Dobra uwaga ... jak dotąd nie sprawdziłem, czy można nadawać, po prostu coś przesyłam, nie sprawdzając, czy jest w porządku. Prawdopodobnie jeśli nie, spróbuję ponownie później.
Michel Keijzers,
@PeterCordes Wygląda na to, że STM32 ma wystarczająco dużo przerwań dla DMA i czytam za każdym razem tylko 1 bajt. Nawet najprostszy STM32 (F103c8t6) ma wystarczającą liczbę dostępnych portów / przerwań DMA.
Michel Keijzers,
5

Korzystanie z DMA wprowadza kilka interesujących pytań i wyzwań wykraczających poza wszelkie inne względy związane z wykorzystaniem urządzeń peryferyjnych UART. Podam wam kilka przykładów: Załóżmy, że twój uC siedzi na magistrali RS485 (lub cokolwiek innego) z innymi urządzeniami. W autobusie jest wiele wiadomości, niektóre są przeznaczone dla twojego UC, niektóre nie. Dodatkowo załóżmy, że wszyscy sąsiedzi magistrali mówią innym protokołem danych, co oznacza, że ​​długości komunikatów są różne.

Oto niektóre pytania, które pojawiają się tylko podczas korzystania z DMA:

  • kiedy mam przerwać?
    • DMA naprawdę lubią przeszkadzać tylko wtedy, gdy przesłały wcześniej określoną ilość danych.
    • Co robisz, jeśli nigdy nie otrzymujesz wystarczającej ilości danych, aby wywołać przerwanie DMA?
  • Co się stanie, jeśli otrzymasz tylko częściową wiadomość, gdy DMA przerwie?
  • Jak wyglądają Twoje bufory RX? Czy są liniowe czy okrągłe?
    • DMA może być niesfornym uczestnikiem bufora cyklicznego w tym sensie, że tylko przestrzega granicy adresu, ale nie ma problemu z przedmuchaniem innych wskaźników w systemie bufora cyklicznego.

W każdym razie po prostu jedzenie do namysłu.

pgvoorhees
źródło
Dzięki za te rozważania. Obecnie zawsze otrzymuję 1 bajt i przechowuję go w buforze pierścieniowym, ponieważ rzeczywiście moje wiadomości (MIDI) mogą mieć różne długości i nie wiem, co otrzymam. W mojej głównej pętli sprawdzam, czy są kompletne komunikaty do ich przetworzenia (a jeśli są kompletne, usuwam je z bufora pierścieniowego). Dlatego zawsze otrzymam wystarczającą ilość danych (chyba że przegapię bajty, muszę to sprawdzić). Mój bufor RX ma tylko 1 bajt, ale kopiuję go do bufora pierścieniowego / okrągłego. Nie sprawdziłem, czy jest pełny (trzeba go dodać).
Michel Keijzers,
Hej, nie martw się. Jestem pewien, że Twoja aplikacja będzie dobrze zaprogramowana. Jak wspomnieli inni, DMA jest świetny, ale nie wszystko jest darmowe. Wprowadza do systemu dodatkowe uwagi, które nie istnieją, jeśli można uciec bez niego.
pgvoorhees
mam nadzieję, że wciąż jestem początkującym.
Michel Keijzers,
3

Po stronie odbierającej (jak sobie przypominam) DMA kończy się przy dopasowaniu znaków lub przy liczbie terminali. Niektóre protokoły i wiele interaktywnych aplikacji nie mieszczą się łatwo w tym modelu i naprawdę trzeba obsługiwać różne znaki. Techniki DMA mogą być również kruche, jeśli łącze komunikacyjne jest zawodne, utrata jednego znaku w strumieniu może łatwo zepsuć maszynę stanu DMA.

Dean Franks
źródło
Rzeczywiście otrzymuję bajt po bajcie i kopiuję go ręcznie do bufora pierścieniowego, aby przetworzyć go później.
Michel Keijzers
1

Użyłem STM32CubeMx / HAL w kilku projektach i odkryłem, że generowane przez niego oprogramowanie do obsługi UART ma wyraźne wady po stronie odbiorczej.

Podczas transmisji zwykle będziesz chciał wysłać blok danych lub wiersz tekstu. W takim przypadku wiesz z góry, jak długo trwa przesyłanie danych, więc użycie DMA jest oczywistym rozwiązaniem. Otrzymujesz przerwanie po zakończeniu przesyłania i możesz użyć funkcji oddzwaniania UART TX complete, aby wskazać kodowi głównemu, że transmisja została zakończona, i możesz wysłać kolejny blok danych.

Jeśli chodzi o odbiór danych, wszystkie funkcje zapewniane przez ST zakładają, że wiesz, ile znaków da ci urządzenie wysyłające, zanim zacznie wysyłać. Zwykle nie jest to znane. Funkcja przerwania umieszcza odebrane dane w buforze i wskazuje tylko, że dane są dostępne po otrzymaniu wstępnie zdefiniowanej liczby znaków. Jeśli spróbujesz użyć funkcji DMA lub funkcji przerwania do odbierania danych przez skonfigurowanie sekwencyjnego przesyłania pojedynczych znaków, to czas konfiguracji każdego z nich będzie oznaczał, że stracisz znaki przy czymkolwiek innym niż najniższa prędkość transmisji (prędkość transmisji, którą będziesz zaczną tracić dane będą zależeć od szybkości zegara procesora) i nadmiernie obciążą procesor, nie pozostawiając cykli instrukcji dla żadnego innego przetwarzania

Aby obejść ten problem, napisałem własną funkcję obsługi przerwań, która przechowuje dane w małym lokalnym buforze cyklicznym i ustawia liczbę odczytywaną przez kod główny (semafor zliczający RTOS), aby wskazać, że dane są gotowe. Główny kod może następnie gromadzić dane z tego bufora w dowolnym momencie, nie ma znaczenia, czy występuje pewne opóźnienie w gromadzeniu danych, pod warunkiem, że bufor lokalny nie przepełni się przed zebraniem danych.

uɐɪ
źródło
Robię dokładnie to samo (tak myślę). Czytam 1 bajt na raz i przechowuję go w buforze cyklicznym i zamierzam sprawdzić w głównej pętli, czy są pełne wiadomości. Może być nieco ulepszony.
Michel Keijzers,
Czy myślisz, że mógłbym mieć problem z tym, że konfigurowanie DMA za każdym razem spowoduje przeciążenie procesora / brakujących znaków przy 31.250 bodów?
Michel Keijzers,
1
Tak długo, jak skonfigurujesz DMA do przesyłania wielu znaków na raz, nie będzie to stanowić problemu. Mam 4 UART z uruchomioną 115200 i wyższą wersją, a I2C używa DMA bez problemów. Transmisje UART mają ~ 20 bajtów lub więcej. Problem polegał na użyciu DMA do odbioru na UART (procesor L4 przy 80 MHz, 9600 bodów).
uɐɪ
Obecnie ustawiam go na 1 bajt naraz, ale mogę go poprawić (robiąc pierwszy bajt i sprawdzając, ile dalszych bajtów jest potrzebnych).
Michel Keijzers