Zapraszam na praktyczną demonstrację połączenia CAN pomiędzy dwoma urządzeniami z ESP32. Pokażę tutaj, jak wygląda procedura nadawania i odbierania ramek na podstawie sterownika TWAI, użyję środowiska PlatformIO. TWAI to skrót od Two-Wire Automotive Interface, czyli w wolnym tłumaczeniu interfejs samochodowy dwuprzewodowy. Jest to oficjalna nazwa używana w SDK od Espressif, choć ja tutaj raczej będę stosować ogólnoprzyjętą nazwę CAN.
Co to jest CAN?
CAN (Controller Area Network) to standard komunikacji szeregowej zaprojektowany do niezawodnej wymiany danych pomiędzy urządzeniami elektronicznymi w czasie rzeczywistym. Najczęściej wykorzystywany jest w motoryzacji (komunikacja ECU, ABS), automatyce przemysłowej czy systemach IoT.
Charakteryzuje się:
- transmisją wielowęzłową (wiele urządzeń na jednej magistrali)
- detekcją i obsługą błędów na poziomie sprzętowym
- priorytetyzacją ramek
- szybkością od kilkudziesięciu kbit/s do 1 Mbit/s (w klasycznym CAN)
Schemat magistrali CAN pokazuje poniższy obrazek:
Źródło obrazka: wikipedia - Stefan-Xp, licencja CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=3607670
Z praktycznego punktu widzenia musimy teraz wiedzieć, jak zestawić takie połączenie - tu nie ma osobnych linii RX i TX, tylko są linie H i L. Nie zamieniamy tu H i L - nawyk z UART nie znajduje tu odzwierciedlenia. Dodatkowo na końcach magistrali CAN powinny być rezystory terminujące o wartości 120 omów, masa też powinna być wspólna.
Plan działania
ESP32 posiada wbudowany kontroler CAN (TWAI), ale wymaga dodatkowego transceivera (np. SN65HVD230, MCP2551 lub kompatybilny), który dopasuje sygnały logiczne 3,3 V do poziomów magistrali CAN_H / CAN_L. Tu przychodzi nam z pomocą gotowy moduł, który prezentowałem wcześniej:
LilyGO T-CAN485 - ESP32 do nauki łączności przemysłowej, magistrale RS485 i CAN
Połączymy ze sobą dwa takie moduły. Przygotujemy jeden program, zasadniczo weźniemy przykład od LilyGo i zrobimy z niego dwie wersje. Obie będą nadawać i odbierać, a my w konsolach UART sprawdzimy, czy płytki wzajemnie się widzą. Przy okazji poznamy jak to wszystko działa.
Dokumentacja sterownika, którego użyjemy: https://docs.espressif.com/projects/esp-idf/e...ble/esp32/api-reference/peripherals/twai.html
Projekt PlatformIO
Na tym etapie możemy już utworzyć projekt w PlatformIO. Aż prosi się tutaj zastosować jedną z właściwości tej platformy - możliwość wygodnego wydzielenia dwóch wersji projektu. Pozwoli to nam jednocześnie obsłużyć dwa porty szeregowe do debugowania oraz rozróżnić co wysyła która płytka.
Kod: Ini
Można by tu też dziedziczyć po wspólnym projekcie, ale nie chciałem komplikować. Najważniejszy jest to wybór portu COM (dwa różne) oraz załączenie dodatkowego #define dla drugiej kompilacji. Użyjemy tego do rozróżnienia co będzie wysyłane.
Przygotujmy przykładowy kod projektu. Będzie on zasadniczo lekko zmodyfikowanym przykładem od LilyGo:
https://github.com/Xinyuan-LilyGO/T-CAN485/bl...rduino-esp32-libs_v3.0.1/examples/CAN/CAN.ino
Najpierw nagłówki, stałe (piny od CAN) i zmienne.
Kod: C / C++
Potem uruchomienie sterownika. Jest tu nieco więcej kodu, niż przy UART, ale i tak wcale nie jest on trudny. Mamy tu ustawienie pinów oraz szybkości transferu. Zdecydowałem się na 250 kilobitów na sekundę. Potem możemy skonfigurować alerty CAN, które pozwalają nam reagować na różne zdarzenia w magistrali. W przykładzie włączamy alerty związane z zakończeniem transmisji, błędami, pełną kolejką odbiorczą i nowymi danymi do odczytu. Dzięki temu nasza aplikacja będzie wiedziała, kiedy wysyłanie lub odbiór wiadomości się powiodło, kiedy wystąpił błąd magistrali, lub kiedy kolejka odbiorcza jest pełna.
Kod: C / C++
Następnie mamy funkcję wysyłającą wiadomość. Tu jest inaczej niż w UART - wysyłamy gotowe ramki. Określamy dla niej identyfikator, typ ramki (standardowa/rozszerzona, dane/zdalna), długość danych oraz same dane (warunkowo, różne w każdej wersji), a następnie próbujemy ją wysłać funkcją twai_transmit(), sprawdzając, czy operacja się powiodła.
Kod: C / C++
Po drugiej stronie mamy funkcję odbioru ramki. Tu też mamy dostęp do jej identyfikatora, długości i danych.
Kod: C / C++
Zostaje jeszcze funkcja setup, czyli ogólna konfiguracja pinów i załączenie sterownika CAN. To tutaj dla przejrzystości dodałem informacje o tym, która z wersji programu została uruchomiona.
Kod: C / C++
Na koniec funkcja loop, czyli to, co wykonuje się w pętli. Najpierw mamy tu odczyt alarmów. Odczyt ten jest blokujący - czas blokady podany jest w tickach RTOS, z tego powodu w argumencie użyto pdMS_TO_TICKS. Potem odczytywane są alarmy i status. Sprawdzana jest flaga odbioru - wtedy wywoływana jest obsługa pakietu. Również okresowo wysyłany jest nowy pakiet.
Kod: C / C++
Zostaje tylko połączyć ze sobą dwie płytki tak, jak pokazałem na schemacie i wgrać na nie odpowiednie programy. PlatformIO bardzo to ułatwia, można zestawić obok siebie nawet dwa monitory portów szeregowych i porównywać co się dzieje:
Zasadniczo widać, że wszystko działa. Na tej bazie można realizować własną komunikację poprzez protokół CAN.
Podsumowanie
W ten sposób można skomunikować ze sobą dwa lub więcej urządzeń przez magistralę CAN. CAN początkowo mógł wydawać się dość trudny, ale moim zdaniem ta krótka prezentacja pokazała, że w rzeczywistości jest bardzo prosty w obsłudze. Gotowa biblioteka wszystko sprawdza, obsługuje błędy, oraz podaje nam jak na tacy gotowe ramki wraz z informacjami o nich.
Czy korzystaliście z CAN, a jeśli tak, to w jakich projektach?
Fajne? Ranking DIY Pomogłem? Kup mi kawę.