W większości nowszych rodzin mikrokontrolerów STM32 linie RX i TX interfejsu UART można zamienić bez konieczności modyfikacji fizycznych połączeń na PCB. Takie proste rozwiązanie programowe może w niektórych przypadkach uratować błędny projekt płytki drukowanej.
Dwie pułapki w oznaczeniach linii RX i TX
Standardowy system oznaczeń, przyjęty w znakomitej większości schematów ideowych i blokowych, z którymi mamy do czynienia na co dzień, definiuje linie interfejsu asynchronicznego UART następująco:
TX - wyjście danych
RX - wejście danych
Pierwszy problem to niejednoznaczność konwencji przyjmowanych w dokumentacjach przez projektantów. O ile w przypadku interfejsów takich jak I2C wszystkie linie zegarowe lub danych o tej samej nazwie są łączone ze sobą (np. linię SCL mikrokontrolera podłączamy do SCL czujnika, a SDA procesora – do SDA sensora), to już w interfejsie UART linie są skrosowane: wyjście danych jednego układu łączymy z wejściem drugiego i na odwrót. W zależności od tego, jaką nomenklaturę przyjmiemy na schemacie, będzie ona niejako „prawidłowa” tylko dla jednego z układów – a zatem nietrudno o pomyłkę na etapie rysowania dokumentacji. Przykładowo na poniższym schemacie oznaczenia nadano „z punktu widzenia” mikrokontrolera.

Oczywiście można ów problem rozwiązać na poziomie samego nazewnictwa, wyraźnie identyfikując przeznaczenie danej sieci (np. MCU_RX, MCU_TX), ale z różnych względów nie zawsze takie rozwiązanie będzie użyteczne lub pożądane.
Kolejne źródło niejednoznaczności wynika z różnic w sposobie opisywania linii UART-u w dokumentacjach niektórych układów scalonych oraz modułów. Poniżej pokazano zrzut ekranu z tabeli wyprowadzeń zamieszczonej w nocie katalogowej modułu u-blox SARA-N3. Wprawdzie kierunek linii jest wyraźnie opisany i to na trzy sposoby (literowo, opisowo oraz numerycznie, w odniesieniu do standardu ITU-T V.24), ale… istnieje spore ryzyko, że z przyzwyczajenia zwrócimy uwagę tylko na same nazwy TXD/RXD, nie zagłębiając się w dalsze szczegóły dokumentacji.

Inny przykład pułapki nazewniczej ma miejsce w przypadku niektórych układów scalonych, w tym nadajników/odbiorników linii RS-485. Tutaj z kolei linia oznaczona literą R to wyjście odbiornika, a nie wejście nadajnika – trzeba ją zatem podłączyć do linii RXD mikrokontrolera.

Błędne połączenie linii spowodowałoby potencjalne zwarcie dwóch wyjść na jednej ścieżce (w momencie, w którym próbowałyby wymusić przeciwne stany logiczne) i pozostawienie drugiej ścieżki (wejście-wejście) bez aktywnego źródła sygnału. W przypadku mikrokontrolerów STM32, o ile oczywiście linie należą do tej samej instancji interfejsu USART, błąd można łatwo skorygować przez ustawienie bitu SWAP, co spowoduje następującą zmianę:
- fizyczny pin
USARTx_TX(np. PA9 w przypadku USART1) zacznie odbierać dane z wyjścia układu/modułu zewnętrznego, - fizyczny pin
USARTx_RX(np. PA10 w przypadku USART1) zacznie nadawać dane do wejścia układu/modułu zewnętrznego.
Implementacja funkcji SWAP w różnych rodzinach STM32
Bit odpowiedzialny za włączenie funkcji zamiany wyprowadzeń danych interfejsu U(S)ART lub LPUART jest elementem struktury rejestru konfiguracyjnego CR2 w zdecydowanej większości nowszych rodzin STM32. Jako przykład podamy tutaj fragment dokumentacji układów z serii STM32C5.

