Kurs STM32 LL cz. 3. Wewnętrzne i zewnętrzne źródła zegara

Pierwszą rzeczą, jaką konfigurujemy po uruchomieniu mikrokontrolera są zegary. Pozwalają nam ustalić odpowiednią dla naszych wymagań częstotliwość taktowania rdzenia i poszczególnych układów peryferyjnych. Dzięki temu zapewniamy odpowiednią szybkość transmisji danych, wykonywania obliczeń oraz konwersji sygnałów analogowych.

Schemat blokowy zegarów

Konfiguracja zegarów w STM32 to dość złożona sprawa. Im bardziej rozbudowany mikrokontroler, tym więcej elementów musimy wziąć pod uwagę. Układ STM32G071RBT6, na tle większych STM-ów, ma stosunkowo łatwy do zrozumienia przepływ sygnałów zegarowych. Schemat przedstawia poniższa grafika.

Napisałem, że jest stosunkowo łatwy do zrozumienia, ale patrząc na niego pierwszy raz można stwierdzić, że nie wiadomo od czego zacząć. Jeśli tak pomyślałeś, to mam nadzieję, że po rozdziale o RCC zmienisz zdanie.

Pierwotne źródła sygnału zegarowego

W STM32G071RB mamy do dyspozycji sześć źródeł sygnału. Każde z nich ma inne zastosowanie. Wśród nich można wymienić:

  • HSI16 RC – szybki oscylator wewnętrzny RC o częstotliwości taktowania 16 MHz
  • HSE OSC – szybki oscylator zewnętrzny (rezonator kwarcowy lub zewnętrzne źródło) o częstotliwości od 4 do 48 MHz
  • LSI RC – wolny oscylator wewnętrzny RC o częstotliwości 32 kHz
  • LSE OSC – wolny oscylator zewnętrzny rezonator kwarcowy o częstotliwości 32,768 kHz lub zewnętrzne źródło do 1 MHz
  • I2S_CKIN – wejście sygnału bezpośrednio dla interfejsu audio I2S1

Na schemacie źródła te zaznaczone zostały zielonym kolorem.

Schemat dotyczy całej serii STM32G0x1, dlatego widać na nim jeszcze źródło HSI48. Jest to zegar dedykowany dla USB, niedostępny dla używanego przez nas STM32G071RB. Warto dodać, że źródłem sygnałów mogą być między sobą przełączane w trakcie działania programu np. aby zmniejszyć pobór prądu.

Wtórne źródła sygnału zegarowego

Oprócz głównych źródeł zegarowych (pierwotnych), w STM32 mamy do dyspozycji także źródła wtórne, które powstają na skutek dzielenia lub mnożenia źródeł pierwotnych. Dzięki takim zabiegom istnieje możliwość dostosowania częstotliwości taktowania dla praktycznie dla każdego układu peryferyjnego oddzielnie. Wśród źródeł wtórnych można wyróżnić:

  • HSISYS – zegar HSI16 podzielony przez dzielnik od 1 do 128
  • PLLPCLK, PLLQCLK, PLLRCLK – wyjścia zegarowe z bloku pętli PLL (Phase Locked Loop), czyli pętli synchronizacji fazy
  • SYSCLK – zegar systemowy wybierany spośród kilku dostępnych źródeł
  • HCLK – zegar dla szyny AHB, powstaje jako SYSCLK podzielony przez dzielnik od 1 do 512
  • HCLK8 – zegar dla szyny AHB dedykowany do timerów rdzenia, powstaje jako SYSCLK podzielony przez dzielnik 8
  • PCLK – zegar dla szyny APB, powstaje jako HCLK podzielony przez dzielnik od 1 do 16
  • TIMPCLK – zegar dedykowany do timerów, powstaje jako PCLK podzielony przez dzielnik od 1 lub 2
  • LPTIMx_IN – wejścia zegarowe dla timerów Low Power

Wyjścia tych źródeł na schemacie zaznaczyłem kolorem żółtym.

Sygnały zegarowe w STM32G071RB mogą mieć maksymalnie częstotliwość 64 MHz. Niektóre z nich mają dodatkowe ograniczenia, które omówimy przy okazji konkretnych przypadków. Najważniejsze na początku będzie dla nas to, że dla HCLK i PCLK nie możemy przekraczać wartości 64 MHz.

Wewnętrzne źródła zegara – HSI16 i LSI

Wśród wewnętrznych źródeł zegarowych STM32G071RB oferuje nam dwa oscylatory RC: HSI16 RC oraz LSI RC.

