CAN FD w nowoczesnych STM32

Original

Czym jest CAN?

CAN (ang. Controller Area Network) – magistrala zaprojektowana z myślą o pojazdach, z czasem stała się standardem również w innych zastosowaniach. Można ją spotkać również w systemach automatyki przemysłowej oraz np. w urządzeniach medycznych.

Cechy magistrali CAN:

  • transmisja różnicowa – znacznie zwiększa odporność na zakłócenia zewnętrzne
image 28
Fot. W magistrali CAN używa się skręconej pary przewodów
  • brak klasycznego podziału Master / Slave – dzięki zastosowanym rozwiązaniom każde urządzenie może nadawać swoje ramki gdy magistrala jest wolna
  • arbitraż na podstawie ID ramki – w przypadku kolizji, ramka o mniejszym ID ma większy priorytet
  • Limit urządzeń wynika z parametrów linii oraz samych transceiverów, przyjmuje się bezpieczną wartość 30 urządzeń

CAN FD

Standardowa ramka CAN umożliwia wysłanie maksymalnie zaledwie 8 bajtów danych. W większości przypadków, jeśli chcemy przesyłać np. pomiary z czujników, jest to wartość wystarczająca.

Ze względu na coraz większe skomplikowanie systemów oraz ciągle rosnącą ilość urządzeń w pojazdach, standard CAN został rozszerzony o FD – ang. Flexible Data-Rate. W tabeli poniżej przedstawiam główne różnice między standardami:

CechaKlasyczny CAN (2.0)CAN FD
Maksymalna długość danych8 bajtów64 bajty
Maksymalna prędkość fazy arbitrażu1 Mbit/s1 Mbit/s
Maksymalna prędkość transmisji pola danych1 Mbit/s8 Mbit/s
CRC15 bitów17 / 21 bitów (w zależności od ilości danych)

Ze względu na rozszerzenie ramki CAN w stosunku do standardu 2.0, magistrale te nie są ze sobą w pełni kompatybilne:

  • Jeśli w magistrali przesyłane są ramki FD – nie powinno być do niej był przyłączone żadne urządzenie CAN 2.0 (urządzenie będzie wykrywało błędy na magistrali i zakłóci jej działanie)
  • Urządzenia CAN FD są kompatybilne z magistralą CAN 2.0 tak długo, jak nie próbują wysyłać ramek w standardzie FD.

Poniżej przykładowe ramki CAN:

image7
Ryc. Ramka CAN 2.0
image8
Ryc. Przykładowa ramka CAN FD (Bitrate switch – 500kbps / 8Mbps)

Peryferia FDCAN vs. CAN/bxCAN w STM32

Wprowadzone w nowszych mikrokontrolerach peryferium FDCAN jest w stanie całkowicie zastąpić istniejący wcześniej bxCAN. Poza obsługą szybszej transmisji danych wprowadza kilka dodatkowych usprawnień.

bxCAN

Dotychczas (bxCAN) filtry CAN były konfigurowane w przeznaczonej do tego przestrzeni – w tzw. bankach filtrów. Banki mieściły:

  • 3 ramki CAN w Tx Mailbox (oczekujące na wysłanie),
  • 3 ramki CAN w FIFO0,
  • 3 ramki CAN w FIFO1,
  • 14 banków filtrów – każdy po jednym filtrze 32-bit lub po dwa filtry 16-bit,
    • Jeśli mikrokontroler posiada dwa peryferia bxCAN – wówczas 28 banków można podzielić wg uznania.

FDCAN

W przypadku nowszego peryferium FDCAN organizacja ramek wygląda inaczej niż w bxCAN. Zamiast sztywnego układu wykorzystywana jest współdzielona pamięć – tzw. Message RAM.

Jest to obszar pamięci przeznaczony na

  • filtry
  • bufory odbiorcze
  • FIFO odbiorcze
  • bufory nadawcze
  • kolejkę/FIFO nadawcze
  • FIFO zdarzeń transmisji

