Moduł HC06 (fotografia 1) jest nieskomplikowany i przez to bardzo łatwy w obsłudze. Wspiera on tylko jeden profil Bluetooth – profil portu szeregowego. Moduł ma cztery wyprowadzenia: piny zasilania (VCC i GND) oraz piny transmisji i odbioru danych interfejsu UART (TX i RX). Po przyłączeniu do nich mikrokontrolera oraz sparowaniu z modułem urządzenia Bluetooth i otwarciu wirtualnego portu szeregowego na tym urządzeniu możliwa jest między nimi wymiana danych na podobnej zasadzie, jak gdyby były one połączone kablem poprzez interfejs RS232.
Konfiguracja modułu – nadanie mu nazwy, wybór szybkości pracy wirtualnego portu szeregowego czy nadanie kodu pin odbywa się również za pośrednictwem interfejsu UART za pomocą specjalnych poleceń, przed sparowaniem urządzeń i otwarciem portu szeregowego. Obsługiwane są następujące polecenia konfiguracyjne:
AT+BAUD[Liczba z zakresu od 1 do 8] ustawiające szybkość transmisji wirtualnego portu szeregowego. Liczba z zakresu 1…8 odpowiada jednej z wymienionych szybkości pracy: 1 – 1200 bps, 2 – 2400 bps, 3 – 4800 bps, 4 – 9600 bps, 5 – 19200 bps, 6 – 38400 bps, 7 – 57600 bps, 8 – 115200 bps. Przykładowo, polecenie „AT_BAUD4” ustawi szybkość połączenia na 9600 bps.
AT+NAME[Nazwa] ustawia nazwę rozgłaszaną przez urządzenie. Na przykład, „AT+NAMEBT_TEST”.
AT+PIN[4 cyfry] ustawia kod pin, którego podanie jest wymagane do sparowania urządzeń. Na przykład, „AT+PIN1234”.
Domyślne parametry to nazwa „HC-06”, szybkość połączenia 9600 bps oraz PIN „1234”. Parametry po zmianie są przechowywane w pamięci EEPROM, więc konfiguracja nie jest tracona po odłączeniu zasilania.
Tworzymy projekt
Program, który utworzymy w kolejnych krokach, będzie korzystał z dwóch modułów peryferyjnych: SPI do wygenerowania sygnału sterującego dla diod WS2812B oraz interfejsu UART do wymiany danych z modułem. Z urządzenia bezprzewodowego przyłączonego do modułu Bluetooth będą przesyłane polecenia zmiany koloru diod na pasku w postaci ramek danych. Ramka ta zaczyna się od znaku „@”, a kończy znakiem nowej linii „\r\n”. Wewnątrz są umieszczone wartości numeryczne z zakresu od 0 do 255, rozdzielone przecinkami i zgrupowane po trzy. Każde kolejne grupy wartości opisują kolor kolejnej diody na pasku. Pierwsza liczba w grupie opisuje jasność składowej czerwonej, druga – zielonej, a trzecia – niebieskiej.
Na listingu 1 pokazano ramkę danych włączającą na kolejnych 30 diodach na pasku naprzemiennie kolory czerwony, zielony i niebieski. Format powyższej ramki nie pozwala na wygodne ręczne sterowanie diodami przy pomocy takiej aplikacji jak np. PuTTY, ułatwia jednak napisanie własnej aplikacji na telefon, tablet lub komputer.
Zatem do dzieła! Standardowo już włączamy program STM32CubeMX i tworzymy w nim nowy projekt, wybierając posiadany przez nas mikrokontroler (u mnie, na płytce KA-NUCELO-F411CE, jest to układ STM32F411CEU6).
Na pierwszym ekranie konfiguratora, zatytułowanym „Pinout”, ustawiamy źródło sygnału taktującego. Na liście po lewej stronie ekranu rozwijamy pozycję „RCC” i w polu „High Speed Clock (HSC)” wybieramy „Crystal/Ceramic Resonator”. Następnie musimy wybrać i uruchomić interfejs SPI. Używany przeze mnie układ dysponuje aż pięcioma takimi interfejsami. Nie ma znaczenia, który z nich wybierzemy, jednak moduły SPI1, SPI4 i SPI5 są taktowane z innej szyny niż SPI2 i SPI3, więc na potrzeby tego projektu wybierzmy któryś z tych pierwszych. W przykładzie użyję interfejsu SPI1 oraz pinów PA5 oraz PA7, odpowiednio w roli SPI1_SCK (sygnału zegara) oraz SPI1_MOSI (wyjścia danych). Jako tryb pracy wybieramy pozycję: „Transmit Only Master”.
Oprócz modułu SPI uruchamiamy również interfejs UART. Możemy wybrać dowolny moduł – USART1, USART2 lub USART6. W przykładzie użyto USART1 na pinach PA10 (RX) i PA9 (TX). Konfigurację wyprowadzeń w STM32CubeMX pokazano na rysunku 2.
Teraz możemy już przyłączyć do płytki pasek diod oraz moduł Bluetooth. Pin VCC (czerwony kabel) z paska diod WS2812b łączymy z wyprowadzeniem 5 V płytki, GND (czarny) do jednego z GND na płytce oraz sygnałowy (zielony) do pinu D11. Wyprowadzenia modułu Bluetooth łączymy w następujący sposób: VCC (modułu) do pinu 3V3 (płytki KA-Nucleo), GND do pinu GND oraz piny TX/RX modułu do pinów D2/D8.
W zakładce „Clock Configuration” zwyczajowo ustawiamy częstotliwość pracy wejściowego oscylatora kwarcowego (u mnie 8 MHz), przełączamy źródło sygnału podawanego na główną pętlę PLL – „PLL Source Mux” na „HSE”, jako źródło sygnału taktującego dla całego układu wybierzmy pętlę PLL – przełącznik „System Clock Mux” ustawiamy na pozycję „PLLCLK”. Teraz musimy jeszcze ustawić pożądaną częstotliwość taktowania naszego układu. Nie będzie to jednak maksymalna, dozwolona wartość. Musimy dobrać ją wspólnie z dzielnikiem częstotliwości taktującej interfejs SPI, aby możliwa była transmisja sygnału „jedynki” i „zera” dla diod w odpowiednim czasie. Robimy to identycznie. jak opisano w EP 3/2017, ponieważ transmisja pojedynczego bitu sygnału sterującego diodami trwa 1,25 ms, a jego sygnał jest generowany przez 8 bitów nadawanych interfejsem SPI. Każdy taki bit powinien trwać 0,15625 ms, co przekłada się na częstotliwość taktowania peryferialu SPI równą 6,4 MHz. Aby uzyskać taką wartość, ustawiamy wartość częstotliwości „HCLK (MHz)” na 51,2 MHz oraz wartość dzielnika, w kolejnym etapie konfiguracji na 8 (rysunek 3).
Teraz przechodzimy do zakładki „Configuration”. Z pola „Connectivity” wybieramy pozycję „SPIx”, gdzie „x” odpowiada numerowi wybranego interfejsu SPI i przechodzimy do jego konfiguracji. Opcją, która zmieniamy, jest wartość preskalera wybrana w poprzednim kroku (rysunek 4).
Teraz przechodzimy do konfiguracji UART. Ponownie z pola „Connectivity” wybieramy pozycję „USARTx”. Tym razem jednak musimy zmienić inny parametr – „Baud Rate”. W tym polu wprowadzamy „9600 Bits/s” (rysunek 5). Następnie, nie wychodząc z okna konfiguracji peryferialu USART, przechodzimy do zakładki „NVIC Settings” i zaznaczamy tam jedyne znajdujące się na liście przerwanie (rysunek 6). Zatwierdzamy zmiany przyciskiem „OK”.
Na tym etapie możemy już zapisać ustawienia i wygenerować projekt, a następnie zaimportować go w środowisku IDE w sposób opisany w poprzednich artykułach – klikamy ikonę zębatki na pasku narzędziowym, wybieramy w polu „Toolchain / IDE” opcję „SW4STM32” i zapisujemy pliki projektu w wybranym miejscu (rysunek 7). Następnie otwieramy IDE System Workbench for STM32, zamykamy planszę powitalną (X), w ramce „Project Explorer” klikamy prawym przyciskiem myszy i z menu kontekstowego wybieramy opcję „Import...”, w nowym oknie, klikamy kolejno: „General”, „Existing Projects into Workspace”, „Next”, wybieramy lokalizację plików projektu oraz zatwierdzamy import przyciskiem „Finish”.
Aby możliwe było korzystanie z wartości zmiennoprzecinkowych, w funkcjach printf(), sprintf(), scanf() oraz sscanf(), konieczne jest dodanie parametrów „ -u _printf_float -u _scanf_float” do linii polecenia linkera. Robimy to, klikając prawym przyciskiem myszy na nazwę nowego projektu, z menu kontekstowego, wybierając pozycję „Properties” oraz nawigując do „C/C++ Build” -> „Settings” -> „Miscellaneous” i dopisując do pola „Linker flags” wartość: „ -u _printf_float -u _scanf_float” (rysunek 8).
Teraz dodamy do projektu cztery pliki, które wykorzystamy do obsługi diod oraz interpretacji opisanych wcześniej ramek (rysunek 9). W tym celu, w „Project Explorerze” rozwijamy katalog projektu, klikamy PPM na znajdujący się w nim podkatalog „Inc” i z menu kontekstowego wybieramy opcję „New” -> „File”. W nowym oknie podajemy nazwę pliku, który chcemy utworzyć – „ws2812b.h”, klikamy „Finish”. Czynności ponawiamy również dla pliku „frame_parser.h”. Następnie, w podobny sposób, do katalogu „Src” dodajemy dwa kolejne pliki: „ws2812b.c” oraz „frame_parser.c”. Dalej, uzupełniamy nowo dodane pliki zawartością listingów 3…6 oraz modyfikujemy zawartość pliku „main.c”, zgodnie z listingiem 2. Kompilujemy program i wgrywamy do pamięci mikrokontrolera (ikony młotka i robaka na pasku narzędziowym).
Po wykonaniu wszystkich powyższych kroków parujemy nasz komputer z modułem Bluetooth. Pod Windowsem 10 uruchamiamy „Ustawienia” i przechodzimy do zakładki „Urządzenia” „Bluetooth’. Tam odnajdujemy urządzenie „ep_bt”. Zaznaczamy je, klikamy „Sparuj” i podajemy pin kod „3498”. Pod Linuksem jest to zależne od używanego środowiska graficznego (rysunek 10).
Teraz otwieramy Menedżer urządzeń i rozwijamy zakładkę „Porty (COM i LPT)”. Zapamiętujemy (lub zapisujemy) numer portu COM pierwszego urządzenia Bluetooth (rysunek 11). Pod Linuksem identyfikator portu nowego urządzenia możemy sprawdzić poleceniem dmesg.
Nie pozostaje nam już nic innego jak otworzyć port szeregowy w programie PuTTY i przesłać do mikrokontrolera ramkę danych. W polu „Connection Type:” zaznaczamy opcję „Serial”, w polu „Serial line” podajemy identyfikator portu szeregowego, a w polu „Speed”, szybkość połączenia 9600 bps. Następnie, klikamy przycisk „Open” i w oknie konsoli, które zostanie wyświetlone po chwili oczekiwania wklejamy testową ramkę danych (listing 7) oraz klikamy „Enter” (rysunek 12). Na pasku LED, na kolejnych diodach, powinny pojawić się na przemian kolory: czerwony, zielony i niebieski.
Biblioteka ws2812b.h/ws2812b.c oraz sposób generowania sygnału sterującego dla diod zostały opisane w EP 3/2017. Funkcja ws2812b_init() tworzy strukturę przechowującą konfigurację, bufory danych i uchwyt na podobną strukturę opisującą używany interfejs SPI. Wywołując ją, musimy podać wskaźnik na uchwyt interfejsu SPI oraz liczbę diod, którymi chcemy sterować. Kolejna funkcja ws2812b_set_diode_color() manipuluje jedynie na buforach danych w tej strukturze. Przesyłamy do niej wskaźnik na uchwyt wygenerowany przez funkcję ws2812b_init(), numer diody oraz pożądany kolor w formie struktury ws2812b_color zawierającej trzy wartości składowe – natężenie barw czerwonej, zielonej i niebieskiej w formie trzech wartości jednobajtowych. Następna funkcja ws2812b_refresh(), do której wywołania potrzebujemy przesłać jedynie wskaźnik na strukturę konfiguracyjną, generuje właściwy sygnał sterujący i ustawia kolory zapisane w buforze, na kolejnych diodach na pasku.
Biblioteka frame_parser.h/frame_parser.c odpowiada za interpretację danych otrzymanych przez interfejs UART z modułu Bluetooth. W odpowiednich momentach wywołuje ona funkcje biblioteki ws2812b, ustawiając kolor konkretnej diody na podstawie danych odebranych z ramek lub wywołując funkcję generującą sygnał - ws2812b_refresh(), gdy nie jest przetwarzane żadne przerwanie. Podobnie jak poprzednio, funkcja frame_parser_init() generuje strukturę przechowującą zmienne konfiguracyjne, bufory danych oraz uchwyt biblioteki ws2812b (wygenerowany przez funkcję ws2812b_init()). Funkcja frame_parser_recv_char() wywoływana jest w momencie otrzymania nowego bajtu danych - znaku ASCII przez interfejs UART, dodaje on do bufora nowy znak, a w odpowiednim momencie (po odebraniu znacznika końca ramki), ustawia ona flagę wymuszającą interpretację nowej ramki przez kolejną omawianą funkcję. Funkcja frame_parser_process_frame() wywoływana jest ciągle, w pętli głównej programu, gdy zachodzi taka potrzeba - gdy ustawiona jest flaga wymuszająca interpretację ramki, funkcja odczytuje z odebranej ramki wartości natężeń poszczególnych składowych kolorów kolejnych diod i ustawia na diodach te kolory, a dalej - po zinterpretowaniu całej ramki, wywołuje funkcję ws2812b_refresh(), generująca sygnał sterujący.
W pliku main.c, w sekcji USER CODE 0, tworzone są zmienne przechowujące uchwyty dla obu bibliotek oraz dwie funkcje - HAL_UART_RxCpltCallback(), wykonywana w przerwaniu, w momencie otrzymania nowego znaku przez interfejs UART oraz setup_uart(), wysyłająca do modułu Bluetooth, polecenia sterujące. Następnie, w sekcji USER CODE 2, inicjowane są uchwyty obu bibliotek, wywoływana jest funkcja uart_setup() oraz uruchamiana jest obsługa przerwań peryferialu UART. W sekcji USER CODE 3 ciągle wywoływana jest, omówiona powyżej, funkcja frame_parser_process_frame().
Aleksander Kurczyk
(Uwaga! Kompletny artykuł zawierający wszystkie rysunki i listingi jest dostępny w pliku PDF).