Ostatnio udało się trochę pchnąć do przodu prace nad softem. Dorobiłem prototypy funkcji debugowych i interfejsu użytkownika, a także napisałem sterownik silników, który następnie przetestowałem na robocie. Okazało się, że na płytce PCB jednak znajdowały się błędy. Zmiany w kodzie zostały wrzucone na GitHub, pojawiły się tam też pierwsze wpisy do Issue Trackera.

Funkcje debugowe

Dodałem do repozytorium prototypy funkcji, które w przyszłości posłużą do zbierania runtimeowych informacji debugowych z robota. Są to dwa główne moduły:

  • logger – jego zadaniem jest drukowanie danych na USART.
  • konsola – otrzymuje i interpretuje komendy wysyłane z USARTa.

Logger będzie korzystał z nadawania przez USART za pomocą DMA. Zamysł jest taki, żeby task loggera obsługiwał dwa bufory. Kiedy jeden jest przekazany do wysyłania, do drugiego zapisywane są wiadomości od aplikacji. W cyklicznym wątku wysłany bufor USART będzie zamieniany z wypełnionym buforem wiadomości. Funkcję zapisywania danych do data loggera mam zamiar podpiąć pod funkcję biblioteki standardowej printf. Zwykle w systemach embedded nie stosuje się standardowego printfa, zamiast tego robi się własne funkcje implementujące tylko najbardziej potrzebne stringi formatujące. Ja jednak mam zamiar napisać swoją wersję wewnętrznej funkcji systemowej _write_r, z której korzysta printf i w ten sposób przekierowywać go na USART.

Konsola debugowa będzie będzie korzystała z prostego odbioru USART w pollingu bez używania przerwań i DMA. W wątku konsoli będzie zaimplementowane drzewo menu, po którym będzie można poruszać się pojedynczymi znakami. Mam zamiar udostępnić przez konsolę takie funkcje jak podgląd mapy labiryntu, wynik działania algorytmu znajdowania ścieżek, czy możliwość przełączenia na ręczne sterowanie robotem. Zdecydowałem się na odbieranie w pollingu, ponieważ z odbiorem asynchronicznych danych jest jeden podstawowy problem – nie wiemy z góry ile danych spodziewamy się odebrać. Mądre algorytmy, które odbierają większe ciągi znaków i znajdują w nich wiadomości są dosyć skomplikowane, a w mojej aplikacji nie przyniosą większych korzyści. Najwyżej jeśli kiedyś będę potrzebował czegoś takiego – mogę doimplementować.

Jeżeli chodzi o funkcje debugowe myślę, że powstanie jeszcze jeden moduł zawierający definicje funkcji do zbierania logów z możliwością wyboru poziomów logowania, assertów itp. Poziomy logów pewnie będą wybierane za pomocą #define i aby nie zaśmiecać kodu wieloma użyciami #ifdef, zastosuję sposób, który opisywałem w artykule o definach.

Sterownik silników

Zaimplementowałem też sterownik mostka H. Sterownik obsługuje dwa kanały – lewy i prawy – składające się z trzech wyjść:

  • PWM – sygnał PWM sterujący szybkością obrotów silnika.
  • CH1 i CH2 – sygnały cyfrowe określające kierunek obrotów silnika.

Sygnały CH1 i CH2 są sterowane za pomocą standardowych portów GPIO. Do generowania sygnałów PWM używam Timer2 skonfigurowany w tryb PWM Center Aligned Mode 3 (po szczegóły odsyłam do Reference Manuala procesora STM32F401RB). Częstotliwość sygnału PWM to 10 kHz.

Zewnętrzne API udostępnia funkcję inicjalizującą moduł mostka H do pracy oraz po jednej funkcji na każdy silnik do zadawania prędkości. Akceptowalne wartości prędkości należą do przedziału od -100% do 100%. Wartość 100% oznacza, że silnik jedzie do przodu z maksymalną prędkością, a -100% że do tyłu. Wartość 0 powoduje zatrzymanie silników.

Aby przetestować działanie drivera stworzyłem nowy target w folderze test/hw_test. Prosty program cyklicznie wykonuje następujące operacje:

  1. Zatrzymuje prawy silnik, lewym kręci do przodu z prędkością 20%.
  2. Zatrzymuje lewy silnik, prawym kręci do przodu z prędkością 20%.
  3. Zatrzymuje prawy silnik, lewym kręci do tyłu z prędkością 20%.
  4. Zatrzymuje lewy silnik, prawym kręci do tyłu z prędkością 20%.

Przy zadawaniu sygnału na silniki należy pamiętać, że za małe wypełnienie PWM podane do mostka H powoduje, że sygnał podany na silniki jest za słaby, aby wprawić wał silnika w ruch. Wartość wymagana do ruszenia wału zależy od silnika i od częstotliwości sygnału PWM. Dla mniejszych częstotliwości minimalne potrzebne wypełnienie jest mniejsze.

Wnioski z testów silników

Po uruchomieniu testu drivera silników miałem kilka spostrzeżeń. Najważniejsze jest takie, że jednak popełniłem błąd w projekcie PCB. Złącze silników ma odwrócone piny. O ile dla skrajnych pinów, czyli sygnałów na silnik i sygnałów z enkoderów odwrócenie sygnałów nie ma znaczenia – można je skompensować w sofcie, to ze środkowymi pinami jest problem, bo zasilania z masą nie mogę zamienić. Na razie poradziłem sobie z tym problemem używając dodatkowych kabelków:

W związku z tym na GitHubie znalazł się pierwszy bug – link.

Po uruchomieniu programu wyszedł kolejny problem, którego byłem świadomy już od dawna, ale oczekiwania się w końcu potwierdziły. Przekładnia znajdująca się na wale silnika nie jest wystarczająco mocno przytwierdzona. Przez to kiedy napotyka ona opór, wał silnika obraca się wewnątrz zębatki nie kręcąc kołami. Możliwym rozwiązaniem jest stworzenie zębatki z otworem na śrubę mocującą ją do wału. Zasada została opisana pod linkiem, sposób nr 1. Zobaczymy, czy moja drukarka 3D podoła temu zadaniu.

Trzecia zauważona rzecz jest dużo mniej ważna i łatwa do poprawienia. Częstotliwość PWM 10 kHz powoduje wydobywanie się z silnika piskliwego dźwięku. Aby tego uniknąć, mogę zwiększyć częstotliwość PWM poza słyszalne pasmo.

Podsumowanie

Pojekt cały czas powoli posuwa się do przodu. Mam już prototypy większości modułów, zostawiłem sobie na później tylko wysokopoziomowe moduły takie jak estymacja pozycji, czy mapowanie labiryntu. W najbliższym czasie chciał bym napisać kod kolejnych driverów i sprawdzić poprawność kolejnych części PCB. Przy okazji ostatnio wprowadzonych zmian, zacząłem również wykorzystywać GitHubowy Issue Tracker.