Dzięki zastosowaniu Message RAM, FDCAN jest znacznie bardziej elastyczny, ale wymaga świadomego podziału pamięci. Ilość możliwych do przechowania ramek nie jest stała – zależy od konfiguracji Message RAM, wybranej długości danych oraz konkretnego modelu STM32. Dla ramek CAN FD o długości 64 bajtów każdy element FIFO lub bufora zajmuje więcej pamięci niż dla klasycznej ramki 8-bajtowej, dlatego maksymalna ilość buforowanych ramek może być mniejsza.

Konkretna implementacja peryferium FDCAN może różnić się w poszczególnych rodzinach STM32. Poniżej porównanie peryferium FDCAN między dwoma przykładowymi rodzinami STM32:

CechaSTM32G474STM32H742
Liczba instancji3xFDCAN2xFDCAN
Wielkość współdzielonej pamięci Message RAM3kB10kB
Możliwość konfiguracji w CubeMXSztywno ustalony podział pamięci pomiędzy instancje. Możliwość zmiany tylko w kodzie aplikacjiMożliwość zmiany podziału pamięci pomiędzy instancje (Ryc.)
image13
Ryc. Możliwości konfiguracji ilości elementów w MessageRAM w STM32H742

Dla STM32G474 domyślna konfiguracja ustawiana przez CubeMX dla jednej instancji FDCAN wygląda następująco:

ElementIlośćWielkość
Filtr standardowy (4 bajty)28 filtrów112 bajtów
Filtr rozszerzony (8 bajtów)8 filtrów64 bajty
Bufor ramki Rx FIFO0 (72 bajty)3 ramki216 bajtów
Bufor ramki Rx FIFO1 (72 bajty)3 ramki216 bajtów
Tx event FIFO (8 bajtów)3 eventy24 bajty
Bufor ramki Tx FIFO/Queue (72 bajty)3 ramki216 bajtów
Suma:848 bajty

W ogólnej architekturze FDCAN rozmiar elementów Rx/Tx może być dobrany do maksymalnej długości danych, np. 8 B zamiast 64 B. Dla klasycznego CAN pozwala to zmniejszyć zużycie Message RAM, ponieważ element przechowujący 8 bajtów danych zajmuje znacznie mniej miejsca niż element przygotowany na 64 bajty. W przypadku STM32G474 konfiguracja generowana przez CubeMX jest jednak ograniczona, dlatego zmiana takiego podziału może wymagać ręcznej modyfikacji kodu i sprawdzenia zgodności z HAL oraz reference manual. 

Transceiver CAN vs. CAN FD

Peryferium CAN/bxCAN/FDCAN generuje sygnały logiczne TXD/RXD, ale to transceiver zamienia je na sygnał różnicowy CANH/CANL. Nawet jeśli mikrokontroler ma FDCAN, to do pracy w CAN FD potrzebny jest również odpowiedni transceiver.

Ponieważ transceiver CAN odpowiada tylko za warstwę fizyczną, jego wybór nie ma związku z obsługą konkretnego standardu od strony logicznej, tj. budowy ramki, CRC itp.

Poniżej ogólne porównanie transceiverów CAN 2.0 / CAN FD:

CechaTransceiver CAN 2.0Transceiver CAN FD
Obsługa CAN 2.0TakTak
Obsługa CAN FD bez BRS (do 1Mbps)TakTak
Obsługa CAN FD z BRS (do 8Mbps)Niezalecane, może nie działać stabilnieTak

Transceiver CAN FD może być bez problemu używany także w klasycznym CAN, natomiast klasyczny transceiver CAN nie powinien być traktowany jako pełnoprawny zamiennik transceivera CAN FD w szybkiej magistrali.

Konfiguracja FDCAN w CubeMX

Do przykładu wykorzystania CAN FD z mikrokontrolerami STM32 użyjemy Nucleo G474RE oraz nakładkę 3xCAN FD od Embedded Garage.

image 5
Fot. Zestaw deweloperski Nucleo G474 + CAN HAT

Ze strony producenta nakładki https://sklep.embeddedgarage.com/produkt/nucleo-g474-can-fd-hat/ pobieramy “CAN_HAT_CubeMX_project_v1”. Jest to szablon projektu w CubeMX przygotowany pod nasz zestaw testowy – dzięki niemu nie będziemy musieli analizować schematu układu.

Ważne! Aby utworzyć własny projekt na podstawie szablonu, należy zmienić nazwę zarówno folderu jak i pliku *.ioc. Nazwy te muszą być identyczne.

