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.
uart
dma
stm32cubemx
stm32f103c8t6
Michel Keijzers
źródło
źródło
Odpowiedzi:
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ś.
źródło
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.
źródło
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.
źródło
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:
W każdym razie po prostu jedzenie do namysłu.
źródło
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.
źródło
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.
źródło