HSI16 to sygnał zegarowy generowany przez wewnętrzny oscylator o częstotliwości 16 MHz. Pozwala nam w łatwy i szybki sposób uruchomić mikrokontroler przez potrzeby dodawania zewnętrznych elementów taktujących. W porównaniu do zewnętrznych kwarców oferuje jednak mniejszą dokładność, co przy wymagających zastosowaniach powoduje, że staje się bezużyteczny. W większości przypadków sygnał ten jest jednak wystarczający i z powodzeniem może być stosowany do taktowania mikrokontrolera w docelowych aplikacjach. Istotną informacją jest to, że zegar HSISYS, który czerpie źródło od HSI16, jest domyślnie uruchamiany po starcie mikrokontrolera.

LSI RC jest źródłem zegara dedykowanym do aplikacji o niskim poborze mocy. Może działać w trybie zatrzymania i gotowości dla niezależnego watchdoga (IWDG) i RTC. Częstotliwość zegara wynosi 32kHz.

Podstawowe rejestry konfiguracyjne – HSI16 jako SYSCLK

Pierwszy przykład jaki chciałbym przedstawić w ramach konfiguracji zegarów, będzie ustawienie HSI16 jako źródła zegara systemowego SYSCLK. Na schemacie poniżej zaznaczyłem przebieg takiego sygnału – pozwoli nam w łatwiej określić, jakie elementy musimy skonfigurować, aby takie efekt osiągnąć.

Interesują nas zatem rejestry dotyczące HSI16, HSISYS oraz SYSCLK.

Rejestr RCC_CR – rejestr kontrolny

Bity HSIDIV[0:2] – dzielnik zegara HSI

Bit HSIRDY – status włączenia zegara HSI

Bit HSION – bit uruchamiający zegar HSI

Rejestr RCC_CFGR – rejestr konfiguracyjny

Bity SWS[2:0] – status źródła sygnału dla zegara systemowego SYSCLK

Bity SW[2:0] – wybór źródła sygnału dla zegara systemowego SYSCLK

Wszystkie projekty z kursu dostępne są w moim repozytorium GitHub.

[PROGRAM] Konfiguracja zegara HSI16 jako źródła zegara SYSCLK

Poznaliśmy potrzebne rejestry, czas przejść do praktycznego przykładu.

Zgodnie z zaznaczonym przebiegiem sygnału zegarowego, w pierwszej kolejności włączamy wewnętrzny oscylator HSI.

LL_RCC_HSI_Enable();

Następnie poprzez sprawdzenie flagi HSIRDY czekamy, aż oscylator się uruchomi. Bez tego dalsza część konfiguracji nie powiedzie się.

while(LL_RCC_HSI_IsReady() != 1)
	;

Teraz ustawiamy dzielnik dla sygnału zegarowego HSISYS.

LL_RCC_SetHSIDiv(LL_RCC_HSI_DIV_1);

Następnie wybieramy HSISYS jako źródło dla zegara systemowego SYSCLK i ponownie czekamy, aż ta konfiguracja przebiegnie poprawnie, sprawdzając flagę SWS. 

LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
	;

Żeby sprawdzić poprawność działania naszego programu, dodamy konfigurację diody LD4 i w pętli while(1) zmianę jej stanu co 1 s. W pierwszej kolejności ustawimy częstotliwość zegara SYSCLK za pomocą funkcji LL_SetSystemCoreClock() oraz LL_Init1msTick(). Jest to potrzebne do uzyskania prawidłowych opóźnień.

LL_SetSystemCoreClock(16000000);
LL_Init1msTick(16000000);

Skąd bierzemy wartość 16000000? Wiemy że oscylator HSI ma częstotliwość 16 MHz, czyli 16000000. Ponieważ dzielnik dla HSISYS jest 1, 16000000/1 = 16000000. Taką częstotliwość ma zegar HSISYS, a tym samym SYSCLK.

Poniższą konfiguracją GPIO na razie nie zawracaj sobie głowy. Omówimy ją w kolejnym rozdziale. Dzisiaj chcemy tylko sprawdzić, czy dioda będzie migała z poprawną częstotliwością.

LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
LL_GPIO_SetPinOutputType(LED_GREEN_GPIO_Port, LED_GREEN_Pin, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(LED_GREEN_GPIO_Port, LED_GREEN_Pin, LL_GPIO_PULL_NO);
LL_GPIO_SetPinSpeed(LED_GREEN_GPIO_Port, LED_GREEN_Pin, LL_GPIO_SPEED_FREQ_LOW);
LL_GPIO_SetPinMode(LED_GREEN_GPIO_Port, LED_GREEN_Pin, LL_GPIO_MODE_OUTPUT);
 
while (1)
{
    LL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
    LL_mDelay(1000);
}

Przypominam, że zgodnie z tym, co przedstawiłem w rozdziale o tworzeniu projektów pliku, w pliku main.h powinny znaleźć się includy oraz definicje pinu.

#define LED_GREEN_Pin LL_GPIO_PIN_5
#define LED_GREEN_GPIO_Port GPIOA

Po skompilowaniu kodu (“Project->Build Project” lub CTRL+B) i wgraniu na płytkę (“Run->Run”), dioda LD4 powinna zmieniać stan co 1 s.

W ramach ćwiczenia, możesz zmienić dzielnik HSISYS na większy. Pamiętaj, że aby zachować poprawność działania programu, musisz dokonać zmiany także przy podaniu aktualnej częstotliwości SYSCLK w funkcjach LL_SetSystemCoreClock() oraz LL_Init1msTick() – np. dla dzielnika 8 (LL_RCC_HSI_DIV_8), SYSCLK będzie miał częstotliwość 16000000/8 = 2000000.

Zewnętrzne źródła zegara – HSE i LSE

Wśród zewnętrznych źródeł zegarowych STM32G071RB oferuje nam dwa wejścia: HSE oraz LSE.

HSE to sygnał zegarowy generowany przez zewnętrzne źródło oscylator o częstotliwości od 4 do 48 MHz. Zewnętrzne kwarce oferują większą dokładność sygnału zegarowego, dlatego są stosowane zwłaszcza przy wymagających urządzeniach. Sygnał HSE może być generowany na dwa sposoby: 

  • za pomocą zewnętrznego kwarcu/rezonatora
  • przy użyciu zewnętrznego sygnału zegarowego

Zastosowanie kwarcu wiąże się z dodaniem na płytce odpowiedniego elementu elektronicznego oraz kondensatorów dopasowanych do stosowanej częstotliwości kwarcu.

Zewnętrzny sygnał zegarowy podpinany jest pod pin OSC_IN. Może to być sygnał wygenerowany np. przez inny mikrokontroler.

LSE jest źródłem zegara dedykowanym do aplikacji o niskim poborze mocy. Może działać w trybie zatrzymania i gotowości dla niezależnego watchdoga (IWDG) i RTC. Częstotliwość kwarcu powinna wynosić 32,768 kHz (typowy kwarc zegarkowy). Rezonator podłączany jest wówczas pod piny OSC32_IN i OSC32_OUT i wymaga dwóch kondensatorów 6 pF. 

Analogicznie do HSE, pod wejście OSC32_IN może zostać podłączony zewnętrzny sygnał zegarowy o częstotliwości do 1 MHz.

Podstawowe rejestry konfiguracyjne – HSE jako SYSCLK

Konfiguracja rejestrów w celu użycia HSE jako SYSLCK bardzo zbliżona do poprzedniego przykładu, gdzie używaliśmy HSI. Musimy skonfigurować to, czy używamy kwarcu (rezonatora), czy zewnętrznego źródła zegara oraz wybrać odpowiednie źródło dla SYSCLK. Schemat przepływu sygnału zegarowego będzie wyglądał jak na schemacie poniżej.

Interesują nas zatem rejestry dotyczące HSE oraz SYSCLK.

Rejestr RCC_CR – rejestr kontrolny

Bit HSEBYP – wartość 0 oznacza, że wykorzystamy zewnętrzny kwarc, wartość 1 oznacza zewnętrzne źródło zegarowe na pinie OSC_IN. Ten bit należy ustawić przed włączeniem źródła HSE.

Bit HSERDY – status włączenia zegara HSE

Bit HSEON – bit uruchamiający zegar HSE

Rejestr RCC_CFGR – rejestr konfiguracyjny

Bity SWS[2:0] – status źródła sygnału dla zegara systemowego SYSCLK

Bity SW[2:0] – wybór źródła sygnału dla zegara systemowego SYSCLK

Konfiguracja Nucleo w celu użycia HSE

Płytka Nucleo ma spore możliwości rekonfiguracji ścieżek dzięki umieszczonym zworkom na płytce. Pozwoli nam to na wykorzystanie zewnętrznego źródła poprzez drobne zmiany na Nucleo. Mamy dwie możliwości wykorzystania HSE do taktowania mikrokontrolera na Nucleo: dodanie kwarcu (rezonatora) lub wykorzystanie zewnętrznego źródła – na Nucleo takim źródłem może być specjalnie w tym celu przygotowany sygnał z programatora oznaczony jako MCO o częstotliwości 8 MHz.

Dodanie zewnętrznego kwarcu (rezonatora) będzie przede wszystkim wymagało wlutowania go w miejsce przygotowane na płytce (pady X3). Musi to być kwarc w obudowie SMD-2 o wymiarach 3,2×2,5 mm. Dedykowanym kwarcem polecanym przez ST jest NX3225GD-8.000M- EXS00A-CG04874. Niestety może on być dość słabo dostępny w polskich sklepach. Poza kwarcem musimy wlutować jeszcze kondensatory C24 i C25 (10 pF) oraz rezystory 100 Ω w miejsca R33 i R34. Należy również usunąć zworki SB25 i SB27 oraz upewnić się, że zworka SB17 jest odlutowana.

W przypadku użycia sygnału zegarowego MCO, musimy zadbać, aby umieścić zworkę SB17 (wlutować rezystor 0 Ω lub zewrzeć pady), usunąć zworki SB25 i SB27 oraz upewnić się, że rezystory R33 i R34 są odlutowane.

Ze względu na problemy z dostępnością kwarcu w obudowie SMD-2 3,2×2,5mm, łatwiej będzie nam wykonać przykład używając MCO jako sygnału HSE.

[PROGRAM] Konfiguracja zegara HSE (MCO) jako źródła zegara SYSCLK

Zgodnie z zaznaczonym przebiegiem sygnału zegarowego, w pierwszej kolejności ustawiamy bit HSEBYP, a potem włączamy zewnętrzny oscylator HSE.

LL_RCC_HSE_EnableBypass();
LL_RCC_HSE_Enable();

Następnie poprzez sprawdzenie flagi HSERDY czekamy, aż oscylator się uruchomi. Bez tego dalsza część konfiguracji nie powiedzie się.

while(LL_RCC_HSE_IsReady() != 1)
	;

Następnie wybieramy HSE jako źródło dla zegara systemowego SYSCLK i ponownie czekamy, aż ta konfiguracja przebiegnie poprawnie, sprawdzając flagę SWS. 

LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSE);
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSE)
	;