Trzeba jednak pamiętać, że bitu SWAP – podobnie jak pola bitowego ADD[7:0], funkcji inwersji danych DATAINV oraz wyprowadzeń (TXINV, RXINV) czy ustawienia kolejności bitów (MSBFIRST) – nie znajdziemy w starszej implementacji bloku peryferyjnego U(S)ART, która była stosowana w następujących rodzinach mikrokontrolerów:
- STM32F1,
- STM32F2,
- STM32F4,
- STM32L1.

Konfiguracja bitu na 4 sposoby
Inicjalizacja z użyciem CubeMX
Najprostsza metoda konfiguracji to rzecz jasna zaznaczenie odpowiedniego pola w konfiguratorze graficznym CubeMX. Należy przy tym zwrócić uwagę, że w widoku obudowy mikrokontrolera linie pozostaną niezmienione, zatem w dalszym ciągu wyświetlane będą domyślne kierunki danych zdefiniowane w nocie katalogowej, w tabeli funkcji alternatywnych GPIO. Widać to doskonale na poniższym zrzucie ekranu – linia PA10 wciąż jest podpisana jako USART1_RX, pomimo ustawienia funkcji TX and RX Pins Swapping na wartość Enable, zaś PC4 wciąż figuruje jako USART1_TX.

Inicjalizacja ręczna z użyciem STM32 HAL1
Jeżeli konfigurację mikrokontrolera wykonujemy ręcznie z użyciem biblioteki STM32 HAL1, musimy pamiętać, by funkcję zamiany linii włączyć przed wywołaniem HAL_UART_Init(). Najpierw zapisujemy wartość makra UART_ADVFEATURE_SWAP_INIT do pola AdvancedInit.AdvFeatureInit struktury konfiguracyjnej, sygnalizując chęć użycia zaawansowanych funkcji konfiguracji UART-u. Dopiero potem do pola AdvancedInit.Swap wpisujemy wartość UART_ADVFEATURE_SWAP_ENABLE , by program mógł docelowo ustawić bit SWAP w rejestrze CR2. Poniżej pokazano przykład funkcji konfigurującej interfejs USART1.
UART_HandleTypeDef huart1;
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.AdvancedInit.AdvFeatureInit =
UART_ADVFEATURE_SWAP_INIT;
huart1.AdvancedInit.Swap =
UART_ADVFEATURE_SWAP_ENABLE;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}Konfiguracja z użyciem biblioteki LL
Według dokumentacji STM32 bit SWAP jest jednym z tych ustawień, które należy wykonywać tylko i wyłącznie przy wyłączonym bloku UART:
This bitfield can only be written when the USART is disabled (UE = 0).
W poniższym kodzie zaznaczono explicite ten fakt poprzez wywołanie funkcji LL_USART_Disable() przed ustawieniem bitu SWAP, a następnie funkcji LL_USART_Enable() dopiero po tej operacji.
LL_USART_Disable(USART1);
LL_USART_SetTXRXSwap(
USART1,
LL_USART_TXRX_SWAPPED
);
LL_USART_Enable(USART1);Bezpośrednia konfiguracja rejestru
Jeżeli pracujemy bezpośrednio na definicjach rejestrów, kod odpowiadający powyższemu przykładowi będzie jeszcze prostszy:
USART1->CR1 &= ~USART_CR1_UE;
USART1->CR2 |= USART_CR2_SWAP;
USART1->CR1 |= USART_CR1_UE;Podsumowanie
Bit SWAP w rejestrze USART_CR2 pozwala programowo zamienić role fizycznych pinów RX i TX. Funkcja ta, dostępna w większości nowszych rodzin STM32, może pomóc „uratować” projekt PCB, w którym przez przypadek zastosowano błędne połączenie wyjścia z wyjściem i wejścia z wejściem. W niektórych przypadkach można w ten sposób także uprościć layout PCB, jeżeli standardowe przypisanie linii danych UART wymagałoby nieco bardziej złożonego prowadzenia ścieżek. Warto jednak pamiętać, że opisana funkcja nie występuje w starszych seriach STM32, czyli w rodzinach STM32F1, F2, F4 oraz L1.

