LinkedIn YouTube Facebook
Szukaj

Newsletter

Proszę czekać.

Dziękujemy za zgłoszenie!

Wstecz
Artykuły

CAN od zera część 3: Jak zarządzać danymi CAN w RTOS? Porównanie mechanizmów i wybór najlepszego

Dlaczego w systemie CAN gubią się ramki, mimo że magistrala działa poprawnie?

To pytanie pojawia się bardzo często.

Analizator CAN pokazuje, że wszystkie ramki zostały wysłane. Transceiver działa poprawnie. CRC się zgadza. A mimo to mikrokontroler nie widzi części wiadomości.

Problem w większości przypadków nie leży w protokole CAN, lecz w:

  • sposobie zarządzania pamięcią,
  • komunikacji między ISR a zadaniami RTOS,
  • nieoptymalnym przekazywaniu danych.

W tym artykule pokażę:

  • dlaczego proste podejście ISR + statyczny bufor prowadzi do utraty ramek,
  • jak wygląda profesjonalna implementacja w systemie RTOS,
  • dlaczego Memory Pool + kolejki z przekazywaniem wskaźników to wzorzec stosowany w systemach automotive i przemysłowych.

Przykłady bazują na mikrokontrolerze STM32.

Schemat podłączenia:

Połączenie układów jest identyczne jak w poprzednim wpisie, czyli mamy dwa mikrokontrolery połączone ze sobą transceiverami protokołem CAN, co przedstawia poniższy schemat blokowy.

Metody zarządzania pamięcią we wbudowanych systemach operacyjnych:

1. Statyczny bufor + ISR + FIFO (najprostsza metoda)

To najprostszy model odbioru danych: przerwanie odbioru CAN zapisuje dane do statycznego bufora (np. tablica struktur), a następnie dane są pobierane w kolejności FIFO.

Przykład bufora:



Zarządzanie wymaga ręcznego obsłużenia indeksów, flag zajętości i kolejkowania.

Ta metoda jest:

  • deterministyczna (czas zawsze taki sam — brak dynamicznej alokacji),
  • bezpieczna pamięciowo (statycznie zaalokowane tablice),
  • mało skalowalna, ponieważ: (ISR wykonuje dużo logiki, przekazywanie danych między zadaniami nie jest izolowane, zmiana rozmiaru bufora wymaga rekompilacji)

Użycie: To dobra metoda na małe systemy i prosty ruch CAN.

2. Statyczny bufor + ISR + Semafor (usprawniona metoda)

To rozwinięcie metody nr 1.

ISR zapisuje ramkę do bufora (FIFO), ale zamiast aktywnie odpytywać bufor, zadanie budzi się dzięki semaforowi:

  • ISR → daje semafor
  • Zadanie → czeka na semafor (blokująco)

Zalety:

  • nadal deterministyczne i statyczne,
  • ISR jest krótsze (tylko zapis + semafor),
  • zadanie CAN nie pracuje gdy nie ma ramek,
  • dobre dla systemów z: (stałym rozmiarem ramek, niewielką liczbą typów wiadomości, umiarkowanym obciążeniem.)

Użycie: To najczęściej spotykana metoda w prostych systemach RTOS.

3. Dynamiczna alokacja (malloc/free)

ISR lub zadanie przydziela pamięć każdej ramce osobno:



Zalety:

  • pełna elastyczność — każdy obiekt może mieć inny rozmiar.

Wady:

  • ryzyko fragmentacji, szczególnie po wielu godzinach/dniach,
  • konieczność ręcznego zwalniania pamięci,
  • potencjalne wycieki i zawieszenia systemu,
  • czas nie jest deterministyczny,
  • niedopuszczalne w systemach wymagających wysokiej niezawodności.

Użycie: Tę metodę stosuje się rzadko — głównie w systemach niekrytycznych lub eksperymentalnych.

4. Kolejki RTOS (Queue) – pełne struktury vs wskaźniki

Zadania mogą komunikować się poprzez:

a) Przesyłanie całych struktur

Łatwe, czytelne, ale:

  • RTOS musi kopiować nawet duże struktury,
  • powoduje duże obciążenie CPU,
  • kolejki mają mały maksymalny rozmiar (kilkadziesiąt wiadomości).

b) Przesyłanie wskaźników w kolejce (zalecane)

Kolejka zawiera tylko pointer, np. do bufora statycznego lub z memory pool.

Zalety:

  • minimalny czas ISR,
  • mało kopiowania danych,
  • skalowalne i wydajne.

Użycie: To najlepsza forma użycia kolejek RTOS w CAN.

5. Memory Pool (najlepsza metoda w RTOS)

To wcześniej przygotowana pula identycznych bloków pamięci, z których system może szybko „wypożyczać” i „oddawać” elementy. Wyobraź sobie, że zamiast dynamicznie budować pamięć za każdym razem (malloc), przygotowujesz na starcie. Potem tylko: bierzesz jeden wolny blok, używasz go, oddajesz go do puli. Bez dzielenia sterty, bez szukania miejsca w pamięci, bez fragmentacji.

Prosta analogia:

Malloc to jak:

  • Szukasz miejsca parkingowego w mieście.
  • Czasem znajdziesz od razu.
  • Czasem krążysz 10 minut.

Memory Pool to jak:

  • Masz prywatny parking z 32 miejscami.
  • Jeśli jest wolne miejsce – wjeżdżasz natychmiast.
  • Jeśli nie ma – od razu wiesz, że parking jest pełny.

ISR odbiera ramkę i rezerwuje blok o stałym rozmiarze z memory pool:

ISR:

  • pobiera blok z poola (stały czas)
  • zapisuje ramkę do bloku
  • wrzuca wskaźnik do kolejki

Zadanie:

  • odbiera wskaźnik z kolejki
  • dekoduje ramkę
  • zwalnia blok do poola

Zalety:

  • zero fragmentacji
  • deterministyczne czasy alokacji i zwalniania
  • idealne do stałych rozmiarów wiadomości CAN
  • ISR bardzo szybkie
  • zadanie dostaje gotowy wskaźnik — zero kopiowania

Użycie: To zdecydowanie najbardziej profesjonalna metoda w systemach pracujących latami.

Porównanie metod:

Przypadek 1 – Dlaczego gubimy ramki?

Założenia testu:

Mikrokontroler 1:

  • wysyła watchdog ID 0x11 co 100 ms,
  • wysyła ID 0x12 po naciśnięciu przycisku.

Mikrokontroler 2:

  • używa statycznego bufora + ISR,
  • przetwarza dane w pętli głównej.

Efekt:

Analizator CAN pokazuje 20 wysłanych ramek ID 0x12.
Mikrokontroler odbiera tylko 5.

Dlaczego?

Bo:

  • ISR ustawia tylko flagę,
  • przetwarzanie w pętli głównej trwa zbyt długo,
  • nowe ramki nadpisują poprzednie dane.

Reasumując:

Magistrala działa poprawnie.
Zarządzanie pamięcią – nie.

Tak wygląda kod do obsługi przerwania:



Główna pętla:



Wysyłam z Mikrokontrolera 1 ramkę o ID 0x11 jako watchdog do Mikrokontrolera 2 co obserwujemy za pomocą konwertera USB CAN w programie USBCAN. Ramki wysyłane są co 100ms co widać na poniższym rysunku:

Po naciśnięciu przycisku wysyłane są ramki o ID 0x12, aby podsłuchiwać tylko te ramki wprowadziłem do programu USBCAN filtr na to ID.

Jak widać na powyższym rysunku rysunku Mikrokotroler 1 wysłał 20 ramek o ID 0x12, a Mikrokontroler 2 odebrał tylko 5 ramek o ID 0x12.

Przypadek 2 – RTOS + Memory Pool (rozwiązanie profesjonalne)

W tym wariancie w Mikrokontrolerze 2:

  • używam dwóch filtrów (FIFO0 i FIFO1),
  • tworzę dwie kolejki,
  • tworzę dwa memory poole,
  • każde FIFO obsługuje osobne zadanie.

ISR trwa bardzo krótko. Zadania przetwarzają dane niezależnie.

Efekt testu:

  • 20 wysłanych ramek ID 0x12.
  • 20 odebranych ramek ID 0x12.

Reasumując: Bez strat.

Poniżej umieściłem diagram pokazujący przepływ danych odbieranych po CAN:

W porównaniu do poprzednich wpisów w tym przypadku, użyłem dwóch filtrów:

Dalej włączyłem dwa przerwania od CAN:

Uruchomiłem RTOS i dodałem następujące dwa zadania:

Umieściłem następujące elementy w kodzie programu, handlery do kolejek i puli pamięci:



Struktury do przechowywania odebranych ramek CAN:



Funkcja, w której tworzone są kolejki:



Funkcja, w której tworzone są banki pamięci:



Przerwanie, w którym odbierane są normalne ramki komunikacyjne:



Przerwanie, w którym odbierane są ramki typu watchdog:



Funkcja odbioru normalnych ramek komunikacyjnych, która jest umieszczona w zadaniu:



Funkcja odbioru ramek typu watchdog, która jest umieszczona w zadaniu:



W funkcji main() umieściłem tylko start CAN, aktywację przerwań dla pierwszego i drugiego FIFO, nagłówek ramki nadawczej, funkcje tworzące kolejki oraz banki pamięci:



Dalej skonfigurowałem następujące filtry dla CAN:



Na końcu umieściłem w osobnych zadaniach funkcje odbierające ramki CAN:



Po skonfigurowaniu Mikrokontrolera 2 zgodnie z powyższym opisem wykonałem taki sam test. Mikrokontroler 1 wysyła periodycznie ramki watchdog o ID 0x11, a po naciśnięciu przycisku wysyłane są ramki o ID 0x12. Tak samo jak w Przypadku 1 wprowadziłem do programu USBCAN filtr na ID 0x12.

Poniżej znajduje się terminal, który informuje o ramkach odebranych przez Mikrokontroler 2. Jak widać Mikrokontroler 2 odebrał wszystkie 20 ramek o ID 0x12.

Podsumowanie:

W tym artykule pokazałem, że problem utraty ramek CAN rzadko wynika z samej magistrali. Najczęściej jest to konsekwencja nieoptymalnego zarządzania pamięcią między ISR a zadaniami RTOS.

W systemach pracujących latami – jak automotive czy przemysł – deterministyczna alokacja pamięci i brak fragmentacji nie są opcją, lecz koniecznością.

Memory Pool + kolejki z przekazywaniem wskaźników to wzorzec projektowy, który pozwala:

  • utrzymać minimalny czas ISR,
  • uniknąć kopiowania danych,
  • zagwarantować brak fragmentacji,
  • skalować system bez utraty stabilności.

Źródło: DMBP

Autor: Mateusz Pluta
Absolwent Politechniki Warszawskiej, specjalizujący się w elektronice przemysłowej. Zainteresowany zagadnieniami z zakresu hardware’u, w szczególności szybkimi interfejsami, kompatybilnością elektromagnetyczną oraz przekształtnikami energoelektronicznymi. Na blogu DMBP dzieli się wiedzą techniczną wynikającą z pasji i doświadczenia.