image17
Ryc. Szablon projektu CubeMX

Konfiguracja prędkości FDCAN

Wynikowa prędkość komunikacji FDCAN zależy od kilku parametrów:

  • częstotliwość zegara dostarczanego do peryferium FDCAN
  • dzielnika zegara na wejściu FDCAN (Clock Divider)
  • Preskalera
  • Konfiguracji parametrów Time Seg1 oraz Time Seg2

W przypadku ramek CAN FD Preskaler, Time Seg1 oraz Time Seg2 konfigurujemy osobno również dla fazy danych.

image2
Ryc. Konfiguracja zegara FDCAN
image9
Ryc. Konfiguracja Baud Rate dla FDCAN

W celu zrozumienia parametrów Time Seg1 oraz Time Seg2 odsyłam do https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf rozdział 44.3.3 Bit Timing.

Konkretne wartości powinny być ustalane w zależności od docelowego środowiska.

Poniżej przykładowe parametry dla różnych prędkości CAN z zegarem 160Mhz:

PrędkośćSync Jump WidthPrescalerTime Seg1Time Seg2
20 kbps6200318
100 kbps640318
125 kbps632318
250 kbps616318
500 kbps68318
1 Mbps64318
2 Mbps62318
4 Mbps42145
5 Mbps61238
8 Mbps41145

W tabelce powyżej pojawił się nieznany parametr Sync Jump Width. Określa on maksymalną korektę synchronizacji bitu ramki CAN / CAN FD. Parametr ten jest wyrażany w jednostkach Time Quantum i pozwala kontrolerowi FDCAN kompensować niewielkie różnice czasowe wynikające z tolerancji zegarów, opóźnień transceiverów oraz propagacji sygnału na magistrali. W CAN FD parametr ten występuje osobno dla fazy danych, ponieważ przy włączonym BRS faza danych może być transmitowana z wyższą prędkością niż faza arbitrażu. Zwykle DataSyncJumpWidth ustawia się na wartość nie większą niż DataTimeSeg2.

Proponuję ustawić parametry 1Mbps / 8Mbps.

Inne parametry FDCAN

  • Frame format – Określa wspierany format ramek. Aby wspierać zarówno ramki CAN2.0 oraz CAN FD ze zmianą prędkości danych należy ustawić ten parametr na FD mode with BitRate Switching.
    Format wysyłanych ramek będziemy określać niezależnie w trakcie ich tworzenia.
  • Mode – Określa tryb pracy peryferium. Wybieramy tryb Normal – pozwoli na standardową, dwustronną komunikację. Więcej o trybach pracy w Reference Manual RM0440 – rozdz. 44.3.4
  • Auto retransmission – powoduje automatyczne ponowienie transmisji, jeśli ramka nie została poprawnie nadana, np. z powodu błędu transmisji albo braku potwierdzenia ACK. Przegranie arbitrażu w CAN nie jest traktowane jako błąd — kontroler ponowi nadawanie, gdy magistrala będzie wolna. Na czas testów zalecam wyłączyć tę opcję – unikniemy przejścia peryferium w tryb ERROR z powodu braku potwierdzenia wysyłanej ramki.
  • Transmit Pause – włącza dodatkowe krótkie opóźnienie po nadaniu ramki, dzięki czemu inne urządzenia będą w stanie “wstrzelić się” ze swoimi ramkami. Dla naszych testów ten parametr jest bez znaczenia.
  • Protocol Exception Handling – określa zachowanie kontrolera FDCAN w przypadku wykrycia określonych wyjątków protokołu CAN FD. Nie jest to filtr ramek ani ogólny mechanizm ignorowania wszystkich błędów magistrali. W typowych aplikacjach można pozostawić tę opcję wyłączoną, zgodnie z domyślną konfiguracją CubeMX. 

Generowanie projektu

W zakładce Projekt Manager możesz ustawić sposób generowania projektu. Domyślnie dla tego szablonu jest to CMake + GCC. Ta konfiguracja bardzo dobrze sprawdza się w połączeniu z Visual Studio Code z oficjalnym dodatkiem STM32CubeIDE for Visual Studio Code od STMicroelectronics.