Żeby sprawdzić poprawność działania naszego programu, dodamy konfigurację diody LD4 i w pętli while(1) zmianę jej stanu co 1 s. W pierwszej kolejności ustawimy częstotliwość zegara SYSCLK za pomocą funkcji LL_SetSystemCoreClock() oraz LL_Init1msTick(). Jest to potrzebne do uzyskania prawidłowych opóźnień.

LL_SetSystemCoreClock(8000000);
LL_Init1msTick(8000000);

Skąd bierzemy wartość 8000000? Jak już wspomniałem, sygnał MCO generowany przez STLink-a ma częstotliwość 8 MHz. Jeżeli chcielibyśmy wykorzystać zewnętrzny kwarc, należałoby wpisać w tym miejscu jego częstotliwość.

Teraz dodajemy podobnie jak w przykładzie z HSI konfigurację GPIO, aby sprawdzić, czy dioda będzie migała z poprawną częstotliwością.

LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
LL_GPIO_SetPinOutputType(LED_GREEN_GPIO_Port, LED_GREEN_Pin, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(LED_GREEN_GPIO_Port, LED_GREEN_Pin, LL_GPIO_PULL_NO);
LL_GPIO_SetPinSpeed(LED_GREEN_GPIO_Port, LED_GREEN_Pin, LL_GPIO_SPEED_FREQ_LOW);
LL_GPIO_SetPinMode(LED_GREEN_GPIO_Port, LED_GREEN_Pin, LL_GPIO_MODE_OUTPUT);
 
while (1)
{
    LL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
    LL_mDelay(1000);
}

Przypominam, że zgodnie z tym, co przedstawiłem w rozdziale o tworzeniu projektów pliku, w pliku main.h powinny znaleźć się includy oraz definicje pinu.

#define LED_GREEN_Pin LL_GPIO_PIN_5
#define LED_GREEN_GPIO_Port GPIOA

Po skompilowaniu kodu (“Project->Build Project” lub CTRL+B) i wgraniu na płytkę (“Run->Run”), dioda LD4 powinna zmieniać stan co 1 s.

Repozytorium GitHub

Chciałbyś otrzymywać na bieżąco informacje o nowych artykułach z kursu? Zapisz się do newslettera!

TO NIE TYLKO MAIL Z INFORMACJĄ O NOWEJ LEKCJI, ALE TAKŻE DODATKOWE MATERIAŁY. NIE PRZEGAP NOWEJ TREŚCI I DODATKOWYCH BONUSÓW. PRZEJDŹ DO STRONY KURSU I PODAJ SWÓJ ADRES E-MAIL. NIE ZAPOMNIJ POTWIERDZIĆ CHĘCI DOŁĄCZENIA W PIERWSZEJ WIADOMOŚCI!

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *