Wstęp
Jedną z podstawowych rzeczy, które chcemy robić w CANoe z poziomu kodu CAPL jest nadawanie ramek. W odniesieniu do magistrali CAN i podobnych, producent oprogramowania wyróżnił dwa podejścia do tematu, można wręcz powiedzieć – dwie filozofie:
Same nazwy nie są na tyle intuicyjne, by wywnioskować z nich o co chodzi – wszak sygnał to element składowy ramki… o co więc chodzi?
Główną różnicą jest odpowiedź na pytanie gdzie i jak przechowywane są bufory danych.
Wciąż niejasne? Nic nie szkodzi. Zaraz omówię to w praktyce.
Message-oriented CAPL
Czyli nadawanie oparte na wiadomościach.
Wewnątrz każdego modułu CAPLowego (czyli Network Node) tworzymy obiekty, którymi możemy manipulować a następnie wysłać je na magistralę.
Przykład
przykładowy kod, który po wciśnięciu przycisku “s” na klawiaturze spowoduje wysłanie na magistralę ramki:
on key 's' {
message LeverState my_mesage_buffer; // utowrzenie obiektu
my_message_buffer.position = 3; //przypisanie wartości
output(my_message_buffer); //wysłanie wiadomości na magistralę
}
Wewnątrz zdarzenia “wciśnięcie przycisku [s]” robimy następujące rzeczy:
- Tworzymy obiekt typu message (ramka). Zakładam, że wcześniej zdefiniowaliśmy sobie bazę danych .DBC i zaciągamy stamtąd definicję ramki typu LeverState wraz z jej strukturą. Nazwa “my_message_buffer” jest dowolnie wybraną nazwą utworzonego obiektu, jak nazwa zmiennej.
- Przypisuję wartość do elementu składowego ramki, tzn. do sygnału o nazwie “position”. Zakładam tutaj ponownie, że sygnał o takiej nazwie znalazł się w definicji, które zaciągamy z pliku DBC. Tak więc nowo utworzony obiekt będzie zawierał wszystkie elementy składowe, które zawiera definicja ramki typu LeverState
- Nadaję ręcznie wiadomość na magistralę dosłownie “wyrzucając” cały obiekt my_mesage_buffer na magistralę przy pomocy polecenia output()
Wizualizacja
Możemy sobie te kroki zwizualizować za pomocą poniższych grafik (nazwy mogą się nieznacznie różnić):
Można więc zauważyć, że operujemy tutaj na buforze: Najpierw go tworzymy, potem możemy dowolnie modyfikować, a następnie w dowolnie wybranym przez siebie miejscu wysłać jego zawartość na magistralę CAN.
Równie dobrze mógłbym sobie stworzyć kilka buforów odnoszących się do takiego samego rodzaju ramki (podobnie jak mogę stworzyć sobie kilka zmiennych typu int) i przechowywać w nich różne wartości:
Ważna uwaga:
Ma to swoje plusy i minusy.
Plusy
Jeśli chodzi o plusy, jest to oczywiście pełna kontrola nad wysyłaniem ramki. Jeśli zauważymy, że przygotowana przez nas symulacja nie działa poprawnie i ramki są nadawane na magistralę w sposób inny niż zamierzony, wiemy dokładnie gdzie szukać przyczyny błędu – odnajdujemy fragmenty w kodzie CAPL, które zmieniają wartość naszych buforów lub je wysyłają.
Minusy
Jeśli chodzi o minusy, dostęp do tych obiektów możliwy jest tylko z poziomu danego pliku CAPL.
Komunikacja z panelem
Jeśli więc chcielibyśmy sterować wartościami nadawanymi w ramce z poziomu panelu, musielibyśmy zrobić następującą konstrukcję:
Wartości z panelu do pliku CAPL moglibyśmy przekazywać za pomocą zmiennych systemowych (SysVars). Tzn. kontrolki na panelu związać do konkretnych zmiennych, zaś z poziomu CAPLa odczytywać wartość tych zmiennych. Jest to nieoczywiste, ale takie właśnie podejście należy przyjąć, jeśli wybraliśmy filozofię “message-oriented CAPL”.
Nadawanie cykliczne
Jak już wspomniałem wcześniej, jeśli chcemy wysłać wiadomość, za każdym razem trzeba skorzystać z funkcji output(). Mówiąc za każdym razem, mam na prawdę na myśli “za każdym”
Jeśli chcemy np. nadawać ramkę cykliczną co 100ms, musimy stworzyć timer dla tej ramki, który z każdym przejściem będzie wykonywał funkcję output(). Najprostsza możliwa implementacja takiego kodu wygląda następująco:
variables
{
message 0x100 message_0x100_buffer; //utworzenie obiektu
msTimer message_0x100_timer; //utworzenie timera
}
on start {
setTimerCyclic(message_0x100_timer, 100); //uruchomienia timera cyklicznie na starcie
}
on timer message_0x100_timer {
output(message_0x100_buffer); //wyslanie ramki
}
Signal-oriented CAPL
Tutaj sytuacja jest zgoła inna.
Dla każdej ramki, którą chcemy nadawać lub/i odbierać zdefiniowany jest dokładnie jeden globalny bufor, do którego dostęp mają wszystkie moduły CANoe. Taka warstwa zawierająca w sobie wszystkie bufory nazywana jest Interactive Layer – w skrócie IL.
Ten skrót jest o tyle ważny, że przewija się on w różnych miejscach w CANoe i przykładowo z poziomu CAPLa mamy dostęp do wielu funkcji odnoszących się właśnie do Interactive Layer:
Wizualizacja
Podejście oparte na Interactive Layer możemy zobrazować następująco:
Jak widać, bezpośrednio z warstwą IL komunikować może się każdy moduł, każde “okienko” w CANoe: Network Node’y, panele, okienka do analizy itd.
Wewnątrz IL przechowywane są bufory dla każdego rodzaju wiadomości:
Zapis jest tutaj dwukierunkowy:
Za każdym razem, gdy na magistralę przychodzi ramka, jej zawartość kopiowana jest do bufora. W ten sposób bufor zawsze przechowuje tylko ostatnią wpisaną do niego wartość.
Jeśli zaś chcemy ramkę nadawać, jedynie wpisujemy wartość konkretnego sygnału lub konkretnych bajtów do bufora. Robimy to przy użyciu znaku dolara – odnosimy się do konkretnego sygnału w IL:
Jednak ważna uwaga:
Nie mamy jednak kontroli nad tym kiedy IL ją nada na magistralę.
Dzieje się tak, ponieważ IL sam nadaje ramki na magistralę zgodnie ze swoimi określonymi czasami nadawania. Wszystko co my możemy zrobić to po prostu wpisać wartość do bufora i czekać aż zostanie nadany na magistralę.
Równie dobrze możemy zmienić wartość bufora np. z okienka Data:
Włączenie i konfiguracja Interactive Layer
Aby włączyć Interactive Layer musimy najpierw zaznaczyć odpowiednią opcję w ustawieniach głównych programu CANoe:
Przy podejściu Signal-Oriented CAPL należy przypisać nasze Network Nodes do konkretnych węzłów zdefiniowanych w debeceku:
Następnie możemy albo edytować ramki dla konkrentego węzła:
… albo zbiorczo dla całej magistali:
Pokaże nam się takie okno:
Plusy
Plusem takiego podejście jest niewątpliwie prostota tworzenia projektu:
- Nie musimy ustawiać timerów do nadawania cyklicznego ramek
- Nie musimy bawić się w zmienne systemowe do komunikacji z panelami
- Nie musimy tworzyć obiektów typu message – po prostu wpisujemy wartość do $sygnału
- Możemy wygodnie modyfikować wartości buforów z każdego miejsca CANoe
Minusy
- Brak ścisłej kontroli nad wysyłaniem ramek
- Możliwe rejsy (race-conditions) jeśli dopuszczamy zapis do zmiennej bez ograniczeń z wielu miejsc
- … a co za tym idzie trudny debug, jeśli próbujemy dojść do tego kto i czemu wpisał coś dziwnego do ramki
Co robić, jak żyć?
i jakie podejście przyjąć?
Oto moja rada:
- Jeśli masz do stworzenia prosty projekt, w którym będziesz mieć panele do sterowania urządzeniem z poziomu CANoe
- Czas ustawienia się sygnału nie jest krytyczny (nie boli jeśli będzie to 120ms zamiast 20ms)
- Nie spodziewasz się Race-Conditions, a jeśli się pojawią to nie będzie to duży problem
… wybierz podejście Signal-Oriented
a jeśli:
- Tworzysz projekt do celu zaawansowanych testów urządzenia,
- Czasówki mają duże znaczenie,
- Będzie robiony debug
- Będziesz robił dokładne sekwencje ramka po ramce, np. trzy kolejne ramki, w których kolejno zmienia się wartość sygnału
… wybierz Message-Oriented CAPL.
A jeśli pracujesz z CANoe, napisz mi w komentarzu z jakiego podejścia korzystasz i dlaczego 🙂
Dodaj komentarz