Nie pozostało nic innego jak wygenerować projekt i otworzyć go w VSCode!

Wprowadzenie do VSCode

Zdaję sobie sprawę, że VSCode nie jest domyślnym IDE wybieranym przez większość programistów do obsługi STM32, dlatego w pigułce pokażę jak zaprogramować i uruchomić program.

Jeśli proces jest już Tobie znany, spokojnie możesz pominąć ten rozdział.

Kompilacja kodu

Po zainstalowaniu VSCode oraz dodatku STM32CubeIDE for Visual Studio Code otwieramy projekt i konfigurujemy go:

  1. Wybieramy dodatek STM32
  2. W Menu STM32CUBE KEY ACTIONS wybieramy funkcję Set Up STM32Cube project(s)
  3. Upewniamy się, że wybrany jest Board / Device NUCLEO-G474RE oraz odpowiedni toolchain GCC
  4. Wybieramy target budowania projektu. Polecam wybrać Debug – umożliwi to sprawne debugowanie
image 29
Ryc. Konfiguracja projektu w VSCode

Konfiguracja może chwilę potrwać. Jeśli zauważymy, że nic widocznego się już w IDE nie dzieje, możemy skompilować projekt klikając przycisk Build na dolnej belce programu:

image15
Ryc. Kompilacja programu zakończona powodzeniem

Voilà! Kod skompilowany, czas go uruchomić!

Uruchomienie Debug

Kod się kompiluje, nic nas nie powstrzyma!

  1. Wybieramy zakładkę RUN AND DEBUG
  2. Klikamy odnośnik create a launch.json file – jeśli zrobimy to teraz, to później wystarczy używać klawisza F5 na klawiaturze aby uruchomić debug
  3. Wybieramy interfejs debugowy – w naszym przypadku będzie to programator ST-LINK wbudowany w płytkę NUCLEO
image5

Na ekranie pojawi się nowo utworzony plik launch.json. Jest to plik konfiguracyjny, który został automatycznie umieszczony w katalogu .vscode. W mojej opinii jest to ogromna zaleta VSCode – trzymanie ustawień projektu w czytelnych plikach JSON. Ułatwia to ich archiwizowanie oraz porównywanie / kopiowanie konfiguracji pomiędzy projektami. Otwarty plik zapisujemy skrótem klawiaturowym Ctrl+S.

Jak już wspominałem, na tym etapie wystarczy nacisnąć klawisz F5 – program powinien się uruchomić i zatrzymać na domyślnym breakpoint na początku funkcji main().

W przypadku problemów odsyłam do filmu, w którym pokazuję krok po kroku cały powyższy proces 1# Przygotowanie projektu *UART na DMA w STM32* (VSCode ,STM32Cube, CMake)

Przykład praktyczny

Jako zwieńczenie tego artykułu uruchomimy wspólnie projekt testowy, który zawiera zarówno nadawanie jak i odbiór danych przez FDCAN.

Proponuję połączyć ze sobą dostępne interfejsy CAN. Dzięki temu:

  • natychmiast zaobserwujemy wysłane przez nas ramki
  • potwierdzimy poprawność obu operacji – nadawania oraz odbioru
  • sąsiedni interfejs potwierdzi wysyłaną przez nas ramkę – nie doprowadzimy do sytuacji, że nadajnik przejdzie w stan Error i przestanie nadawać ramki
  • nie będziemy musieli kupować dwóch zestawów do zabawy

Chciałbym, aby przedstawiany przykład był możliwie najprostszy, dlatego umówmy się, że wysyłać dane będziemy przez pierwszy interfejs (FDCAN1), a odbierać przez drugi (FDCAN2).

Uwaga! Będziemy używać funkcji delay! Tak, wiem – to zakazane. Jednak w tym przypadku pozwoli to na maksymalne uproszczenie przykładu. W dodatku – tak jak wspominałem, interfejs CAN “żyje swoim życiem” – więc pomimo, że program będzie bezczynnie czekał jedną sekundę, interfejs będzie odbierał ramki i umieszczał w Message RAM. My tylko sprawdzimy czy coś się tam w tym czasie nie pojawiło

Uruchomienie komunikacji

