Zapewne nie raz w Internecie i nie tylko widziałeś problemy osób stawiających pierwsze kroki z programowaniem mikrokontrolerów. Jednym z takich problemów jest to, iż nie potrafimy wykonywać „wielu rzeczy naraz”. Przykładowo czekać na pomiar jednocześnie migać diodą i czytać stan przycisku. Frustracja narasta, a „fachowcy” śmieją się z takich osób na grupach Facebookowych. O co chodzi i jak sobie z tym radzić?
Nie mogę robić wielu rzeczy naraz…
Wśród osób początkujących w programowaniu mikrokontrolerów panuje mylne wrażenie, iż wszystko, co napiszemy powinno wykonywać się jednocześnie. Zobaczmy na PC. Jednocześnie przecież procesor łapie to, co jest na klawiaturze, wyświetla na ekranie, a jeszcze w tle sobie gada z Internetem.
Ta „jednoczesność” to nie jest do końca prawda. Zwłaszcza na mikrokontrolerach. To jest tylko złudzenie. Operacje po prostu wykonują się tak szybko, iż dla człowieka sprawia to wrażenia chodzenia „wielu rzeczy naraz”.
Na PC jest tak samo. System operacyjny np. Windows wykonuje w tle ogromne ilości zadań.
Tylko to jest PC. On ma wiele rdzeni, które przyjmują te zadania. OS przełącza sprytnie aktualnie wykonywane zadanie i mamy właśnie wrażenie działania wszystkiego jednocześnie.
Mikrokontroler ma jeden, malutki rdzeń. Może wykonywać tylko jedną czynność na raz!
Nasze programy początkowo nie ma takiego systemu operacyjnego i nikt mu nie przełącza zadań. Zresztą takiego Windowsa czy Linuksa i tak nie postawimy. Na mikrokontrolery są inne systemy i do tego przejdziemy za chwilę.
Dlaczego się blokujemy?
Przechodzimy do sedna problemu. Dlaczego nie udaje nam się wykonywać wielu czynności naraz?
Odpowiedź jest prosta. Blokujemy pracę mikrokontrolera. W jaki sposób?
Jest taka funkcja dostępna w większości Frameworków, w których piszemy oprogramowanie:
- Dla STM32 będzie to HAL_Delay
- Dla AVR _delay_ms
- Dla Arduino delay
Jej działanie psuje nasz cały misterny plan wykonywania „jednocześnie wielu rzeczy naraz”.
Dlaczego? Otóż ta funkcja rozkazuje, aby nasz program STAŁ i CZEKAŁ. Palił prąd jak to mówię
Jest to funkcja BLOKUJĄCA pracę programu. Dopóki warunek nie zostanie spełniony – upływ zadanego czasu – to ta funkcja nie pozwala na wykonywanie innych czynności. Po prostu procesor ma za zadanie czekać i się nudzić.
I to jest ogromny problem pisania oprogramowania. Bo mógłby czekać, ale w tym samym czasie wykonywać inne rzeczy i właśnie sprawiać wrażenie równoległego wykonywania zadań.
Jak sobie radzić z Delayem?
W skrócie – trzeba tego Delaya usunąć. Zapomnieć o nim jak najszybciej!
No może nie do końca… Sam czasem go używam, ALE nie w pętli głównej programu. Tylko podczas inicjalizacji sprzętu. Gdy program już chodzi, unikam go jak ognia.
No dobra, ale samo usunięcie spowoduje przecież to, iż nie będziemy mieli opóźnień. Jak wtedy odczekać na kolejny pomiar lub inne cykliczne czynności?
Jest na to kilka technik, z którymi tak naprawdę zastępujemy Delaya, zamiast go tylko usunąć.
1. Timer programowy / millis
Timer programowy jest najprostszym sposobem na usunięcie Delaya. Jak on działa?
Polega on na jednym, Timerze sprzętowym, który zlicza – najczęściej – liczbę milisekund od startu programu. Niezależnie co robimy, ten Timer ma co 1 ms przerwanie i jedyne co robi to podnosi globalny licznik tzw. Tick Systemowy.
Do odczytu mijającego czasu mamy specjalne funkcje.
- W STM32: HAL_GetTick()
- W Arduino: millis()
- W AVR: Nie ma. Trzeba samemu zaimplementować na którymś Timerze sprzętowym
Odczytując bieżący stan Ticka wiemy, jaki mamy czas. Czytając kolejny raz wiemy ile tego czasu upłynęło.
Jeśli mamy do odczekania np. 500 milisekund, to „czekamy” aż Tick wzrośnie o ten czas. ALE nie czekamy blokująco!
- Zapisujemy bieżący czas.
- Z każdym obiegiem pętli sprawdzamy, czy minął już zadany czas.
- Jeśli nie – wychodzimy z warunku i robimy coś innego. (Tu jest ta „równoległość”)
- Jeśli tak – wchodzimy w warunek i wykonujemy tę cykliczną czynność.
- Przeładowujemy sobie zmienną z aktualnym czasem, aby znowu zaczekać 500ms.
- Powtarzamy w kółko.
Sposobów na sprawdzanie, czy upłynął zadany czas jest kilka. Można przeładowywać pamiętany Tick. Można dodawać do zapamiętanego Ticku stałą. Można przeładować/dodać przed wykonaniem czynności lub po. To jak napiszemy zależy od tego, co chcemy osiągnąć.
2. Maszyna stanów
To jest interesujący sposób na prowadzenie ścieżki programu lub części programu. Pozwala na naprawdę wygodne prowadzenie tego jak program ma się zachowywać.
Sama maszyna stanów nie pozwoli na eliminację blokujących opóźnień. Trzeba ja połączyć z Timerem Programowym.
Chodzi o to, iż jakiś obiekt lub kawałek programu może znajdować się w różnych stanach. Najlepszym przykładem będzie przycisk. Taki zwykły microswitch.
Możemy dla niego wyróżnić 3 podstawowe stany:
- IDLE – nikt nic nie robi, po prostu stoi i czeka na wciśnięcie.
- DEBOUNCE – zostało wykryte wciśnięcie, musimy odczekać 20-50 ms na potwierdzenie wciśnięcia.
- PRESSED – odczekaliśmy, przycisk przez cały czas wciśnięty – potwierdzamy wciśnięcie i wykonujemy akcję.
Między stanami przeskakujemy robiąc różne akcje dla wszystkich z nich. Określamy warunki przejść między nimi.
- IDLE czeka na wciśnięcie. Jak było – skacze do DEBOUNCE.
- DEBOUNCE odlicza 20-50 ms przy pomocy timera programowego. Jak odliczy sprawdza, czy przez cały czas jest wciśnięcie. Jak tak – skacze do PRESSED. Jak nie – wraca do IDLE.
- PRESSED wykonuje jednorazowo akcję. Dopóki wciąż trzymamy przycisk nie robi nic. Jak puścimy – wraca do IDLE.
Tych stanów może być więcej. Dokładając kolejne Timery programowe możemy wykrywać:
- Długie wciśnięcie
- Powtarzalną akcję co X milisekund
- Akcję puszczenia przycisku
Jest co tutaj robić, a maszyna stanów nam to ułatwia. Jak to działa?
Wchodzisz do maszyny i patrzysz na aktualny stan. jeżeli jest coś do zrobienia – robisz. Jak nie – wychodzisz z maszyny i robisz coś innego. Wracasz w kolejnym obiegu pętli.
Proste? Jasne Znowu nie użyliśmy Delaya, a mamy już coś naprawdę dużego.
3. RTOS – System Czasu Rzeczywistego
To jest już gruby kaliber, ale i też jest najskuteczniejszy. Kiedyś w końcu projekty zaczną być skomplikowane. Wtedy sam Timer programowy i maszyna stanów to będzie za mało, aby dobrze zapanować nad dystrybucją czasu procesora między poszczególne zadania.
Z pomocą przychodzi RTOS, czyli system czasu rzeczywistego (Real Time Operating System). Są to małe systemy – nie takie jak Windows, czy Linux – które dostarczają nam funkcji potrzebnych do budowy wielozadaniowego systemu.
Jednak chciałbym nieco przestrzec osoby początkujące. Często padają takie rady, aby wziąć RTOSa i będzie git. Niestety tak nie będzie.
RTOSy to skompilowane narzędzia. Nie da się ich po prostu włączyć, aby dobrze działały. Trzeba pisać aplikację z myślą o tym, aby działała pod systemem.
Wszystkie zadania w takim systemie są takimi jakby osobnymi programami. System sam rozdziela czas procesora na poszczególne zadania według priorytetów i tego, czy są gotowe do działania. Między zadaniami komunikujemy się w specjalny sposób przy użyciu tego, co oferuje nam taki system: kolejki, semafory, mutexy, sygnały.
Pisząc z RTOSem musimy zmienić nasz sposób myślenia na temat tworzenia oprogramowania.
Moja rada jest taka: spróbuj najpierw prostych rzeczy z punktami 1 i 2. Dopiero po wdrożeniu się w programowanie próbuj RTOSów.
Jakie mamy dostępne? Kilka przykładowych to FreeRTOS, mbed OS, Azure RTOS, Keil RTX, ThreadX, Zephyr.
Gdzie się tego uczyć?
Tego wszystkiego uczę osobiście w Kursie STM32 dla Początkujących.
Zaczynamy od Timerów programowych i maszyny stanów. Budujemy proste rzeczy i uczymy się korzystać z tych technik programistycznych.
Po poznaniu dodatkowych peryferiów przechodzimy do poznania FreeRTOSa i budujemy prawdziwą aplikację działającą pod tym systemem.
Kurs zawiera około 50 godzin materiałów z programowania STM32. Oprócz poznania samych peryferiów mikrokontrolera przekazuję w nim masę dobrych praktyk i użytecznych sztuczek. Łącznie 12 modułów ogromnej wiedzy.
Właśnie realizowane są zapisy do kolejnej – czwartej już – edycji. Do tej pory dołączyło PONAD 550 osób!
Nie przegap okazji! Zapisy realizowane są tylko do piątku 25.02.2022 do godziny 22:00.
Wszystkie szczegóły razem z AGENDĄ KURSU i LEKCJAMI DEMO znajdziesz na stronie https://kursstm32.pl