Ostatnio opisywałem szablon projektu na STM32 i konfigurację środowiska, więc teraz pora na jakiś praktyczny projekt. Będzie nim program grający melodie na silnikach. Kiedyś napisałem taki programik, żeby sprawdzić czy działa sterowanie silnikami w moim robocie. W tym czasie na Forbot.pl był konkurs na robota z akcentem świątecznym i Wśród nocnej ciszy w wykonaniu mojego robota wygrało.

Pojawiła się propozycja, żeby z okazji zwycięstwa zagrać na silnikach We are the champions, ale poległem na tym zadaniu. Problemem było to, że za słabo znam się na muzyce i bazowałem tylko na nutach znalezionych w necie i jak coś było nie tak, nie umiałem tego poprawić. Ostatnio wróciłem do tematu i postanowiłem opublikować i trochę opisać kod na STM32F4 Discovery.

Potrzebne elementy

Do wykonania projektu potrzebujemy:

  • Zestaw uruchomieniowy STM32F4 Discovery.
  • Moduł mostka H TB6612.
  • Silnik DC – w moim wypadku były to dwa silniki Pololu, ale może być jakikolwiek silnik i wystarczy jeden.
  • Zasilanie do silników – ja użyłem zasilacza laboratoryjnego.
  • Przewody połączeniowe.

Połączenia pinów pomiędzy modułem Discovery a mostkiem TB6612 obrazuje tabela.

Pin STM32F4 DiscoveryPin TB6612
PA00PWMA
PA01PWMB
PC00AIN1
PC01BIN1
PC02AIN2
PC03BIN2

Zasada działania

Dźwięki to fale akustyczne o odpowiedniej częstotliwości. Częstotliwości konkretnych dźwięków można znaleźć na stronie: link. Jeśli na silniki DC podamy sygnał PWM o pewnej częstotliwości, powstaną fale dźwiękowe. Jeżeli możemy sterować częstotliwością PWM, to możemy również sterować częstotliwością dźwięków wydawanych przez silnik. Program musi więc podawać na silnik sygnał o zmieniającej się częstotliwości, żeby wygenerować melodię.

Program

Program oparty jest na moim szablonie i wykorzystuje FreeRTOS. Składa się z drivera układu TB6612 znajdującego się w folderze src/hw/tb6612 i z tasku wykonującego główną logikę programu w folderze src/code/music_task. Jest jeszcze plik src/code/music_task/tone_freqs.h zawierający definicje częstotliwości poszczególnych dźwięków. Źródła są dostępne na moim GitHubie.

Driver silnika wykorzystuje Timer 2 w trybie PWM, 2 porty GPIO na wyjścia PWM i 4 porty GPIO do sterowania kierunkiem obrotów silnika. Driver udostępnia funkcję zadającą na oba silniki sygnał PWM o zadanym wypełnieniu i okresie w mikrosekundach przekazywanym w parametrze. Timer jest skonfigurowany tak, że jeden tick wynosi 1us, tak więc wartość okresu jest ustawiana jako wartość przepełnienia licznika (rejestr ARR). Do rejestrów sterujących wypełnieniem (CCR) wpisywana jest wartość okresu przesunięta o 3 bity w prawo, czyli podzielona przez 8, co daje 12.5% wypełnienia. Jeżeli silniki nie startują przy tym wypełnieniu, można je zwiększyć.

Plik music_task.c zawiera funkcję music_task, gdzie wykonuje się główna logika programu. W niej inicjalizowany jest driver silnika, a następnie w pętli wywoływana funkcja play z wybraną melodią przekazywaną jako parametr. Funkcja play czyta kolejne nuty z tablicy i ustawia odpowiednie częstotliwości na silniki.

Melodie są przechowywane w tablicach nut (tablice struktur note). Dla każdej nuty zdefiniowana jest częstotliwość odpowiadająca dźwiękowi np. C4, Gis5 itd. oraz długość np. półnuta, ósemka itd. W programie znajdują się wypełnione tablice dla trzech melodii – Marszu Imperialnego z Gwiezdnych Wojen, kolędy Wśród nocnej ciszy i We are the champions zespołu Queen.

Podsumowanie

Nie jestem muzykiem, więc aranżacje pozostawiają trochę do życzenia 🙂 Zaimplementowany przeze mnie sposób przechowywania zapisu nutowego jest dosyć ubogi. Nie przewiduje różnych opcji pauz, przedłużania dźwięków itp. Żeby zaimplementować wszystkie możliwe ficzery, które daje nam zapis nutowy raczej trzeba by było zmienić koncepcję funkcji play. Poza tym można by dodać granie przez oba silniki innych partii. Źródła są ogólnie dostępne, więc może ktoś będzie chciał się pobawić i stworzyć swoją bardziej rozbudowaną wersję.