Skocz do zawartości

[STM32][HAL] UART w trybie Single Wire (Half-Duplex)


atlantis86

Pomocna odpowiedź

Projektując płytkę do jednego se swoich projektów na STM32F407 miałem dość napiętą sytuację z pinami. Aby oszczędzić jeden z nich stwierdziłem, że skonfiguruję sobie UART w trybie Single Wire (Half-Duplex), mogąc dzięki temu przeznaczyć pin RX do innych celów. UART tak czy inaczej jest mi potrzebny do debugowania i nie planowałem czegokolwiek n niego wysyłać. W STM32CubeMX skonfigurowałem USART1 w następujący sposób:

Baud Rate: 115200 bps
Word Lenght: 8 bits (including parity)
Parity: None
Stop bits: 1
Data Direction: Receive and Transmit (próbowałem też Transmit Only)
Over Sampling: 16 Samples

Dodatkowo ustawienia pinu odpowiadającego wyjściu USART1:
GPIO mode: Alternte Function Open Drain (próbowałem też lternate Function Push Pull)
GPIO pull-up/pull-down: pull-up
Maximum output speed: very high

Kod do obsługi nadawania wygląda następująco:

HAL_HalfDuplex_EnableTransmitter(&huart1);
HAL_UART_Transmit(&huart1, (uint8_t*)"Test\r\n", 6, HAL_MAX_DELAY);
HAL_HalfDuplex_EnableReceiver(&huart1);

Próbowałem także usunąć wywołanie "EnbleReceiver" a także wywoływać "EnableTransmitter" tylko raz, na początku programu.

Za każdym razem sytuacja wygląda tak samo - komunikat niby dochodzi, ale są w nim dość spore przekłamania. W co drugiej linijce nie zgadza się któryś znak. Próbowałem na dwóch różnych przejściówkach USB-UART - jedna to porządny układ na FTDI z izolacją galwaniczną, drugi to jakiś tani chiński adapter. Obydwa zachowywały się podobnie.

Żeby było ciekawiej, analizator stanów logicznych podpięty do tej linii poprawnie interpretuje pojawiające się na niej impulsy jako ciąg znaków "Test\r\n", a także wykrywa poprawnie prędkość 115200 bps. Na zdekodowanych danych nie widać żadnych błędów. Dlaczego więc zwykłe przejściówki UART-USB nie mogą sobie poradzić? Czyżby ten UART Single Wire nie był do końca tym samym co linia TX i wymagał jakiegoś bardziej złożonego interfejsu?

Link do komentarza
Share on other sites

Dnia 5.04.2021 o 20:02, atlantis86 napisał:

Czyżby ten UART Single Wire nie był do końca tym samym co linia TX i wymagał jakiegoś bardziej złożonego interfejsu?

Nie, to dokładnie to samo co TX. Używam tego trybu (hdx/single wire) w wielu projektach do komunikacji po rs485 i wszystko działa poprawnie. Więc w Twoim wypadku sprawdziłbym nietypowe rzeczy jak na przykład to czy aby na pewno GND masz połączone poprawnie.

Link do komentarza
Share on other sites

Być może w trybie half duplex jedyny pin UARTa jest mocnym wyjściem tylko w czasie nadawania znaku a podczas przerw w pracy nadajnika jest wejściem żeby  w ogóle można było coś odbierać. Sprawdź jak HAL inicjalizuje ten pin albo doczytaj w Reference Manual jak wygląda sterowanie jego kierunkiem, bo możliwości jest wiele. Może to być (w czasie nadawania) wyjście OC, może być push-pull a i wejście może mieć (albo nie) podciąg. Jeśli linia nie ma podciągania do Vcc, to w zależności od tego co podłączysz to albo będzie wszystko dobrze (bo to coś co podłączyłeś wymusza sobie jedynkę) albo źle (bo coś ciągnie - nawet leciutko - do masy i między znakami widzisz po jakimś czasie od ostatniego bitu STOP stan zero więc nie rozpoznajesz poprawnie bitów START kolejnego znaku).

Link do komentarza
Share on other sites

Zrobiłem jeszcze kilka dodatkowych prób. Wychodzi na to, że:

  • Podciągnięcie linii do plusa z pomocą rezystor 10k nie daje właściwie nic.
  • Użycie funkcji HAL_HalfDuplex_EnableTransmitter(&huart1) tylko raz na początku programu i niewywoływanie funkcji  HAL_HalfDuplex_EnableReceiver(&huart1) po zakończeniu transmisji tylko pogarsza sprawę - w terminalu pojawia się istna sieczka.
  • Najbliższym do ideału rozwiązaniem jest wysłanie kilku bajtów o wartości 0 przed właściwym komunikatem. Wtedy każda linia zaczyna się od nieznnego znaku, ale ciąg dalszy jest ok.