Transceivery CAN umieszczone na nakładce CAN HAT zawierają piny !STDBY połączone z portami mikrokontrolera. W CubeMX są one już zdefiniowane (odpowiednio FDCAN1_STDBY oraz FDCAN2_STDBY), więc jedyne co powinniśmy zrobić, to ustawić im odpowiedni (niski) stan.

Najlepszym miejscem na to będzie przestrzeń na kod użytkownika zaraz przed pętlą nieskończoną:

image 22
Ryc. Uruchomienie transceiverów FDCAN1 i FDCAN2

Kolejną czynnością wspólną dla obu interfejsów będzie uruchomienie samej komunikacji CAN:

image 27

Odbieranie danych

Generator projektu CubeMX zadbał o ustawienie parametrów zgodnie z naszymi oczekiwaniami. Domyślnie filtry FDCAN są ustawione w taki sposób, że otrzymane ramki trafiają do kolejki FIFO0.

Po Uruchomieniu komunikacji peryferium FDCAN zaczyna “żyć swoim życiem”. Jeśli zostanie odebrana jakaś ramka, interfejs automatycznie umieści ją w kolejce FIFO we wspomnianej wcześniej pamięci Message RAM. Z naszej strony wystarczy sprawdzić czy pojawiła się jakaś ramka i ją odczytać.

Aby odebrać dane, musimy wykonać więc następujące czynności:

  1. Sprawdzić, czy w kolejce FIFO są dostępne nowe elementy
  2. Przygotować przestrzeń do odczytu odebranej ramki
  3. Odczytać otrzymaną ramkę
  4. Zareagować na otrzymaną ramkę – w naszym przypadku zamrugamy diodą LD2 obok złącza interfejsu CAN1
image10

Nadawanie danych

Wysyłanie danych jest równie proste. Musimy jednak zadbać o odpowiednie wypełnienie nagłówka wiadomości – od tego zależy w jaki sposób ramka zostanie wysłana przez interfejs FDCAN.

Należy wykonać następujące kroki:

  1. Przygotować nagłówek ramki. Tutaj umieszczamy informacje zarówno o ID ramki jak i o sposobie jej transmisji. W naszym przykładzie od razu pójdziemy na całość – wyślemy ramkę CAN FD ze zmianą prędkości transmisji.
  2. Przygotować bufor danych o odpowiedniej długości. Żeby nie znalazły się w nim losowe dane, proponuję go wypełnić za pomocą funkcji memset(). Pamiętajmy o dodaniu nagłówka <string.h> w sekcji Private Includes
  3. Dodanie do kolejki TxFifo
  4. Jeśli dodanie do kolejki się powiedzie, zmieńmy stan LED1 przy interfejsie CAN2
  5. Na koniec dodajmy wspomniany delay
image16

Uruchomienie i… nie działa!

Co mogło pójść nie tak? Ułatwię nam te rozważania 😀 Celowo zostawiłem to na koniec. Gdybyś teraz zaczął testować różne konfiguracje, okazałoby się, że problem występuje tylko w przypadku ustawienia prędkości FDCAN na 8Mbps. Jeśli zmniejszymy prędkość – wszystko będzie działać bez zarzutu. Problemem jest czas propagacji sygnału w pętli nadajnika.

TL;DR: Transceiver, aby nadać ramkę musi aktywnie słuchać tego, co dzieje się na magistrali. Jeśli nadaje stan recesywny – oczekuje, że taki stan pojawi się na magistrali. Jeśli odczytany stan będzie inny – uzna, że nastąpiła kolizja i przerwie nadawanie.

W przypadku CAN FD, ze względu na znacznie skrócony czas trwania bitu, istotny staje się czas, w jakim sygnał z magistrali wraca do odbiornika.

Czas propagacji zależy więc przede wszystkim od czasu reakcji układów wewnętrznych transceivera.

Czy w takim razie osiągnęliśmy limit prędkości naszego zestawu? Oczywiście, że nie!

Rzut oka na schemat nakładki:

image 23
Ryc. Schemat obwodu transceivera CAN nakładki CAN HAT

I widzimy, że wykorzystane zostały transceivery MCP2562FD. Przeglądając notę katalogową transceivera znajdziemy diagram:

image 24

