Małe podsumowanie.
Ostatnie dyskusje z tego wątku:
linuxcnc-na-usb-poszukiwane-chetne-osob ... ml#p843563
będę kontynuował tutaj, ponieważ dotyczą wersji z ethernetem.
Udało się zapanować nad determinizmem czasowym z wykorzystaniem ethernet, "uporządkowując" przerwania.
Pierwsza sprawa to isolcpus które należy zrobić zaraz po instalacji systemu z linuxcnc - o tym już na forum było tutaj:
isolcpus-t103711.html
więc nic więcej dodawać nie trzeba.
W tym przypadku odizolować trzeba 2 rdzenie - jeden dla procesu rt linuxcnc, drugi do obsługi przerwania od ethernetu.
Ja mam 8 rdzeni więc odizolowane mam rdzenie 6 i 7. Tutaj też warto wyłączyć całe rdzenie, jeśli jest to rdzeń HT, czyli jeśli mamy 4 rdzenie po 2 wątki, to trzeba odizolować całe rdzenie. Może nawet warto 2 rdzenie czyli 4 wątki, jeden rdzeń na proces rt, drugi na obsługę przerwania, i tak jeszcze 2 rdzenie zostanie wolne do pozostałych zadań.
Druga sprawa, to wyłączenie/zatrzymanie
irqbalance.
irqbalance to narzędzie, które rozdziela przerwania sprzętowe pomiędzy procesory w celu poprawy wydajności systemu. Domyślnie działa jako usługa w tle, ale można go uruchomić także tylko raz, dodając opcję --oneshot.
Ja chcę ręcznie przypisać przerwanie od ethernetu do konkretnego rdzenia, więc irqbalance wyłączam.
Robi się to poleceniem:
Następnie trzeba odnaleźć które przerwanie odpowiada karcie ethernet:
Od razu też widać jaką nazwę ma karta (tutaj eno1) oraz do którego rdzenia została przydzielona (CPU3).
Teraz idea jest taka, że albo trzeba odizolować rdzeń do którego została przydzielona, aby miał on czas zajmować się tylko tym przerwaniem, albo na sztywno przypisać do przerwanie (na tym komputerze 28) do rdzenia odizolowanego (CPU6).
CPU7 pozostawiamy wolny do użycia przez proces rt linuxcnc.
Przypisanie odbywa się za pomocą maski bitowej przekazywanej w postaci szesnastkowej.
Czyli rdzeń numer 6 będzie miał maskę 0x40.
Przypisanie rdzenia odbywa się komendą:
Kod: Zaznacz cały
sudo sh -c "echo 40 > /proc/irq/28/smp_affinity"
Działa to w aktualnej sesji systemu, po ponownym uruchomieniu trzeba zrobić to od nowa, dlatego napisałem skrypt, który można dodać do automatycznego uruchamiania przy starcie systemu, tak aby o tym nie myśleć:
Kod: Zaznacz cały
#!/bin/bash
#wyłącza narzędzie optymalizujące zarządzanie przerwaniami
sudo service irqbalance stop
echo "Stopping the irqbalance tools"
#Nazwa interfejsu ethernet
ethernet_name=$(ip link show | awk -F': ' '/^[0-9]+: (en|eth)/ {print $2}')
echo "Ethernet interface name: $ethernet_name"
#numer przerwania od ethernetu
ethernet_irq_number=$(cat /proc/interrupts | awk -v ethernet="$ethernet_name" '$0 ~ ethernet {split($1, irq, ":"); print irq[1]}')
echo "Interrupt number for ethernet: $ethernet_irq_number"
# Pobieranie liczby rdzeni procesora
num_cores=$(grep -c '^processor' /proc/cpuinfo)
echo "The number of available processor cores: $num_cores"
# Obliczanie maski na przedostatni rdzeń
if ((num_cores >= 2)); then
affinity_mask=$((1 << (num_cores - 2)))
else
affinity_mask=1
fi
echo "The hex mask value for the penultimate core: 0x" $(printf "%x" $affinity_mask)
#Przypisanie numeru rdzenia do przerwania od ethernetu
sudo sh -c "printf "%x" $affinity_mask > /proc/irq/$ethernet_irq_number/smp_affinity"
Oprócz tego przeglądnąłem jeszcze metodę obliczania opóźnienia pomiędzy odczytem wejść a wysterowaniem wyjść.
Na to opóźnienie składa się kilka rzeczy:
-czas buforowania wejść
-czas przesłania bufora wejść
-czas buforowanie wyjść
-czas przesłania bufora wyjść
-opóźnienie wewnętrzne linuxcnc (wynikające z okresu SERVO_PERIOD)
SERVO_PERIOD domyślnie ustawione jest na 1ms i prawdopodobnie nikt tego nie zmienia.
Wartość ta zaokrąglana jest do całkowitej wielokrotności okresu bazowego (BASE_PERIOD).
1 ms to wartość odpowiednia dla większości zastosowań ale...
No właśnie. Tu trzeba zrobić rachunek, czy się opłaca zmieniać i czy warto.
Jeśli mamy ruch synchroniczny, to w najgorszym wypadku przy okresie 1 ms rozpoczynamy ruch z 1 ms opóźnieniem.
Przy odczytu sygnałów (np limity) zatrzymanie nastąpi w najgorszym wypadku po 1 ms. (to akurat nie powinno mieć znaczenia). Ale nie chce mi się nad tym teraz zastanawiać, ten temat jeszcze przemyślę.
Ja u siebie USTAWIŁEM BASE_PERIOD 40us i SERVO_PERIOD 80us i działa prawidłowo.
Po co SERVO_PERIOD tak nisko? No bo w tym przypadku opóźnienie (odczyt wejścia do wysterowania wyjscia) sumuje się z czasem SERVO_PERIOD (w najgorszym wypadku).
Zrobiłem więc przegląd metody obliczania opóźnienia i miałem błąd (nie uwzględniałem do tej pory czasu transmisji wraz z całą otoczką). Zrobiłem zatem pomiar tego opóźnienia (zamiast obliczania) i możliwość wyświetlenia go w linuxcnc. Dodatkowo dorobiłem w linuxcnc wyświetlenie błędu zbyt dużego opóźnienia (gdyby takie zaszło) po przekroczeniu ustawionego w pliku konfiguracyjnym progu. W ten sposób po dobraniu parametrów jest pewność, że opóźnienie nie jest większe niż próg a więc jest zagwarantowana odpowiednia dokładność działania np. ruchów synchronicznych.
Aktualnie udało się uzyskać opóźnienie (całkowite) w przedziale 1.2-1.5 ms.
Da się to także łatwo sprawdzić, wykonując prosty test.
Jedno z wejść skonfigurowane jako wejście limitów, jedno z wyjść jako wyjście włączenia wzmacniaczy.
Obydwie linie podłączyć na 2 kanały oscyloskopu.
Zmieniając stan wejście wyzwalany zostaje pomiar oscyloskopem a czas pomiędzy zmianą stanu wejścia a zmianą stanu wyjścia to całkowity czas opóźnienia.
Jeśli taki sam test zostanie wykonany na fizycznym porcie LPT ale pozostawimy SERVO_PERIOD na 1ms to to opóźnienie zmierzone będzie się wahać pomiędzy 0 a 1ms.