Niskopoziomowa funkcja _write() wygląda u mnie w tej chwili jnastępująco:

int _write(int file, char *data, int len)
{
   if ((file != STDOUT_FILENO) && (file != STDERR_FILENO))
   {
      errno = EBADF;
      return -1;
   }

   HAL_HalfDuplex_EnableTransmitter(&huart1);
   HAL_UART_Transmit(&huart1, (uint8_t*)"\0\0\0\0\0", 5, 1000);				//FIX THAT
   HAL_StatusTypeDef status = HAL_UART_Transmit(&huart1, (uint8_t*)data, len, 1000);
   HAL_HalfDuplex_EnableReceiver(&huart1);

   // return # of bytes written - as best we can tell
   return (status == HAL_OK ? len : 0);
}

Poza tym mam jeszcze jeden problem. Czasem transmisja się psuje, najwyraźniej w wyniku konfliktu pomiędzy wywołaniami "printf"
 z różnych wątków RTOS-a. Ktoś się orientuje co zrobić, żeby printf był "RTOS friendly?"

RTOS na pewno nie jest przyczyną oryginalnego problemu (tego częściowo rozwiązanego przez wysłanie zer przed wlaściwą transmisj) bo z identycznym problemem zetknąłem się już wcześniej, w innym projekcie, robionym bez RTOS-a.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

13 minut temu, atlantis86 napisał:

Poza tym mam jeszcze jeden problem. Czasem transmisja się psuje, najwyraźniej w wyniku konfliktu pomiędzy wywołaniami "printf"
 z różnych wątków RTOS-a. Ktoś się orientuje co zrobić, żeby printf był "RTOS friendly?"

Najprościej? Wyłączyć przerwania przed wywołaniem printf/przywrócić poprzedni stan po wywołaniu printf. A najbardziej poprawnie, opakować printf w mutexa, sekcje krytyczna czy inny obiekt, jaki Twoj RTOS ma do synchronizacji.

A przy okazji, czemu w ogóle half-duplex/single wire? Jak chcesz tylko wysyłać dane, to albo tryb asynchroniczny i nieskonfigurowany pin RX w GPIO, albo... w sumie dalej nieskonfigurowany pin RX w GPIO i tylko włączony nadajnik.

Co do tego o właściwie robi HAL w tych swoich funkcjach i czy coś złego z tego wynika - nie będę w stanie pomóc.

Edytowano przez kaworu
Link do komentarza
Share on other sites

3 godziny temu, kaworu napisał:

A przy okazji, czemu w ogóle half-duplex/single wire? Jak chcesz tylko wysyłać dane, to albo tryb asynchroniczny i nieskonfigurowany pin RX w GPIO, albo... w sumie dalej nieskonfigurowany pin RX w GPIO i tylko włączony nadajnik.

To była moja pierwsza myśl. Niestety, STM32CubeMX z jakiegoś powodu nie pozwala na takie rozwiązanie. Jeśli wybiorę tryb asynchroniczny, to automatycznie rezerwowane są dwa piny. Nawet jeśli w ustawieniach wybiorę "transmit only". Jeśli spróbuję ręcznie zwolnić pin RX i przypisać mu inną funkcję, to automatycznie dezaktywuje się UART. Co więcej - jeśli wcześniej przypiszę pin RX to jakiegoś innego celu, to program w ogóle nie pozwala wybrać trybu asynchronicznego. Single wire to jedyny tryb, który pozwala mi korzystać tylko z linii TX...

Link do komentarza
Share on other sites

Ah, ok, dopiszę to jako kolejny powód do listy "dlaczego hejtuję HAL" 🙂

Ale w takim razie przecież jak masz uart w trybie asynchronicznym, to ustawienia GPIO i tak możesz to zmienić w kodzie, który CubeMX wygenerował.

Jakby Ci to miało pomóc, kod inicjalizacji USART1 prawie-bez-bez HAL:

    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
                        
    RCC->APB2RSTR |= RCC_APB2RSTR_USART1RST;
    RCC->APB2RSTR &= ~(RCC_APB2RSTR_USART1RST);
            
    USART1->CR1 = 0;
    USART1->CR2 = 0;
    USART1->CR3 = 0;

	USART1->BRR = HAL_RCC_GetPCLK2Freq() / baudrate;        
    USART1->CR1 = USART_CR1_TE | USART_CR1_UE;

i wysyłki:

void usart1WriteStr(const char *str)
{
	while (*str != 0x00)
	{
		while(!(USART1->SR & USART_SR_TXE));
		USART1->DR = *str;
		str++;
	}
}

Wzięty z projektu na F407, do tego trzeba jeszcze ustawić pin TX w GPIO.

Edytowano przez kaworu
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.