Czas który nas interesuje ukryty jest pod numerem 7. Wyjaśnia to tabela:

image 25

Wynika z tego, że opóźnienie sygnału wynosi maksymalnie 120 ns.

Teraz garść obliczeń:

Czas trwania Time Quantum dla naszej konfiguracji wynosi 1 / (Fcanfd / ClockDivider / Prescaler). Podstawiając do wzoru: 1 / (160Mhz / 1 / 1) = 6,25ns.

Obliczmy teraz ilość Time Quantum dla czasu propagacji z dokumentacji: 

  • dla wartości typowej: 90ns / 6,25ns = 14,4

Uwaga! Powyższe obliczenia zostały dokonane dla prędkości 8Mbps którą ustawiliśmy wcześniej zgodnie z tabelą.

Taką wartość należy teraz umieścić w konfiguracji interfejsów:

image19
Ryc. Konfiguracja kompensacji opóźnienia pętli nadawanie – odbiór

Funkcja przyjmuje jeszcze parametr TdcFilter. Służy on do odfiltrowania nieoczekiwanego zbocza opadającego przed odliczeniem czasu TdcOffset (czyli naszego opóźnienia). Przydatny jeśli na magistrali pojawiają się zakłócenia czy tzw. ringing – wówczas sensowne będzie jego ustawienie. W naszym przypadku pozostawimy tutaj wartość 0.

Powyższe parametry można również dobrać doświadczalnie – ustawiać coraz większe wartości i wybrać średnią z wartości dla której już zadziałało oraz jeszcze działa. Można również dla potwierdzenia zmierzyć tą wartość oscyloskopem / analizatorem stanów logicznych.

Na koniec uruchomienie programu i… działa 🙂

image4


Podsumowanie

CAN FD jest rozwinięciem klasycznego CAN, które zachowuje najważniejsze zalety tej magistrali – odporność na zakłócenia, arbitraż na podstawie identyfikatora ramki oraz prostą topologię wielowęzłową. Jednocześnie usuwa jedne z największych ograniczeń klasycznego CAN: niewielką długość pola danych oraz ograniczoną przepustowość. Dzięki możliwości przesyłania do 64 bajtów danych w jednej ramce oraz zastosowaniu wyższej prędkości w fazie danych, CAN FD dobrze sprawdza się w nowoczesnych systemach embedded, automotive i przemysłowych.

W mikrokontrolerach STM32 obsługa CAN FD realizowana jest przez peryferium FDCAN, które zastępuje starszy kontroler bxCAN. W porównaniu z bxCAN, FDCAN oferuje znacznie większą elastyczność: wykorzystuje konfigurowalną Message RAM, obsługuje większe ramki, osobne prędkości dla fazy arbitrażu i danych, bardziej rozbudowane filtry oraz mechanizmy takie jak Tx Delay Compensation. Jednocześnie wymaga dokładniejszej konfiguracji, szczególnie w zakresie podziału Message RAM, ustawień bit timing, filtrów oraz parametrów transmisji CAN FD.

Istotnym elementem projektu jest również warstwa sprzętowa. Sam mikrokontroler z FDCAN nie wystarczy do poprawnej pracy z CAN FD przy wysokich prędkościach. Konieczne jest zastosowanie odpowiedniego transceivera CAN FD, poprawna terminacja magistrali oraz właściwe prowadzenie sygnałów CANH/CANL. Przy transmisji z dużą prędkością, np. 8 Mbit/s w fazie danych, znaczenie zaczynają mieć także opóźnienia propagacji transceivera. W takich przypadkach należy skonfigurować mechanizm Tx Delay Compensation, który pozwala kontrolerowi FDCAN poprawnie próbkować sygnał mimo opóźnienia w pętli nadajnik – odbiornik.

Podsumowując, FDCAN w STM32 daje duże możliwości i pozwala budować wydajne sieci CAN FD, ale wymaga lepszego zrozumienia działania magistrali niż klasyczny CAN. Poprawnie skonfigurowany układ pozwala przesyłać większe pakiety danych z wyższą prędkością, zachowując przy tym prostotę i niezawodność znaną z klasycznego CAN.

Autor: Mikołaj Andrzejewski

Przewijanie do góry