Układy STM32L4 są wyposażone w rozbudowane przetworniki ADC, które pozwalają na precyzyjny i szybki pomiar napięcia (do 5,33 mln pomiarów na sekundę przy 12-bitowym pomiarze).
Pora przećwiczyć podstawy pracy z ADC i DMA. Przy okazji poznamy też program STM Studio, a na koniec uruchomimy także DAC i komparator.
W trakcie wykonywania ćwiczeń z tej części kursu STM32L4 poznasz podstawy pracy z ADC – od pomiaru napięcia z jednego wejścia, przez pomiar wielu kanałów, aż po wykorzystanie DMA. Dowiesz się też, jak rezystancja źródła może zafałszować pomiary i jak rozwiązać ten problem w sposób cyfrowy. Oprócz tego dowiesz się również, jak podglądać dane z mikrokontrolerów na żywo w formie wykresów. Na koniec w ramach ciekawostki uruchomimy wbudowany w STM32L4 komparator i moduł DAC.
Przetworniki analogowo-cyfrowe w STM32L476RG
STM32L476RG wyposażony jest aż w trzy 12-bitowe przetworniki ADC – warto podkreślić, że mowa tu o trzech przetwornikach analogowo-cyfrowych, a nie o trzech wejściach analogowych. Przetworniki ADC1 i ADC2 posiadają na wejściu multipleksery, dzięki którym mogą mierzyć napięcie aż z 16 źródeł, a przetwornik ADC3 wyposażony jest w multiplekser z 12 wejściami.
Większość wejść analogowych jest współdzielona pomiędzy przetwornikami, w efekcie uzyskujemy łącznie możliwość pomiaru napięć na 24 wejściach mikrokontrolera.
Źródłem mierzonego napięcia mogą być 24 zewnętrzne wejścia analogowe lub sygnały wewnętrzne (napięcie referencyjne, wbudowany termometr analogowy, napięcie używane do podtrzymania zegara RTC oraz wyjścia dwóch DAC). Nie każdy przetwornik jest połączony z każdym źródłem sygnału, do tego dochodzi opcja pomiarów różnicowych i podział kanałów na wolne oraz szybkie. Na początku można się w tym trochę pogubić, ale wszystko będzie jasne po wykonaniu kilku ćwiczeń.
Przetworniki ADC pozwalają na połączenie cyfrowego świata mikrokontrolerów z elektroniką analogową
Warto podkreślić, że przetworniki ADC wbudowane do naszego mikrokontrolera to zaawansowane peryferia, które dają nam bardzo duże możliwości. Sytuacja jest znacznie bardziej skomplikowana niż np. w przypadku Arduino UNO, gdzie mamy jeden przetwornik i 6 wejść analogowych.
Pierwszy pomiar przez ADC
Zaczniemy od prostego projektu, dzięki któremu sprawdzimy, czy przetwornik działa poprawnie. Nie będziemy nic podłączać do mikrokontrolera – zmierzymy wartość napięcia referencyjnego. Powinno ono mieścić się w konkretnym przedziale, który producent opisał w dokumentacji. Upewnimy się więc, że wykonujemy pomiar w poprawny sposób.
Informacja o wartości napięcia referencyjnego w STM32L476
Konfiguracja projektu z ADC
Zaczynamy od wstępnego projektu z mikrokontrolerem STM32L476RG, który pracuje z częstotliwością 80 MHz. Uruchamiamy debugger i USART2 w trybie asynchronicznym, a pin PA5 konfigurujemy jako wyjście LD2. Dodajemy przekierowanie komunikatów wysyłanych przez printf – tak samo, jak robiliśmy to w części o komunikacji STM32L4 przez UART. Wystarczy dodanie pliku nagłówkowego:
Teraz możemy rozpocząć konfigurację przetwornika analogowo-cyfrowego. Wracamy do perspektywy CubeMX i z kategorii Analog wybieramy moduł ADC1. Pojawi się wtedy długa lista dostępnych wejść, z których możemy mierzyć napięcie.
Po przewinięciu całej listy zobaczymy, że pod polami IN1-IN15 są jeszcze dodatkowe opcje. Wybieramy tam pomiar napięcia referencyjnego, czyli zaznaczamy Vrefint Channel. Po aktywacji tej opcji w dolnej części okna pojawi się długa lista parametrów modułu ADC. Jest ich dużo, ale na razie nie musimy nic tam zmieniać – w tym przykładzie wystarczą wartości domyślne.
Aktywacja pomiaru napięcia referencyjnego przez ADC
Jeśli pierwszy raz w danym projekcie włączymy przetwornik analogowo-cyfrowy, to prawdopodobnie zobaczymy ikonę błędu na zakładce, w której konfigurujemy zegary. CubeMX wykrył, że włączyliśmy nowe peryferium (ADC1), a obecna konfiguracja zegarów nie może obsłużyć poprawnie tej opcji.
Ikona błędu na zakładce konfiguracji zegarów w CubeMX
Przechodzimy na tę zakładkę, ale nie musimy nic tam ręcznie zmieniać – od razu pojawi się propozycja automatycznego naprawienia konfiguracji zegarów. Oczywiście chętnie zgadzamy się na taką operację. Po chwili naszym oczom ukaże się zaktualizowana konfiguracja.
Mikrokontroler będzie cały czas pracował z maksymalną częstotliwością, tj. 80 MHz. Zmiany zostały wprowadzone jedynie w jednej z pętli PLL, ponieważ to jej sygnał jest wybrany na multiplekserze, który opisany jest jako ADC Clock Mux, do taktowania przetwornika ADC.
Nowa, automatyczna konfiguracja zegara dla ADC
Jeśli zgodziliśmy się na automatyczne ustawienie konfiguracji, to nasz przetwornik będzie taktowany z 32 MHz. Maksymalna częstotliwość pracy ADC wynosi 80 MHz, jednak podczas testów zostaniemy przy automatycznie ustawionych 32 MHz (większa częstotliwość nie będzie nam potrzebna).
Warto pamiętać, że im wyższa częstotliwość, tym większy pobór prądu, ale z drugiej strony przetwornik działa szybciej, więc otrzymujemy więcej próbek na sekundę.
Kod programu korzystającego z ADC
Zapisujemy projekt i przechodzimy do edycji kodu. Przed pierwszym pomiarem powinniśmy wykonać kalibrację przetwornika ADC – można to zrobić wywołując funkcję HAL_ADCEx_Calibration_Start. Wymaga ona dwóch parametrów: wskaźnika do struktury opisującej przetwornik (w tym przypadku jest to zmienna hadc1) oraz informacji na temat trybu kalibracji przetwornika. Do wyboru mamy dwa tryby kalibracji: zwykły (ADC_SINGLE_ENDED) i różnicowy (ADC_DIFFERENTIAL_ENDED).
Nie wszystkie układy analogowe zwracają wyniki jako napięcie, które powinno być mierzone względem masy. Dobrym przykładem jest sytuacja, w której używamy mostka pomiarowego. Wówczas wynikiem jest różnica między dwoma wyjściami, a nie jednym wyjściem i masą. Aby odczytać taki wynik, możemy wykonać konwersję za pomocą ADC dwa razy i odjąć od siebie uzyskane wartości.
Niestety oznacza to wykonanie pomiaru w dwóch różnych momentach (jeśli sygnał nie jest stały to otrzymamy zły wynik). Wtedy przydaje się tryb różnicowy, który wykonuje podobne zadanie, ale odczyt następuję w tej samej chwili.
Nie będziemy się teraz zajmować trybem różnicowym w praktyce, więc wywołujemy w kodzie (przed pętlą while) funkcję kalibracji z takimi parametrami:
Domyślnie przetwornik działa w trybie pojedynczego pomiaru, więc przed każdym pomiarem musimy wywołać funkcję HAL_ADC_Start, która go rozpocznie. Funkcja ta przyjmuje jeden parametr – wskaźnik do zmiennej opisującej przetwornik.
Po uruchomieniu pomiaru musimy poczekać na jego zakończenie. Tutaj konieczne jest użycie funkcji HAL_ADC_PollForConversion, która przyjmuje dwa parametry. Pierwszy to oczywiście wskaźnik do zmiennej opisującej nasz przetwornik, a drugi to liczba milisekund, które możemy czekać na wykonanie pomiaru. Podczas testów nie zależy nam na czasie, więc posłużymy się „maksymalnie długim czasem”, czyli stałą HAL_MAX_DELAY, z której korzystaliśmy już w trakcie pracy z UART-em.
Na koniec pozostaje nam odczytać pomiar z przetwornika za pomocą funkcji HAL_ADC_GetValue, która przyjmuje tylko jeden parametr, czyli wskaźnik do zmiennej opisującej przetwornik. Łącząc te trzy funkcje, możemy napisać program, który będzie cyklicznie dokonywał pomiaru, a wyniki będą do nas wysyłane przez UART. Kod, który umieszczamy wewnątrz pętli while, może wyglądać następująco:
C
1
2
3
4
5
6
7
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
uint32_t value=HAL_ADC_GetValue(&hadc1);
printf("ADC = %lu\n",value);
HAL_Delay(250);
Po uruchomieniu tej wersji programu w terminalu powinniśmy zobaczyć ciąg wartości zbliżonych do poniższych. Mogą występować pewne różnice i wahania, ale odczyty powinny być w miarę stabilne.
Pomiar napięcia referencyjnego na STM32L4
Jak interpretować wynik z ADC?
Pomiar wykonywany jest względem napięcia referencyjnego ADC. W przypadku mikrokontrolera, który używamy w tym kursie jest to napięcie zasilania, czyli 3,3 V. Układ ten występuje również w większych obudowach, wtedy znaleźć można jeszcze dodatkowe wyprowadzenia (VREF+/VREF-), dzięki którym możliwe jest podłączenie innego napięcia odniesienia dla ADC/DAC.
Na potrzeby tego artykułu zakładamy więc, że napięcie referencyjne dla ADC to 3,3 V.
Przetwornik ADC jest 12-bitowy, więc wynik to liczba z zakresu od 0 do 4095 (2 do potęgi 12 = 4096 wartości). Jeśli na wejściu przetwornika pojawi się napięcie VADC, to powinniśmy odczytać wartość:
ADC = VADC / 3,3 V * 4096
Wiemy już z dokumentacji, że wewnętrzne napięcie referencyjne (nie mylić z napięciem referencyjnym ADC) powinno wynosić 1,2 V, czyli jeśli wszystko jest dobrze, pomiar z programu powinien wskazywać 1,2 V / 3,3 V * 4096, czyli 1489 jednostek – zgadza się to idealnie z powyższym zrzutem ekranu.
Dla ułatwienia możemy jeszcze edytować nasz program, tak aby sam dodatkowo przeliczał wyniki ADC na napięcie w woltach. Nowa wersja wnętrza pętli while:
C
1
2
3
4
5
6
7
8
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
uint32_t value=HAL_ADC_GetValue(&hadc1);
floatvoltage=3.3f*value/4096.0f;
printf("ADC = %lu (%.3f V)\n",value,voltage);
HAL_Delay(250);
Trzeba oczywiście pamiętać o tym, że używanie liczb zmiennoprzecinkowych wymaga zmiany ustawień (Project > Properties > C/C++ Build > Settings > MCU Settings > Use float with printf...) – więcej informacji na ten temat znaleźć można w poradniku opisującym korzystanie z printf na STM32L4.
Efekt działania nowej wersji programu
Umiemy już odczytać wartość napięcia referencyjnego, potrafimy też przeliczyć wynik na napięcie, a otrzymane wartości pokrywają się z obliczonymi teoretycznie. Możemy więc spokojnie przejść dalej i zmierzyć napięcie z jakiegoś zewnętrznego źródła.
Gotowe zestawy do kursów Forbota
Komplet elementów Gwarancja pomocy Wysyłka w 24h
Zamów zestaw elementów i wykonaj ćwiczenia z tego kursu! W komplecie płytka NUCLEO-L476RG oraz m.in. wyświetlacz graficzny, joystick, enkoder, czujniki (światła, temperatury, wysokości, odległości), pilot IR i wiele innych.
Masz już zestaw? Zarejestruj go wykorzystując dołączony do niego kod. Szczegóły »
Pomiar napięcia zewnętrznego przez STM32
Na początek bardzo ważna uwaga: do wejścia przetwornika analogowo-cyfrowego można podłączyć napięcie z zakresu od 0 V do 3,3 V. Wyjście poza ten zakres może spowodować nieodwracalne uszkodzenie mikrokontrolera. Należy być więc szczególnie ostrożnym, jeśli w układzie korzystamy z dwóch napięć: 3,3 V i 5 V.
Większość wejść układów STM32 toleruje 5 V, ale tyczy się to tylko pracy jako wejścia cyfrowe. Przypadkowe podanie 5 V na wejście przetwornika ADC uszkodzi mikrokontroler.
W ramach kolejnego eksperymentu podłączymy do przetwornika ADC potencjometr, jednak oczywiście musimy podłączyć go między masę a 3,3 V – w takim przypadku, kręcąc potencjometrem, będziemy regulować napięcie w bezpiecznym zakresie. Środkowe wyprowadzenie potencjometru podłączamy do wejścia PC0, bo jest to jednocześnie kanał IN1 przetwornika ADC1.
Schemat ideowy i montażowy układu testującego przetwornik ADC
Po podłączeniu potencjometru wracamy do CubeMX i zmieniamy konfigurację ADC1. Zaczynamy od wyłączenia pomiaru napięcia referencyjnego, które włączyliśmy podczas poprzedniego przykładu. Tym razem jako źródło pomiarów ADC wybieramy wejście IN1 i ustawiamy je w trybie IN1 Single-ended. CubeMX automatycznie ustawi konfigurację pinu PC0 na tryb ADC1_IN1.
Ustawienia przetwornika ADC w CubeMX
Na początek zostawiamy wszystkie ustawienia domyślne. Nasza konfiguracja jest bardzo podobna do przetestowanej poprzednio – jedyna różnica to użycie wejścia IN1 zamiast napięcia referencyjnego. Po zapisaniu zmian CubeMX wygeneruje nową wersję projektu. Nie musimy jednak nic zmieniać w kodzie. Kompilujemy i wgrywamy projekt – obracanie osi potencjometru powinno wpływać na wyniki.
Zmiana wartości napięcia w zależności od ustawień potencjometru
Jeśli mamy pod ręką miernik, to warto porównać napięcie zmierzone multimetrem i wyniki pomiarów wyświetlone w terminalu. Wyniki te mogą się dość znacznie różnić – szczególnie gdy potencjometr jest w połowie swojego zakresu. W naszym przypadku multimetr wskazywał 1,58 V, a program wyświetlał wartość w okolicach 1,49 V. Różnica to 0,1 V – niby mało, ale „procentowo” pomiar jest zafałszowany bardzo mocno.
Pomiar napięcia na wyjściu potencjometru
Jeśli eksperymentalnie do układu dodamy kondensator 100 nF (między masę a PC0), to zobaczymy, że wyniki z terminala będą już podobne do tych, które uzyskujemy za pomocą multimetru. Okazuje się, że przyczyną takiego zjawiska jest rezystancja źródła mierzonego sygnału. Potencjometr działa tu oczywiście jako dzielnik napięcia o zmiennym oporze, a w dokumentacji mikrokontrolera znajdziemy tabelę opisującą zależność czasu próbkowania od rezystancji źródła (dla zegara 80 MHz).
Informacja o zależności czasu próbkowania ADC od oporu źródła
Podczas próbkowania napięcia mikrokontroler (w uproszczeniu) ładuje wewnętrzny kondensator. Zajmuje to trochę czasu, a im większa jest rezystancja, przez którą jest on ładowany, tym więcej czasu potrzeba na jego naładowanie. Gdy nasz potencjometr 10 k ustawimy w połowie, rezystancja takiego źródła będzie wynosiła (zgodnie z twierdzeniem Theveniena) około 2,5 k (tak, 2,5 k, a nie 5 k).
Dotychczas używaliśmy domyślnego czasu próbkowania, który jest ustawiony na wartość minimalną, czyli 2,5 cyklu. Zegar przetwornika ma częstotliwość 32 MHz, więc próbkowanie trwa raptem 78,125 ns – a to za mało, żeby przy rezystancji źródła wynoszącej 2,5 k w pełni naładować kondensator w naszym przetworniku. Dodanie kondensatora 100 nF sprawiło, że w momencie wykonywania pomiaru prąd mógł z niego szybko przepłynąć do kondensatora wbudowanego w przetwornik, a kondensator 100 nF doładowywał się między pomiarami.
Proces ten przebiegał na tyle szybko, aby układ mógł poprawnie mierzyć napięcie wejściowe.
Jednak nie zawsze mamy możliwość, aby dodać taki kondensator. W wielu przypadkach znacznie lepiej byłoby rozwiązać ten problem „cyfrowo”. Można to na szczęście zrobić bardzo łatwo – wystarczy, że mikrokontroler będzie poświęcał więcej czasu na dokonanie pomiarów.
Odłączamy kondensator 100 nF i wracamy do perspektywy CubeMX. Przechodzimy do modułu ADC1 i w parametrach (dolna część okna) odnajdujemy grupę ADC_Regular_ConversionMode, w jej wnętrzu odszukujemy grupę parametrów Rank 1. Rozwijamy ją i edytujemy Sampling Time – wybieramy 640,5 cykli zegarowych (jest to wartość maksymalna).
Zmiana czasu próbkowania sygnału przez przetwornik ADC
Po tej prostej zmianie zapisujemy projekt i wgrywamy nową wersję kodu na mikrokontroler. Tym razem wyniki odczytywane przez mikrokontroler powinny być zbliżone do pomiarów z woltomierza, nawet jeśli nie dodamy kondensatora 100 nF.
Ile czasu zajmuje pomiar ADC?
Przetwornik analogowo-cyfrowy działa niejako w dwóch etapach. W pierwszym wykonuje próbkowanie, czyli zapamiętuje mierzone napięcie, natomiast w drugim przekształca zapamiętane napięcie na postać cyfrową, czyli liczbę, którą odczytujemy później jako wynik.
W przypadku korzystania z 12 bitów rozdzielczości ADC etap próbkowania i zapamiętywania (tzw. sample&hold) może zajmować od 2,5 do 640,5 cyklu zegara, natomiast przetwarzanie na postać cyfrową i trwa zawsze 12,5 cyklu. Cała praca przetwornika zajmuje więc od 15 do 653 cykli zegara. Znając częstotliwość taktowania przetwornika (32 MHz), możemy policzyć, ile czasu zajmie wykonanie całego pomiaru. Nasza pierwsza konfiguracja używała 2,5 cyklu próbkowania, więc cała konwersja wymagała 15 cykli zegara. Taktowanie 32 MHz przekłada się na sygnał o okresie 1/32 μs, czyli:
15 × 1/32 μs = 0,47 μs
Gdybyśmy wykonywali pomiary jeden po drugim, w ciągu sekundy moglibyśmy wykonać około 2,13 miliona pomiarów – nie trzeba tego z niczym porównywać, bo od razu widać, że to dużo, bardzo dużo. Gdyby nasz przetwornik był taktowany z maksymalną częstotliwością (80 MHz), wówczas pomiar zajmowałby 0,1875 μs (15 * 1/80 μs), a to oznacza 5,33 miliona pomiarów na sekundę i taka właśnie wartość jest podana w dokumentacji używanego przez nas mikrokontrolera.
Fragment dokumentacji informujący o maksymalnej liczbie pomiarów
Po zmianach konfiguracji projektu próbkowanie zajmuje 640,5 cyklu zegara, do tego dodajemy 12,5 μs na konwersję, czyli razem 653 cykle. W takim przypadku całe przetwarzanie zajmuje:
653 × 1/32 μs = 20,4 μs
Jesteśmy więc w stanie wykonać 49 tysięcy pomiarów na sekundę. To znacznie mniej niż poprzednio, ale za to uzyskujemy poprawny wynik nawet ze źródła o wyższej impedancji. W kolejnych przykładach będziemy zawsze używali takich ustawień.
Ciągły odczyt z przetwornika ADC
Umiemy już wykonywać pojedynczy pomiar, wiemy, że po wywołaniu HAL_ADC_Start rozpoczyna się pomiar, na który możemy czekać, wywołując funkcję HAL_ADC_PollForConversion. Często jednak po zakończeniu jednego pomiaru chcielibyśmy od razu zacząć następny. Dzięki temu nie będziemy musieli czekać na kolejne dane, a co ważniejsze – pomiary będą wykonywane w stałych odstępach czasu.
Wracamy do CubeMX, wybieramy przetwornik ADC i dokonujemy dwóch zmian. Pierwsza to włączenie trybu ciągłego (Continuous Conversion Mode: Enabled). Druga zmiana to ustawienie przetwornika w taki sposób, aby nadpisywał poprzednie pomiary, nie zwracając uwagi na to, czy zdążyliśmy je odebrać (Overrun behaviour: Overrun data overwritten). Domyślne ustawienie (Overrun data preserved) sprawia, że układ nie aktualizuje pomiarów, dopóki nie odczytamy poprzedniego pomiaru.
Zmiana ustawień przetwornika ADC
Zapisujemy projekt, aby wygenerować nowy szkielet programu, i przechodzimy do edycji kodu. Teraz będzie jeszcze prościej – wystarczy skalibrować przetwornik i raz uruchomić pomiar. Wyniki możemy od teraz odczytywać w dowolnym momencie za pomocą funkcji HAL_ADC_GetValue. Nie musimy na nic czekać ani każdorazowo „ręcznie” uruchamiać pomiarów.
Nowa wersja programu powinna działać tak samo jak poprzednia:
Efekt działania najnowszej wersji programu
Odczyt z wielu kanałów ADC
Pomiar jednej wartości jest oczywiście bardzo ciekawy, ale mamy aż 16 kanałów w przetworniku, więc warto byłoby nauczyć się wykonywać pomiar z więcej niż jednego. Zacznijmy od podłączenia drugiego potencjometru – tym razem jego środkowa nóżka powinna łączyć się z pinem PC1.
Schemat ideowy i montażowy do kolejnego ćwiczenia
Wracamy do CubeMX i zmieniamy konfigurację przetwornika. Zakładając, że nadal pracujemy na tym samym projekcie, zaczynamy od wyłączenia trybu ciągłego (Continuous Conversion Mode: Disabled). Następnie włączamy kanały IN1 oraz IN2, ustawiając im opcję Single-ended. CubeMX oczywiście sam skonfiguruje piny: PC0 jako ADC1_IN1 oraz PC1 jako ADC1_IN2.
Aktywacja dwóch kanałów przetwornika ADC w STM32L4
To nie koniec zmian, które trzeba wyklikać. Co ważne, część opcji pojawi się dopiero w wyniku innych, dlatego warto działać zgodnie z poniższym opisem. Przewijamy okienko z parametrami przetwornika ADC1, aż znajdziemy się w grupie ADC_Regular_ConversionMode, zmieniamy tam opcję Number Of Conversion na 2. Nasz przetwornik ADC może wykonywać całą sekwencję konwersji (czyli odczytów z różnych kanałów) – jej długość może wynosić aż 16.
Można również dowolnie ustawiać kolejność odczytywanych kanałów!
Po zmianie wartości Number Of Conversion pojawi się grupa parametrów Rank 2, które można tutaj rozumieć jako drugi pomiar. Wcześniej mieliśmy tylko jedną konwersję w sekwencji, więc dostępne były parametry jedynie dla Rank 1. Każdemu pomiarowi możemy przypisać kanał, który zostanie użyty, oraz liczbę cykli przewidzianych na próbkowanie napięcia.
Dla pierwszego pomiaru ustawiamy parametry:
Channel: Channel 1
Sampling Time: 640.5 Cycles
Dla drugiego pomiaru wybieramy drugi kanał, czyli:
Channel: Channel 2
Sampling Time: 640.5 Cycles
W obu przypadkach dostępne jest jeszcze pole offset, którego nie będziemy tutaj używać. Pozwala ono odjąć stałą wartość (offset) od wyników pomiaru z danego kanału.
Warto też sprawdzić, czy CubeMX sam, automatycznie, aktywował jeszcze tryb Scan Conversion Mode, który dostępny jest w sekcji ADC_Settings. Ustawienie Number Of Conversion na wartość większą od 1 włącza tryb skanowania, a ponowne ustawienie na 1 wyłącza go.
Nowa wersja ustawień przetwornika ADC w CubeMX
Zapisujemy nową wersję projektu, generujemy szkielet programu i przechodzimy do edycji kodu. Kod oczywiście musi zaczynać się od kalibracji przetwornika ADC. Nie używamy już trybu ciągłego, więc będziemy przy każdym pomiarze wywoływać funkcję HAL_ADC_Start, natomiast przetwornik sam będzie już zmieniał kanały, na których wykonywany jest pomiar. Właśnie dlatego w kodzie będziemy wykonywać pomiar dwa razy, aby wykonać całą sekwencję:
Po kompilacji programu wszystko powinno działać zgodnie z oczekiwaniami – powinniśmy otrzymywać pomiary z dwóch wejść ADC. Jeśli okaże się, że pomiary są takie same, to znaczy, że został popełniony błąd podczas konfiguracji przetwornika (najpewniej podczas zmiany ustawień Rank 2):
Pomiar napięcia na dwóch wejściach ADC
Pomiary przy użyciu DMA
W poprzednim przykładzie zobaczyliśmy, jak programowo można odczytywać dane z dwóch kanałów. Takie działanie wymaga sporo pracy ze strony procesora, a gdybyśmy chcieli użyć trybu ciągłego to wymagałoby to jeszcze więcej pracy, żeby nie tracić danych (w przypadku jednego kanału nie było to dla nas problemem, ale teraz moglibyśmy się pogubić, który kanał odczytujemy).
Z pomocą przychodzi nam mechanizm nazywany DMA, czyli Direct Memory Access. Możemy go sobie wyobrazić jako taki dodatkowy, prosty procesor, który potrafi kopiować dane z modułów peryferyjnych, czyli np. z ADC do pamięci; może również kopiować dane w przeciwną stronę, przykładowo z bufora w pamięci do portu szeregowego, albo dane z pamięci w inny jej fragment.
Zaletą DMA jest to, że działa niejako równolegle z mikrokontrolerem, więc podobnie jak w maszynach wielordzeniowych możemy dalej wykonywać program, gdy procesor pomocniczy realizuje swoje zadanie.
Zobaczymy teraz, jak skonfigurować DMA, żeby odczytywał dane z przetwornika analogowo-cyfrowego i zapisywał wyniki bezpośrednio do pamięci, czyli do naszych zmiennych. Wracamy do perspektywy CubeMX i ustawień ADC1. Następnie przechodzimy do zupełnie nowej dla nas zakładki, która nazywa się DMA Settings. Klikamy przycisk Add i w kolumnie DMA Request ustawiamy opcję ADC1. W dolnej części okna pojawią się parametry nowego kanału DMA – ustawiamy tam tryb na Circular, a pozostałe opcje zostawiamy bez zmian.
Ustawienia DMA dla ADC1 w STM32L4
Tryb Circular oznacza, że DMA będzie działał „w kółko”, czyli że po skopiowaniu wyników we właściwe miejsce zacznie robić to samo. W dolnej części okna mamy dwa parametry – moduły peryferyjne (Peripheral) oraz pamięć RAM (Memory), przy której aktywowana jest opcja inkrementacji.
Przy takiej konfiguracji adresy odnoszące się do modułu nie będą inkrementowane, więc za każdym razem będziemy odczytywać dane z tego samego rejestru sprzętowego. Odpowiada to wywołaniom funkcji HAL_ADC_GetValue. Natomiast adresy w pamięci są zwiększane za każdym razem, dzięki czemu kolejne wartości trafią pod inny adres (jeśli użyjemy tablicy, to trafią do kolejnych elementów).
Pod tymi opcjami znajdują się informacje na temat rozmiaru danych, które mają być kopiowane przez DMA. Rozmiar danych ustawionych na pół słowa (ang. half word) oznacza wartość 16-bitową. Dostępne są też bajty (8 bitów) i pełne słowa (32 bity).
Teraz możemy wrócić do konfiguracji przetwornika. Musimy włączyć tryb ciągły i uruchomić żądania DMA. Na liście parametrów ADC1 ustawiamy następujące wartości:
Continuous Conversion Mode: Enabled
DMA Continuous Requests: Enabled
Zmiana ustawień przetwornika DMA
Na koniec warto jeszcze popatrzeć na zakładkę NVIC Settings. Zobaczymy w niej nowe przerwanie „DMA1 channel1 global interrupt”, które jest automatycznie włączone (nie można go nawet wyłączyć). Zgodnie z informacją z poprzednich części kursu dobrą praktyką jest zmniejszenie priorytetu tego typu przerwań. Przechodzimy do modułu NVIC i zmieniamy priorytet, np. z 0 na 2.
Teraz nasza konfiguracja jest już gotowa, czas zapisać zmiany i przejść do edycji kodu. Zamiast funkcji HAL_ADC_Start będziemy wywoływali funkcję HAL_ADC_Start_DMA, która – jak łatwo się domyślić – właśnie wspiera użycie DMA. Pierwszy parametr tej funkcji to wskaźniki do zmiennej przechowującej informacje o przetworniku ADC. Drugi parametr wskazuje adres bufora, do którego DMA skopiuje dane z przetwornika ADC. Trzeci parametr to liczba danych do skopiowania.
Zanim przejdziemy do pokazania pełnego kodu, najpierw małe wyjaśnienie. Podczas konfiguracji DMA ustawiliśmy typ danych w pamięci na „Half Word”, więc będziemy przechowywać wyniki w zmiennych 16-bitowych, czyli uint16_t. Jednak funkcja spodziewa się wskaźnika do typu uint32_t. Możemy albo zignorować ostrzeżenie kompilatora, albo użyć jawnego rzutowania.
Sam bufor deklarujemy jako:
C
1
volatilestaticuint16_t value[2];
Użycie modyfikatora volatile jest ważne, ponieważ w programie nie będziemy nigdy sami zapisywać danych do tej tablicy – wartość będzie zmieniana w tle przez DMA. Bez dodania volatile kompilator mógłby potraktować tablicę value jako stałą, zawsze równą zeru.
Druga ważna sprawa to umiejscowienie bufora w pamięci. Zmienne lokalne są tworzone na stosie, więc po wyjściu z funkcji zajmowana przez nie pamięć jest zwalniana (albo raczej używana przez inne zmienne lokalne). W związku z tym, że mechanizm DMA będzie cały czas zapisywał dane do bufora, powinniśmy utworzyć zmienną jako globalną (albo lokalną statyczną). Dzięki temu pamięć dla niej będzie przydzielona „na zawsze” i DMA nie nadpisze danych należących do innych zmiennych.
Skomplikowany opis sprowadza się jednak do bardzo prostego programu:
Widać tutaj, że dzięki wykorzystaniu DMA mamy jeszcze mniej „do programowania” niż poprzednio. Kalibrujemy przetwornik i uruchamiamy pomiar z użyciem DMA, a cała reszta dzieje się w tle. W kodzie możemy po prostu odwoływać się do konkretnych zmiennych, mając pewność, że DMA podstawi do nich aktualne odczyty z ADC.
Pułapki pracy z DMA
Musimy jednak wyjaśnić pewną sztuczkę, którą przemyciliśmy w poprzednim przykładzie. Okazuje się, że wybranie opcji czasu próbkowania (ustawiliśmy na 640,5 cyklu) jest ważne nie tylko ze względu na rezystancję źródła. W naszym przypadku DMA mógł działać całkowicie w tle, bo nie wykorzystywaliśmy powiadomienia o zakończeniu transmisji, a „biblioteka HAL” wysyła domyślnie takie powiadomienie, nawet jeśli z niego nie korzystamy.
Mówiąc dokładniej, włącza ona dwa przerwania – pierwsze zgłaszane w połowie transmisji i drugie po jej zakończeniu. To przydatna funkcjonalność, ale w związku z tym, że przesyłamy tylko dwa wyniki, to przerwania pojawiają się po odczycie każdej wartości. Nie napisaliśmy procedur obsługi tych przerwań, ale kod w bibliotece i tak je odbiera i zajmuje nieco czasu. Przy domyślnych ustawieniach optymalizacji zajmuje to około 3,5 μs.
Oznacza to, że jeśli pomiar trwałby tyle (lub mniej), to układ poświęcałby 100% czasu na obsługę przerwań, a i tak mógłby nie nadążać i nie miałby czasu na wykonywanie programu.
Przy naszym ustawieniu przetwornika próbki są odczytywane co ok. 20 μs, więc między przerwaniami jest sporo czasu na wykonywanie programu. Jednak gdyby zależało nam na pełnym wykorzystaniu możliwości przetwornika analogowo-cyfrowego, musielibyśmy nie tylko zadbać o odpowiednie źródło sygnału, ale również zoptymalizować program lub wyłączyć te przerwania. W tym celu należy przejść w CubeMX do modułu NVIC i odznaczyć opcję Force DMA channels Interrupts – dopiero wtedy Cube pozwoli wyłączyć wspominane przerwania. Jeśli nie odznaczymy tej opcji to przerwania od DMA będą domyślnie włączone i nie da się ich odznaczyć.
Domyślnie włączone przerwania od DMA
Użycie DMA ma ogromne zalety przy transferze większych ilości danych. Natomiast opisany tutaj przypadek należy potraktować jako przykład, który pozwolił na przetestowanie tego mechanizmu. Oczywiście można z niego korzystać w taki sposób, ale trzeba mieć na uwadze, że w niektórych zastosowaniach może on utrudniać, a nie ułatwiać pracę.
Oversampling, czyli filtrowanie pomiarów
Jeśli ustawimy potencjometry w stałym położeniu i popatrzymy na dane, to dość szybko odkryjemy, że kolejne wyniki różnią się od siebie (nieznacznie, ale jednak). W idealnym świecie wyniki powinny być równe, niestety rzeczywistość nie zawsze nadąża za ideałami. Parametr określający rozrzut wyników pomiaru niezmiennej wartości nazywamy precyzją. Czyli im wyższa precyzja, tym wyniki powinny być bardziej do siebie zbliżone. Niestety to, co widzimy, nie jest przesadnie precyzyjne.
Różne pomiary podczas stałego ustawienia potencjometrów
Prostym sposobem poprawienia precyzji jest uśrednianie wyników. Moglibyśmy wykonać pomiar np. 16 razy, a następnie policzyć średnią. Przetwornik wbudowany w nasz mikrokontroler może taką operację wykonać samodzielnie (w pełni sprzętowo).
W ramach testu wracamy do perspektywy CubeMX i ponownie edytujemy konfigurację przetwornika ADC1. W grupie parametrów ADC_Regular_ConversionMode odszukujemy opcję Enable Regular Oversampling. Jeśli ją właczymy, to na liście parametrów pojawią się kolejne pozycje, które ustawiamy w następujący sposób:
Oversampling Right Shift: 4 bit shift for oversampling
Oversampling Ratio: Oversampling ratio 16x
Parametr Oversampling ratio określa, ile pomiarów wykonujemy. Może mieć wartość od 2 do 256, w tym przykładzie wybraliśmy 16, ale nie ma to żadnego konkretnego uzasadnienia. Trzeba jednak mieć na uwadze, że wyniki są sumowane w 20-bitowym rejestrze, a następnie są zwracane jako wartość 16-bitowa. To oznacza, że mogłoby nastąpić przepełnienie rejestru. Warto więc przesunąć wynik o zadaną liczbę bitów (przesunięcie w prawo o 1 bit to po prostu dzielenie przez 2). Ustawiliśmy przesunięcie wyniku o 4 bity, co oznacza podzielenie przez 16.
Czyli najpierw wykonujemy 16 pomiarów, dodajemy do siebie, a na koniec dzielimy wynik przez 16. Jak łatwo odgadnąć, powinniśmy uzyskać w ten sposób uśrednione wyniki.
Nie musimy nic już zmieniać w kodzie. Wystarczy zapisać projekt, skompilować i przesłać go na Nucleo. Nadal odczytane wartości będą mogły się delikatnie wahać, ale będą znacznie precyzyjniejsze od tego, co otrzymywaliśmy poprzednio.
Sprzętowe filtrowanie pomiarów z ADC
Wzrosła precyzja pomiaru, ale odbyło się to oczywiście za cenę czasu działania, bo musimy pobierać nie 1 próbkę, ale 16. Takie rozwiązanie będzie pomocne w wielu zastosowaniach – zwłaszcza jeśli przypomnimy sobie, jak wiele pomiarów robi nasz mikrokontroler w ciągu 1 s.
Obsługa joysticka na STM32L4
Potencjometry to mało widowiskowe elementy, ale mogliśmy dzięki nim opanować podstawy, które teraz wykorzystamy do obsługi joysticka. Element ten działa dokładnie tak samo jak dwa połączone ze sobą (mechanicznie) potencjometry. Bez żadnej edycji kodu programu możemy teraz zwyczajnie wpiąć moduł joysticka: VRx do PC0, a VRy do PC1.
Jedno z wyprowadzeń modułu joysticka ma oznaczenie +5 V, my jednak podłączamy je do napięcia 3,3 V, ponieważ podłączenie go do 5 V może uszkodzić mikrokontroler.
Schemat ideowy i montażowy dla układu testującego moduł joysticka
Mamy już napisany program, który obsługuje zarówno DMA, jak i nadpróbkowanie (oversampling). Wykonamy w nim dwie zmiany: zmienną value, w której DMA zapisuje wyniki, nazwiemy teraz joystick oraz zapiszemy ją jako zmienną globalną. Dzięki tym zmianom za chwilę będzie nam łatwiej odczytać jej wartość w programie STMStudio, który będziemy testować. Zmienimy też format wyświetlanych danych, tak by lepiej pasowały do nowego projektu.
Przed funkcją main umieszczamy więc deklarację zmiennej:
C
1
volatilestaticuint16_t joystick[2];
Możemy zostawić modyfikator static, chociaż teraz będzie miał nieco inne znaczenie – nasza zmienna będzie globalna, ale dostępna tylko w aktualnym module (pliku).
Po wgraniu tej wersji programu wszystko powinno działać praktycznie tak samo – zwiększy się tylko liczba wysyłanych próbek oraz opis zmierzonych wartości (teraz jest to pozycja X i Y), które będą się zmieniały zależnie od ustawień joysticka.
Odczyt informacji z joysticka
Rysowanie wykresów w STM Studio
Przesyłanie danych tekstowych do terminala ma swoje uroki, ale w dobie graficznych interfejsów wiele osób oczekiwałoby pewnie czegoś więcej. Z pomocą przychodzi nam program STM-STUDIO-STM32, który za darmo pobierzemy ze strony firmy STMicroelectronics. Jest to narzędzie, które w ekstremalny sposób ułatwia prezentowanie danych z wnętrza mikrokontrolera w graficzny sposób.
Jeśli nie utworzymy podczas instalacji skrótu do programu, to w celu jego późniejszego uruchomienia konieczne będzie wejście do katalogu C:\Program Files (x86)\STMicroelectronics\STMStudio i uruchomienie pliku STMStudio.jar.
Program ten do poprawnego działania wymaga Java Runtime Environment. Jeśli nie posiadamy tego środowiska, to podczas instalacji STM-STUDIO-STM32 zostaniemy poproszeni o pobranie brakującego programu, a później całość przebiegnie już standardowo.
Okno główne programu STM-STUDIO-STM32
Po uruchomieniu programu przechodzimy do File > Import variables. W górnej części okna zobaczymy opcję wyboru pliku wykonywalnego (ang. executable file). Musimy tam wskazać plik z programem, który mamy właśnie wgrany do mikrokontrolera, a plik ten znajdziemy w naszej przestrzeni roboczej.
Ścieżkę projektu można sprawdzić, klikając w niego prawym klawiszem myszy w eksploratorze projektów STM32CubeIDE, a następnie wybierając opcję Show In > System Explorer.
Następnie przechodzimy do folderu, w którym jest nasz aktualny projekt, dalej do folderu Debug i finalnie wybieramy tam plik z rozszerzeniem elf. Plik zostanie błyskawicznie zaimportowany, a naszym oczom ukaże się mnóstwo zmiennych, które są w nim używane. W polu Show symbols containing wpisujemy nazwę naszej zmiennej, czyli joystick. Na liście zobaczymy tylko pierwszy element tablicy, czyli joystick[0]. Zaznaczamy więc opcję Expand table elements – od teraz powinniśmy widzieć również drugi element tej tablicy.
Podgląd zmiennych używanych w programie
Zaznaczamy oba elementy tablicy, klikając w nie lewym przyciskiem myszy z wciśniętym Ctrl. Dalej klikamy Import i zamykamy okno dialogowe. Od teraz dwa elementy tablicy joystick będą widoczne w lewym górnym rogu STM-STUDIO-STM32.
Zmienne zaimportowane do STM-STUDIO-STM32
Program automatycznie przypisał kolory do zmiennych, ale oczywiście możemy je dowolnie zmienić. Gdy ustawimy ulubione kolory, możemy zaznaczyć obie zmienne, a następnie kliknąć w nie prawym przyciskiem myszy i wybrać opcję Send To > VarViewer1.
Program STM-STUDIO-STM32 pozwala na prezentowanie danych w różnych formatach. W lewej dolnej części okna znajdziemy ustawienia, a po wybraniu zakładki VarViewer1 będziemy mogli ustawić sposób prezentacji danych. Zacznijmy od wybrania widoku tabelarycznego – z listy przy polu VarViewer1 as wybieramy opcję Table.
Zmiana widoku na tabelaryczny
Teraz wystarczy, że naciśniemy przycisk z zieloną strzałką, który znajduje się w górnej części okna. Dane z naszego mikrokontrolera pojawią się „na żywo” w tym programie i nie będzie to miało nic wspólnego z tym, że wysyłamy je przez UART.
Działanie tego narzędzia można przyrównać do debuggera, który pozwala na podglądanie zawartości dowolnych zmiennych wprost z pamięci mikrokontrolera!
Po zmianie sposobu wyświetlania na Bar Graph zobaczymy nieco ciekawszą możliwość prezentacji danych (za pomocą scrolla możliwa jest zmiana zakresu wyświetlanych liczb).
Podgląd danych za pomocą wykresu słupkowego
Warto jeszcze wypróbować wyświetlanie danych w postaci krzywych (Curve). Zobaczymy wówczas wykres, który sprawdziłby się świetnie, gdybyśmy chcieli śledzić dane z jakichś czujników.
Podgląd danych w formie wykresu z krzywych
Na koniec jeszcze jeden sposób prezentacji danych, czyli wykres punktowy, który w tym przypadku pozwoli osiągnąć bardzo ciekawy efekt. Zacznijmy od zakresu danych – w zakładce General ustawiamy zakresy od 0 do 4095.
Następnie przechodzimy do zakładki Point Viewer, klikamy prawym klawiszem myszy w białe tło i wybieramy opcję New – program utworzy wtedy listę punktów o nazwie tp0. Możemy zmienić tę nazwę i kolor, ale co najważniejsze, w dolnej części okna powinniśmy wybrać zmienne, które chcemy wyświetlać w tej formie: dla Var on X wybieramy joystick[0], a dla Var on Y – joystick[1].
Ustawienie zakresu danych
Ustawienia widoku punktowego
Teraz w menu programu wybieramy opcję Views, a w niej Point Viewer. Nasz program wyświetli oba widoki: wykres danych oraz wykres punktowy. Jeśli chcemy ukryć wykres, to możemy w zakładce Views odznaczyć wybór VarViewer1. Po uruchomieniu pobierania danych (przyciskiem z zieloną strzałką) zobaczymy na ekranie punkt, którym będziemy mogli sterować za pomocą joysticka. Jeśli na zakładce General zaznaczymy opcję Draw Line, to na ekranie będzie dodatkowo rysowana linia.
Widok punktowy
Widok punktowy z rysowaniem linii
STM-STUDIO-STM32 to proste i wygodne narzędzie, które może przydać się w wielu sytuacjach. Może być również pomocne podczas debugowania układu, ponieważ w stosunkowo łatwy sposób możemy dzięki niemu przedstawiać graficznie zawartości dowolnych zmiennych.
Inne moduły analogowe w STM32L4
Przetworniki ADC to najpopularniejsze moduły analogowe, które kojarzone są przez większość osób z mikrokontrolerami. Warto jednak wiedzieć, że rozbudowane mikrokontrolery (takie jak STM32L4) mają w tym zakresie do zaoferowania znacznie więcej.
Po pierwsze, same przetworniki ADC są bardzo rozbudowane. To, co omówiliśmy do tej pory, to ułamek możliwości, które dają przetworniki analogowo-cyfrowe w STM32L4. Po drugie, nasz mikrokontroler ma w sobie jeszcze inne moduły analogowe. Wewnątrz STM32L476RG znajdziemy:
1 × przetwornik cyfrowo-analogowy (DAC) z dwoma wyjściami,
2 × wzmacniacz operacyjny,
2 × komparator.
Przetwornik cyfrowo-analogowy (DAC, ang. digital-analog converter) jest niejako przeciwieństwem opisywanych przed chwilą przetworników analogowo-cyfrowych. Pozwala on na zamianę wartości cyfrowych na konkretne napięcie. W naszym mikrokontrolerze znajdziemy 12-bitowy przetwornik, który ma dwa kanały wyjściowe. Możemy ustawić w nich 4096 poziomów napięcia od 0 V do 3,3 V (chyba, że w przypadku układów w większych obudowach wykorzystamy dodatkowe wejścia, aby podłączyć inne napięcie referencyjne dla ADC/DAC).
Przetworniki DAC wykorzystywane są np. do odtwarzania dźwięków lub generowania napięcia odniesienia, które porównywane jest z innym napięciem za pomocą komparatorów.
Wzmacniacze operacyjne to temat na więcej niż jedną książkę – nie będziemy się tutaj rozpisywać na ich temat, bo w jednej z kolejnych części tego kursu użyjemy wbudowanego wzmacniacza podczas przetwarzania sygnałów z czujnika analogowego.
Z kolei komparatory analogowe to układy, które – pisząc w największym uproszczeniu – porównują napięcia na swoich wejściach i reagują na nie odpowiednim stanem na wyjściu układu. Podstawowe informacje na temat tych elementów znaleźć można w naszym kursie elektroniki (poziom II), konkretnie oczywiście w części, która omawia komparatory. W ramach prostego eksperymentu wykorzystamy teraz jeden z komparatorów, który wbudowany jest do STM32L476RG.
Wykorzystanie komparatora z STM32L4
Chyba każdy początkujący elektronik chociaż raz wykonał eksperyment z realizacją lampki nocnej, czyli układu, który samoczynnie włącza diodę po zapadnięciu zmroku. Taki projekt można zrealizować na wiele sposobów – np. z użyciem tradycyjnego komparatora lub programowo (sprawdzając, czy pomiar z ADC jest powyżej lub poniżej jakiegoś poziomu).
Tym razem wykorzystamy komparator, który znajduje się wewnątrz naszego STM32L4. Zaczynamy od podłączenia czujnika – będzie to fotorezystor wpięty w układ dzielnika napięcia. Dzięki temu zmiana jasności, która wpływa na zmianę rezystancji fotorezystora, będzie przekładała się na zmianę napięcia na wejściu PC5. Do tego podłączamy diodę z rezystorem do pinu PB0.
Schemat ideowy i montażowy układu testującego komparator w STM32L4
Po podłączeniu przechodzimy do CubeMX. Z zakładki Analog wybieramy moduł DAC1. Wykorzystamy go do generowania napięcia odniesienia, czyli tego, które będzie porównywane do odczytu z czujnika światła. Opcję OUT1 connected to ustawiamy na only to on chip analog peripherals.
Aktywacja modułu DAC1
Takie ustawienie sprawi, że napięcie wyjściowe nie będzie dostępne na pinach układu. Są też inne opcje – przykładowo ustawienie only to external pin sprawia, że napięcie wyjściowe przetwornika będzie dostępne na pinie mikrokontrolera. Moglibyśmy je wykorzystać np. do odtwarzania dźwięku, bo jest to jedno z częstszych zastosowań dla DAC.
Następnie przechodzimy do konfiguracji komparatora, czyli do modułu COMP1:
zaznaczamy opcję Input [+]: INP,
jako źródło odniesienia, czyli opcję Input [-], wybieramy wyjście przetwornika DAC1,
włączamy też zewnętrzne wyjście komparatora, zaznaczając opcję ExternalOutput,
w dolnej części okna (zakładka z parametrami) opcję Output Pol ustawiamy na COMP output on GPIO is inverted (odwracamy działanie wyjścia, bo chcemy, aby dioda włączała się po zmroku).
Ustawienia modułu komparatora COMP1 w STM32L4
Oczywiście podczas uruchamiania modułu DAC oraz komparatora CubeMX z automatu oznaczył już piny, które są wykorzystywane przez te moduły. Wszystko powinno zgadzać się z naszym schematem, tzn. wejście komparatora, do którego podłączamy czujnik, to PC5, a wyjście komparatora to PB0.
Zapisujemy projekt, aby wygenerować szablon programu, i przechodzimy do edycji kodu. Tym razem kod jest ekstremalnie prosty, a całość musi zostać wykonana tylko raz (przed pętlą nieskończoną):
HAL_DAC_Start uruchamia kanał pierwszy przetwornika DAC1, a za pomocą HAL_DAC_SetValue ustawiamy napięcie wyjściowe, czyli próg zadziałania. Wartość 3000 została dobrana eksperymentalnie – przy takim ustawieniu dioda powinna włączać się, gdy tylko przysłonimy fotorezystor ręką.
Wartość DAC_ALIGN_12B_R określa format przekazywanej wartości, czyli 12-bitową liczbę wyrównywaną do prawej (a zatem taką, jakiej używamy najczęściej).
Na koniec wywołujemy funkcję HAL_COMP_Start, która uruchamia komparator, i tyle – nie musimy już robić absolutnie nic więcej. Napięcie z naszego dzielnika jest porównywane za pomocą wbudowanego komparatora z napięciem, które jest generowane przez moduł DAC. Układ powinien działać poprawnie, czyli dioda powinna włączać się po zasłonięciu fotorezystora ręką.
Fotorezystor odsłonięty – nie świeci
Fotorezystor zasłonięty – świeci
Jak to się różni od standardowego podejścia do tematu, czyli od sprawdzania w pętli, czy wartość z ADC jest poniżej ustalonego poziomu? Wykorzystanie komparatora sprawia, że cała ta operacja jest realizowana sprzętowo! Nie obciążamy tym mikrokontrolera, co więcej, możemy go nawet uśpić, aby obniżyć pobór energii, a nasz czujnik i tak będzie działał. Po drugie, udało nam się zaoszczędzić jedno wejście ADC – w niektórych projektach może być to kluczowe.
Zadanie domowe
Napisz program, który wykorzystuje informacje z potencjometru do regulacji częstotliwość migania dwóch diod świecących (podłączonych do oddzielnych wyprowadzeń mikrokontrolera).
Rozbuduj program z zadania pierwszego w taki sposób, aby jasność diod była zależna od poziomu oświetlenia – im jest jaśniej, tym diody powinny świecić z większą intensywnością.
Dodaj do programu obsługę joysticka – mikrokontroler powinien przesyłać przez UART informacje o jego aktualnym ustawieniu (up/down/left/right). Informacja powinna być wysyłana do komputera tylko wtedy, gdy pozycja ulegnie zmianie.
Podsumowanie – co powinieneś zapamiętać?
Przetworniki ADC to rozbudowany temat – najważniejsze, abyś po lekturze tego artykułu wiedział, jak za pomocą DMA odczytywać napięcia zmierzone jednocześnie przez kilka kanałów i jak przeliczać taki pomiar na wolty. Do tego powinieneś wiedzieć, jak podejrzeć wybrane zmienne na żywo za pomocą nowo poznanego programu STM Studio.
Czy wpis był pomocny? Oceń go:
Średnia ocena 5 / 5. Głosów łącznie: 49
Nikt jeszcze nie głosował, bądź pierwszy!
Artykuł nie był pomocny? Jak możemy go poprawić? Wpisz swoje sugestie poniżej. Jeśli masz pytanie to zadaj je w komentarzu - ten formularz jest anonimowy, nie będziemy mogli Ci odpowiedzieć!
W kolejnej części kursu STM32L4 zajmiemy się komunikacją przez SPI – zaczniemy od obsługi bardzo prostego układu, jakim jest ekspander GPIO. Przy okazji zmienimy trochę strukturę projektu, który jest generowany przez CubeMX. Nauczymy się też tworzyć własne, uniwersalne biblioteki.
Główny autor kursu: Piotr Bugalski Współautor: Damian Szymański, ilustracje: Piotr Adamczyk Oficjalnym partnerem tego kursu jest firma STMicroelectronics Zakaz kopiowania treści kursów oraz grafik bez zgody FORBOT.pl
Dołącz do 20 tysięcy osób, które otrzymują powiadomienia o nowych artykułach! Zapisz się, a otrzymasz PDF-y ze ściągami (m.in. na temat mocy, tranzystorów, diod i schematów) oraz listę inspirujących DIY na bazie Arduino i Raspberry Pi.
Dołącz do 20 tysięcy osób, które otrzymują powiadomienia o nowych artykułach! Zapisz się, a otrzymasz PDF-y ze ściągami (m.in. na temat mocy, tranzystorów, diod i schematów) oraz listę inspirujących DIY z Arduino i RPi.
Trwa ładowanie komentarzy...