STM32: OSPEEDR, czyli szybko, szybciej i najszybciej (1). Podstawy

Udostępnij

Rejestr GPIOx_OSPEEDR, odpowiedzialny w mikrokontrolerach STM32 za dynamikę sygnałów na liniach GPIO, często bywa konfigurowany „na wszelki wypadek” od razu na najwyższą możliwą prędkość. Takie działanie to jednak spory błąd – w rzeczywistości od ustawienia OSPEEDR zależy bardzo wiele, choć zwykle problem wyjdzie na jaw w najmniej pożądanym momencie. Tym artykułem zaczynamy krótki cykl publikacji, w których bardzo szczegółowo przyglądamy się zagadnieniom związanym z prawidłową konfiguracją dynamiki GPIO.

Rejestr OSPEEDR w STM32 – do czego służy?

W mikrokontrolerach STM32 każda linia GPIO ma przypisane dwa bity [1:0], które wybierają tryb szybkości wyjściowej: 00 – Low speed, 01 – Medium speed, 10 – High speed, 11 – Very high speed. W notach katalogowych poszczególnych rodzin układów STM32 można znaleźć szczegółowe parametry dynamiczne, które znacznie różnią się pomiędzy mikrokontrolerami z różnych serii. Spróbujmy więc na początek potraktować temat niejako „z lotu ptaka”. W tabeli poniżej zebraliśmy najważniejsze informacje o wszystkich czterech trybach pracy wraz z ich przykładowymi zastosowaniami.

OSPEEDR[1:0] / SpeedZnaczenie praktycznePrzykładowe zastosowania
00 / LowNajwolniejsze zbocza sygnału; obsługiwane częstotliwości przełączania są przeważnie w zakresie do kilku MHzSterowanie diodami LED, przekaźnikami, prostymi sygnałami binarnymi; łącza UART, I2C i SPI pracujące z niewielką prędkością
01 / MediumUstawienie pośrednie dla umiarkowanie szybkich sygnałów; częstotliwość przełączania przeważnie ograniczona do 10...25 MHzSygnały PWM wysokiej częstotliwości, szybkie magistrale UART i SPI
10 / HighStrome zbocza dla szybkich sygnałów cyfrowych; ograniczenie częstotliwości do kilkudziesięciu MHzSzybkie SPI, obwody wymagające obsługi krótkich impulsów
11 / Very highMaksymalna stromość zboczy i najwyższa częstotliwość pracy (do 100 MHz lub więcej)QSPI o wysokiej częstotliwości taktowania, SDIO, kontroler pamięci zewnętrznej (FSMC)

Warto w tym miejscu zwrócić uwagę na bardzo ważny szczegół – blok sterownika bufora wyjściowego (Output control na poniższym rysunku) kontroluje parę tranzystorów komplementarnych zarówno w trybie zwykłego wyjścia GPIO (typu push-pull lub open drain), jak i podczas pracy jako wyjście funkcji alternatywnej (AF).

Schemat blokowy struktury obsługującej pin GPIO w mikrokontrolerach STM32 (źródło: RM0090)

Ustawienie rejestrów OSPEEDR ma zatem znaczenie tylko wtedy, gdy pin pracuje jako wyjście. Przykładami mogą być interfejsy SPI, QSPI, FSMC czy SDIO, bloki USART skonfigurowane do pracy przy bardzo wysokich prędkościach transmisji, wyjścia sygnału zegarowego (MCO) itp. Trzeba pamiętać, że w przypadku wejścia cyfrowego ustawienie szybkości nie „przyspiesza” odczytu stanu logicznego; jest to tylko i wyłącznie parametr bufora wyjściowego i jego zdolności do formowania zboczy. Zresztą i tak tego rodzaju ustawienie nie miałoby większego sensu w odniesieniu do wejść cyfrowych STM32 – te bowiem mają wbudowane przerzutniki Schmitta, które skutecznie „odfiltrowują” zbyt wolne zbocza.

Konfiguracja OSPEEDR w CubeMX, czyli… jak nie wpaść w pułapkę lenistwa

Ustawienie szybkości GPIO w przypadku korzystania z kreatora STM32CubeMX/STM32CubeMX2 jest dobrym punktem wyjścia do szybkiego uruchomienia programu. Zasadniczo narzędzie samo dobiera prędkość odpowiednio do wybranej rodziny mikrokontrolerów i… do typowych warunków pracy. Co to oznacza? Jak zawsze posłużymy się konkretnym przykładem.

Spójrzmy na domyślną konfigurację linii I/O obsługujących SPI w mikrokontrolerze z serii STM32C0 – czyli jednego z relatywnie nowszych układów na rynku.

Domyślna konfiguracja pinów SPI mikrokontrolera STM32C011 – maksymalna szybkość GPIO (kolumna 6) jest ustawiona na najniższą wartość (rys. HardwareDesigner.pl)

Jak widać, program dobrał „na dzień dobry” prędkość najniższą z możliwych, co odpowiada ustawieniu bitów OSPEEDR[1:0]na wartość 00.

Jeżeli jednak dokładnie takie same kroki podejmiemy w stosunku np. do niezwykle popularnego układu z serii STM32F407, naszym oczom ukaże się diametralnie inny obraz:


Domyślna konfiguracja pinów SPI mikrokontrolera STM32F407 – tutaj maksymalna szybkość GPIO (kolumna 6) jest ustawiona na najwyższym z czterech możliwych poziomów (rys. HardwareDesigner.pl)

Z czego wynika aż taka różnica? Co ciekawe, wcale nie wiąże się to z faktem, że rodzina STM32F4 jest „ogólnie szybsza” od STM32C0, choćby pod względem maksymalnej częstotliwości taktowania rdzenia czy magistral APBx. W tym konkretnym przypadku wynika to z problemów z przekłamaniami ostatniego bitu, wykrytych swego czasu w mikrokontrolerach STM32F405/407/415/417 – więcej szczegółów na ten temat można znaleźć w erracie nr ES0182.

Czy oznacza to, że STM32CubeMX zawsze wyręczy nas w doborze odpowiedniego ustawienia? Niestety nie – byłoby to zresztą dość trudne do implementacji, gdyż właściwy wybór konfiguracji rejestru OSPEEDR zależy także od czynników innych niż sama tylko częstotliwość przełączania. Trzeba bowiem wziąć pod uwagę także napięcie zasilania mikrokontrolera, a nawet… pojemność obciążenia danej linii GPIO – a ten parametr zależy przecież od obciążenia, layoutu płytki drukowanej itd.

Fragment tabeli dotyczącej częstotliwości przełączania i czasu narastania/opadania GPIO w zależności od ustawienia bitów OSPEEDR[1:0] i napięcia zasilania mikrokontrolera (źródło: nota katalogowa STM32C011x4/x6)

O tym, że ustawienia rejestru OSPEEDR zawsze należy dobrać świadomie, można się przekonać wykonując prosty eksperyment. Niezależnie od tego, jaką prędkość transmisji (częstotliwość zegara, np. SPI) ustawimy w sekcji konfiguracyjnej danego bloku peryferyjnego, tryb prędkości pozostanie niezmieniony dopóty, dopóki ręcznie go nie przestawimy. A zatem jeżeli nasz przykładowy interfejs SPI będziemy chcieli zastosować przy taktowaniu linii SCK na poziomie kilkudziesięciu megaherców, to bez naszej ingerencji domyślna konfiguracja może wciąż pozostać na poziomie Low speed – co rzecz jasna uniemożliwi poprawne działanie interfejsu przy pełnej prędkości, gdyż wolne zbocza sygnału stłumią go do postaci kompletnie nieużytecznej dla układów cyfrowych.

Konfiguracja bez użycia STM32CubeMX na trzy sposoby

Programiści niekorzystający z generatora graficznego mogą oczywiście ustawić prędkość GPIO bezpośrednio w kodzie programu. Przy użyciu HALa ustawienie OSPEEDR jest reprezentowane przez pole Speed w strukturze GPIO_InitTypeDef. Przykładowo, linia PA5 jako wolne wyjście push-pull, może być skonfigurowana następująco:

GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();

GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

Pozostałe dopuszczalne wartości to oczywiście:

GPIO_SPEED_FREQ_MEDIUM,

GPIO_SPEED_FREQ_HIGH,

GPIO_SPEED_FREQ_VERY_HIGH.

Analogicznie, konfiguracja z użyciem biblioteki LL będzie wyglądała następująco:

LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);

GPIO_InitStruct.Pin = LL_GPIO_PIN_5;
GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

Jeżeli natomiast piszemy w rejestrach – po prostu ręcznie wyzerujemy obydwa bity OSPEEDR[1:0] odpowiadające linii PA5:

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

GPIOA->MODER &= ~GPIO_MODER_MODE5_Msk;
GPIOA->MODER |=  GPIO_MODER_MODE5_0;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT5;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD5_Msk;
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED5;

Szybkość GPIO – szansa, ale nie gwarancja

Określenie „GPIO speed” bywa mylące, nie należy bowiem interpretować wartości katalogowych jako gwarancji, że pin będzie można zawsze efektywnie przełączać z taką częstotliwością. OSPEEDR określa właściwości sygnałowe wyjścia cyfrowego. Każda linia GPIO steruje realną pojemnością: pojemnością wejścia odbiornika, ścieżki PCB, przelotek, złączy, przewodów, elementów zabezpieczających (np. transili), czy wreszcie sondy pomiarowej oscyloskopu użytego do pomiaru. Aby zmienić stan logiczny, bufor wyjściowy musi tę sumaryczną pojemność naładować lub rozładować.

Problem nie polega tylko na tym, czy sygnał „zdąży” osiągnąć poziom logicznej jedynki lub zera. Zbyt wolne zbocze może zmniejszać margines czasowy interfejsu, powodować błędy próbkowania i wydłużać czas przebywania sygnału w obszarze przejściowym odbiornika. Z kolei zbyt szybkie zbocze zawiera więcej składowych wysokoczęstotliwościowych, łatwiej pobudza rezonanse ścieżek i przewodów, zwiększa przesłuchy oraz emisję zaburzeń.

Podsumowanie

OSPEEDR w STM32 nie służy do „przyspieszania programu”, lecz do wyboru właściwości bufora wyjściowego pinu. Ustawienie to wpływa na czas narastania i opadania sygnału, maksymalną użyteczną częstotliwość przy danym obciążeniu pojemnościowym oraz poziom zakłóceń generowanych przez przełączające się GPIO. Najlepszą praktyką jest ustawianie możliwie najniższej szybkości zgodnej z wymaganiami sygnału, a nie bezrefleksyjne wybieranie trybu Very High.

W kolejnych częściach artykułu zagłębimy się w dalsze szczegóły związane z konfiguracją szybkości GPIO w mikrokontrolerach STM32. Zaprezentujemy także wyniki bardzo interesujących pomiarów, które wykonaliśmy w naszym laboratorium i pokażemy, jakie ważne wnioski można z nich wyciągnąć. Koniecznie zaobserwuj nasze profile na Facebooku i LinkedIn, a także zapisz się na nasz Newsletter, aby być zawsze na bieżąco z nowościami!

Przemysław
Przemysław
Redaktor Naczelny portalu HardwareDesginer.pl. Konstruktor elektronik i programista embedded z ponad 20-letnim doświadczeniem w projektowaniu PCB. Z wykształcenia elektronik medyczny i elektroradiolog, specjalizuje się w projektowaniu i badaniach aparatury medycznej, laboratoryjnej i pomiarowej.

Czytaj więcej

Wybrane dla Ciebie