Elektroda.pl
Elektroda.pl
X
Elektroda.pl
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[STM32] Odbieraniedanych z UART1 i odsyłanie ich dalej poprzez UART6

11 Cze 2019 16:20 675 31
  • Poziom 23  
    Witam serdecznie.

    Mam zagwozdkę związaną z uartami. Wysyłam dane z mikrokontrolera poprzez UART1 do modemu GSM a konkretnie komendę "AT" w odpowiedzi dostaję "OK" i chciałbym teraz tę odpowiedz wysłać (odbić dalej) poprzez UART6, który jest podłączony do konwertera UART <--> USB.
    Przeglądałem poradnik https://forbot.pl/blog/kurs-stm32-f4-7-komunikacja-przez-uart-id13472 ale coś nie udaje się niczego wysłać.

    Intuicyjnie należy w funkcji przerwania:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    wywołać funkcję wysyłającą dane poprzez UART6. To jest jasne, ale co podać za argumenty tych funkcji, jak wyłuskać odpowiedź z modemu? Proszę o podpowiedzi.


    Oto fragment kodu:

    Kod: c
    Zaloguj się, aby zobaczyć kod
  • Pomocny post
    Poziom 30  
    matej1410 napisał:
    chciałbym teraz tę odpowiedz wysłać (odbić dalej) poprzez UART6

    Użyłbym DMA do tego celu.

    matej1410 napisał:
    który jest podłączony do konwertera UART <--> USB.

    Co to za STM?
    Dlaczego wybrałeś STM bez USB tylko używasz przejściówki?
  • Poziom 23  
    Teraz mam nucleo z STM32F401RE, ale docelowo nie będzie nucleo tylko sam procesor i modem na płytce.

    Użyłbyś DMA, ale czy to znaczy, że nie da się tego zrobić na przerwaniach?
    Czy na DMA jest łatwiej, przystępniej?
  • Pomocny post
    Poziom 30  
    matej1410 napisał:
    Teraz mam nucleo z STM32F401RE, ale docelowo nie będzie nucleo tylko sam procesor i modem na płytce.

    STM32F401RE ma USB, dlaczego więc bawisz się w przejściówki?

    matej1410 napisał:
    żyłbyś DMA, ale czy to znaczy, że nie da się tego zrobić na przerwaniach?

    Oczywiście, że się da.

    matej1410 napisał:
    Czy na DMA jest łatwiej, przystępniej?

    Używasz HAL a tam odbiór z UART nie jest zrobiony zbyt szczęśliwie. Z DMA powinno być łatwiej a z pewnością mniej obciąża CPU.
  • Poziom 23  
    Wszystko rozumiem kolego LChucki, ale po coś przecież jest funkcja:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    i przecież przerwanie można obsłużyć. Czy jesteś w stanie wytłumaczyć jak to zrobić na przerwaniach?
    Albo inaczej jeszcze. Jak podejrzeć w inny sposób, co zostało wysłane przez modem?
  • Pomocny post
    Poziom 8  
    LChucki napisał:
    Z DMA powinno być łatwiej a z pewnością mniej obciąża CPU.
    Oczywiscie ze nie. Jak zwykle kol. @LChucki wprowadza w błąd.

    Dla 2 znaków oczywiście to nie ma sensu. Trzeba po stronie odbierającej wykrywać koniec transmisji (np poprzez IDLE, przekróecenie bufora itd itd). Do tego trzeba zaimplementować odpowiednie mechanizmy synchronizacyjne. Sądząc po zadawanych pytaniach - nie jest to jeszcze dla Ciebie.


    Jeżeli prędkości obydwu USARTÓW są identyczne to wystarczy od biedy:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Oczywiście powinno się dodać bufory kołowe, obsługę błedów etc etc. Ale to taka najprostsza (a raczej najprymitywniejsza) wersja
  • Pomocny post
    Poziom 30  
    atollic napisał:
    LChucki napisał:
    Z DMA powinno być łatwiej a z pewnością mniej obciąża CPU.
    Oczywiscie ze nie. Jak zwykle kol. @LChucki wprowadza w błąd.

    Piotrus_999 vel stmx vel Omniboard vel atollic, czy przerwania mniej obciążają CPU niż DMA?
  • Pomocny post
    Specjalista - Mikrokontrolery
    Sposób najprostszy - użyć przewodów zamiast mikrokontrolera.
    Jeśli nie stać nas na dwa przewody, można zaprogramować DMA w tryb cykliczny na bezpośrenią transmisję pomiędzy rejestrami danych obu UARTów.
    Jeśli oprócz kopiowania dane mają być jeszcze przetwarzane, to należy oprogramować oba UARTy na przerwaniach i w nich zrobić przetwarzanie danych i wrzucanie do bufora do wysłania.

    To, czego na pewno NIE należy robić, to wywołanie funkcji Transmit z callbacka odbioru innego UARTA, bo w ten sposób zawiśniemy w przerwaniu UARTA na czas nadawania łańcucha znaków przez drugi UART i zapewne zgubimy znaki odbierane przez ten pierwszy.

    No i oczywiście napisanie tego bez HAL dałoby kod źródłowy w C przynajmniej 5 razy krótszy niż z HAL.
  • Poziom 23  
    Dziękuję za wszystkie podpowiedzi. Spróbuje z DMA.

    Na początek cały kod:
    Kod: c
    Zaloguj się, aby zobaczyć kod



    Więc tak:
    0. Deklaruję sobie globalnie:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    1. Wysyłam komendę AT do modemu poprzez UART1 spodziewając się odpowiedzi "OK"
    Kod: c
    Zaloguj się, aby zobaczyć kod

    2. Uruchamiam przerwanie od wiadomości przychodzącej z modemu:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    3. W funkcji obsługi przerwania po odpowiedzi modemu zmieniam stan diody na przeciwny, po czym zapisuje dane przychodzące z modemu z UART1 do tablicy odebrane przy pomocy funkcji HAL_UART_Receive_DMA, po czym wysyłam (niejako przekazuje) to co odebrałem z UART1 do UART6. Program działa tak, że funkcja HAL_UART_Transmit_DMA(&huart6, odebrane, 2); wysyła mi przez UART6 nie "OK" tylko "AT". Dlaczego?

    Kod: c
    Zaloguj się, aby zobaczyć kod
  • Poziom 30  
    matej1410 napisał:

    0. Deklaruję sobie globalnie:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Na CRLF nie czekasz?
    Jeśli nawet ma to być łańcuch znaków to powinno byc miejsce na znak końca teksu w przeciwnym wypadku zapomnij o wykorzystaniu "string.h".
  • Poziom 23  
    no bo rozumiem, że funkcja:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    służy do zapisania danych przychodzących z UART1 do tablicy odebrane, natomiast funkcja:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    służy do zapisania (przekazania) danych odebranych z funkcji wyżej (a dokładnie z tablicy odebrane) do kanału UART6

    Dodano po 18 [minuty]:

    W momencie w którym wysyłam cokolwiek tzn. z punktu widzenia modemu "bzdurę" czyli nie "AT" a np. "ab" czyli coś czego modem nie zna, to dostaję z powrotem to, co wysyłam czyli "ab"
  • Specjalista - Mikrokontrolery
    Zawsze modem odbija to, co wysyałasz. Wyślesz AT - dostaniesz AT i OK.
  • Pomocny post
    Specjalista - Mikrokontrolery
    BlueDraco napisał:
    Zawsze modem odbija to, co wysyałasz.

    Nie zawsze - zależy czy jest włączone echo. Zwykle wyłącza się echo komendą `ATE0`, a włącza - `ATE1`. Czy domyślnie jest w danym modemie włączone czy nie to już zależy od modemu chyba.
  • Poziom 30  
    Freddie Chopin napisał:
    Czy domyślnie jest w danym modemie włączone czy nie to już zależy od modemu chyba.

    I/lub S-rejestrów.
  • Poziom 23  
    Zmodyfikowałem nieznacznie kod:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Podpiąłem się oscyloskopem pod linię UARTU - mikrokontroler <-> modem i na TX (z punktu widzenia mikrokontrolera) mam w ramce dokładnie AT+\n\r czyli poprawnie wysłaną komendę AT, natomiast na lini RX mam w ramce "OK". Czyli mikrokontroler wysyłA poprawnie ale nie "odbija" w dalszym ciągu tego "OK" na uart6. HAL_GPIO_TogglePin jet wywoływana i dioda "mruga". Dodatkowo dodałem opóźnienie w pętli while, żeby było wygodniej obserwować to, jak działa mikrokontroler
  • Pomocny post
    Specjalista - Mikrokontrolery
    To po prostu tak nie zadziała, bo w kodzie powyżej masz pełno różnych błędów.

    Zrób tak jak to opisał Piotr Jankowski w poście nr 6, czyli w przerwaniach. Twoje założenie, że modem odsyła tylko dwa znaki jest mocno błędne, choćby dlatego, że odsyła on zwykle sekwencję \r\n zarówno przed jak i po odpowiedzi. Do tego poza OK możesz dostać bardzo wiele różnych rzeczy, np. różne URC które sobie włączysz, albo błąd wykonania komendy. Jak już koniecznie chcesz się bawić w DMA, to zrób tak jak pisał Blue Draco, czyli po prostu "w nieskończoność" DMA przekazuje w trybie circular bezpośrednio z USARTx->DR na USARTy->DR, bez żadnych buforów. I oczywiście będzie tak jak to Blue Draco wspomniał, czyli bez HALa do 10-ciu linijek bez wywoływania magicznych funkcji i durnych callbacków wymyślonych przez kogoś kto w życiu nie napisał nawet linijki obiektowego kodu (to oczywiście uwaga do autorów HALa, zapewne studentów z Indii lub coś w ten deseń).
  • Poziom 23  
    A które peryferia są porządnie oprogramowane w bibliotekach HALa? Rozumiem, że UART jest słabo zrobiony.
    Próbowałem z UARTEM działać na samym początku na bibliotekach STD i mi odradzono, więc przerzuciłem się na STM32CUBE i co za tym idzie na HALa, okazuje się, że tu UART też jest źle zaimplementowany. Czy to oznacza, że trzeba pracować na rejestrach i nie używać bibliotek?
    Trochę to dziwne.
  • Specjalista - Mikrokontrolery
    matej1410 napisał:
    Czy to oznacza, że trzeba pracować na rejestrach i nie używać bibliotek?

    Nic nie "trzeba". Tylko po prostu jak chcesz zrobić coś choć trochę bardziej dziwnego niż to przewidzieli twórcy HALa, to zaczyna się temat na 50 postów o tym "jakie magiczne funkcje i jakie magiczne argumenty przekazać", gdy tymczasem rozwiązanie problemu bez tego HALa zajmuje autentycznie z 5 linijek (jak opcja przekazywania danych bezpośrednio między rejestrami USART->DR przy pomocy DMA). Albo zrób to na przerwaniach i tyle. Albo zrewiduj założenia projektu, bo faktycznie całość wydaje się mocno przekombinowana - wystarczyłoby przejściówkę USB podpiąć do modemu i problem z głowy - po co komu mikrokontroler po drodze jak nie ma nie robić z tymi danymi?
  • Poziom 30  
    matej1410 napisał:
    A które peryferia są porządnie oprogramowane w bibliotekach HALa?

    W HAL to chyba wszystko jest skopane i wszystko można poprawić ale nie miałem problemów z SPI i I2C zarówno na przerwaniach jak i DMA ale...jak przesyłasz większe ilości danych, jak tych danych jest niewiele (wtedy raczej nie używa się DMA czy przerwań) to narzut robiony przez HAL (uruchomienie transmisji i jej zakończenie) jest bardzo duży i pomiędzy przesyłanymi danymi są bardzo duże przerwy.
    UART używam bardzo rzadko, wysyłanie jest ok, czy to z przerwaniami czy DMA, ale odbiór jest jaki jest. Jak odbierasz niewiele danych z niedużymi (poniżej 1Mb/s) prędkościami to jakoś to działa.
  • Poziom 23  
    Freddie Chopin napisał:
    Albo zrewiduj założenia projektu, bo faktycznie całość wydaje się mocno przekombinowana - wystarczyłoby przejściówkę USB podpiąć do modemu i problem z głowy - po co komu mikrokontroler po drodze jak nie ma nie robić z tymi danymi?


    Oczywiście, że mikrokontroler będzie odczytywał dane z czujnika i pompował je poprzez modem do chmury, ale nie chcę robić naraz kilku rzeczy i zaczynam od komunikacji pomiędzy uC a modemem i oczywiście podłączałem się z termianala do modemu poprzez przejściówkę i wysyłałem sobie dane, smsy itd. Nie mam problemu z obsługą modemu wprost, a z obsługą modemu z poziomu mikrokontrolera, bo w projekt nie zakłada przejściówki, terminala i klawaitury, a jedynie modem i procesor.
    Męczę się z tym, bo nie wiem nawet co odpowiada modem i chcę sobie to co odpowiada wyrzucać do uartu osobnego. No chyba, że robię to na darmo i można w jakiś inny, prostszy sposób podglądać to, co odpowiada mi mój modem.
  • Pomocny post
    Specjalista - Mikrokontrolery
    Jesteś dopiero na początku problemów z modemem. Jeśli uC ma sterować modemem, to musisz w kodzie zbudować całkiem spory automat z parsowaniem dowolnych odpowiedzi modemu i np. ustawianiem znaczników wykrycia poszczególnych odpowiedzi. Pierwsze zdziwienie, jakiego doświadczysz: modem może w dowolnym momencie wypluć dowolną wiadomość - i Twoje oprogramowanie musi sobie z tym poradzić. Nie możesz zakładać, że na po jakimś poleceniu zawsze dostaniesz odpowiedź na nie właśnie. Musisz też odczekiwać pewien niedługi czas po każdym CR wysyłanym do modemu. To dopiero początek modemowych niuansów...
  • Poziom 30  
    BlueDraco napisał:
    Musisz też odczekiwać pewien niedługi czas po każdym CR

    Niedługi?
    Po komendzie wysłania SMS do 60 sekund (chyba o modemie GSM jest ta dyskusja?). Odpowiedzi na niektóre komendy do 3 sekund, większość do sekundy. W dokumentacji modemu jest informacja o czasach odpowiedzi na poszczególne komendy.

    BlueDraco napisał:
    musisz w kodzie zbudować całkiem spory automat z parsowaniem dowolnych odpowiedzi modemu

    Poza RING, CLIP, z opóźnieniem przychodzą komendy o zalogowaniu, wylogowaniu. Niektóre zależą od ustawień modemu.

    Z pewnością "Arduinowe" podejście do modemu (send cmd, delay, send smc, delay, idt) nie zda egzaminu. Trochę roboty przy tym jest, trzeba uwzględnić timeout, ponawianie niektórych komend gdy zwracają błąd. Tak z miesiąc roboty aby to zrobić dobrze.
  • Poziom 23  
    Freddie Chopin napisał:
    To po prostu tak nie zadziała, bo w kodzie powyżej masz pełno różnych błędów.

    Zrób tak jak to opisał Piotr Jankowski w poście nr 6, czyli w przerwaniach. Twoje założenie, że modem odsyła tylko dwa znaki jest mocno błędne, choćby dlatego, że odsyła on zwykle sekwencję \r\n zarówno przed jak i po odpowiedzi. Do tego poza OK możesz dostać bardzo wiele różnych rzeczy, np. różne URC które sobie włączysz, albo błąd wykonania komendy. Jak już koniecznie chcesz się bawić w DMA, to zrób tak jak pisał Blue Draco, czyli po prostu "w nieskończoność" DMA przekazuje w trybie circular bezpośrednio z USARTx->DR na USARTy->DR, bez żadnych buforów. I oczywiście będzie tak jak to Blue Draco wspomniał, czyli bez HALa (...)


    Czyli zrobić to na przerwaniach lub z użyciem DMA, ale użyć bibliotek standardowych i unikać HALa, tak? Dobrze rozumiem?
  • Pomocny post
    Specjalista - Mikrokontrolery
    matej1410 napisał:
    Dobrze rozumiem?

    Źle.

    Tak zwany "SPL" (zapewne to rozumiesz przez "biblioteki standardowe") to taki sam badziew jak "HAL". No może większy, z racji tego że projekt nie jest już rozwijany.
  • Poziom 23  
    Freddie Chopin napisał:
    matej1410 napisał:
    Dobrze rozumiem?

    Źle.

    Tak zwany "SPL" (zapewne to rozumiesz przez "biblioteki standardowe") to taki sam badziew jak "HAL". No może większy, z racji tego że projekt nie jest już rozwijany.


    Czyli na rejestrach pozostaje to zrobić?
  • Poziom 30  
    matej1410 napisał:
    Freddie Chopin napisał:
    matej1410 napisał:
    Dobrze rozumiem?

    Źle.

    Tak zwany "SPL" (zapewne to rozumiesz przez "biblioteki standardowe") to taki sam badziew jak "HAL". No może większy, z racji tego że projekt nie jest już rozwijany.


    Czyli na rejestrach pozostaje to zrobić?

    To co chcesz to akurat na rejestrach najlepiej zrobić. Co do narzekania na HAL, używam metody mieszanej, głównie HAL a tam gdzie się nie sprawdza "rzeźbię" na rejestrach.
    HAL-a trzeba się nauczyć, jak Arduino, jego ograniczeń. Zasadniczo jestem zadowolony z HAL. USB dział całkiem dobrze. Jest dosyć uproszczone ale w większości zastosowań wystarcza.
  • Pomocny post
    Poziom 34  
    matej1410 napisał:
    Podpiąłem się oscyloskopem pod linię UARTU - mikrokontroler <-> modem i na TX (z punktu widzenia mikrokontrolera) mam w ramce dokładnie AT+\n\r czyli poprawnie wysłaną komendę AT, natomiast na lini RX mam w ramce "OK". Czyli mikrokontroler wysyłA poprawnie ale nie "odbija" w dalszym ciągu tego "OK" na uart6. HAL_GPIO_TogglePin jet wywoływana i dioda "mruga". Dodatkowo dodałem opóźnienie w pętli while, żeby było wygodniej obserwować to, jak działa mikrokontroler


    Ten kod masz prawie dobry. Trochę poprawiłem i działa mi na NucleoF401RE tak jak ma działać.
    Tak że katastroficzne wizje co niektórych że HAL tego zadania nie uciągnie, trochę chyba były przesadzone.

    Tylko taka generalna sprawa.
    Jeśli spinasz programowo dwa uarty tak że w przerwaniu od odbioru jednego uarta wstawiasz nadawanie na drugim, to Baud rate tego drugiego musi być zdecydowanie wyższy (np. x2) niż tego pierwszego. Jeśli prędkości będą takie same, to będą duże problemy (gubienie znaków), nawet niezależnie od realizacji, czy na samych rejestrach czy na HAL. Jedynym wyjściem, jeśli by musiały być prędkości tych uartów te same, była by konieczność dołożenia bufora kołowego.
    Co oczywiście jest do zrobienia ale trochę skomplikuje program.

    Tak że w tym co już zrobiłeś i co po korekcie działa, zalecałbym koniecznie podniesienie Baud rate tego podsłuchującego usartu względem tego podsłuchiwanego. Ja tu w testach użyłem 115200 a na tym dodatkowym 230400 i śmiga.

    Tu może wkleję tylko istotne fragmenty źródeł tego co u mnie działa, reszta tak jak u Ciebie. Z tym że mam inne usarty użyte niż Ty (USART2 do komunikacji z modemem, USART1 - podsłuchujący) to musisz mieć to na uwadze i pozmieniać indeksy do Twojego schematu:


    Kod: c
    Zaloguj się, aby zobaczyć kod



    Czyli jak widzisz odbiór zrobiłem po jednym znaku (bo i tak nie wiadomo ile przyjdzie). Wybrałem tryb przerwaniowy dlatego że DMA w tej sytuacji zupełnie tu nic nie daje, tylko komplikuje. Bo i tak istotą obsługi jest przerwanie i ten callback w jego obsłudze.
    Zwracam uwagę na to by w HAL_UART_RxCpltCallback jako pierwsze wykonać HAL_UART_Receive_IT by uart od razu odbierał następny znak, a później resztę.
  • Poziom 23  
    Serdecznie dziękuję za odpowiedź rb401.
    Zmodyfikowałem kod:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    ale mam warningi:
    [STM32] Odbieraniedanych z UART1 i odsyłanie ich dalej poprzez UART6

    warningi znikają po usunięciu volatile sprzed deklaracji zmiennej buf1, ale nic się nie pojawia w terminalu

    Dodano po 36 [minuty]:

    To trochę dziwne, bo wychodzi na to, że kompilator krzyczy, że zmienna uint8_t buf1 jest niezgodna z tą którą przyjmuje np. funkcja:
    HAL_UART_Receive_IT(&huart1, &buf1, 1); bo jest volatile a przecież ta funkcja ma związek z przerwaniami więc zmienna, która się podaje jako argument powinna być volatile.
    Taka sprzeczność?
  • Pomocny post
    Poziom 8  
    matej1410 napisał:
    bo jest volatile a przecież ta funkcja ma związek z przerwaniami więc zmienna, która się podaje jako argument powinna być volatile.
    Nie musi.

    Nie wiem, dlaczego nie chcesz po prostu zrobic jak Ci napisałem. Masz gotowe dwa handlery. Dodatkowym plusem jest to, że będzie działać dwukierunkowo.
  • Poziom 23  
    nie bardzo rozumiem czym się różni funkcja HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) od USART1_IRQHandler(void)

    Callback jest wywoływana po odebraniu ściśle określonej liczby znaków przez mikrokontroler z zewnątrz natomiast ta druga?

    Dodano po 4 [minuty]:

    i jeszcze jedna kwestia:
    Czy to, co proponujesz tu niżej to mam zmienić w pliku sem32f4xx_it.c edytując zawartości funkcji w tymże pliku, czy wrzucić do main.c?

    Kod: c
    Zaloguj się, aby zobaczyć kod