Czy Raspberry Pi może niezawodnie przekroczyć prędkość 9600 bodów i czy istnieje przykładowy kod?

29

Zastanawiam się, jak wykonalne jest użycie bitbangingu do sterowania 9600 bodami szeregowymi przez piny GPIO na Raspberry Pi.

Oczywiście Linux nie jest zbyt dobrą platformą do bitbangingu, ponieważ istnieje duża liczba sterowników i innych przerwań, które mogą blokować procesor przez dłuższy czas (1-10 ms). Jednak ostatnio sytuacja uległa znacznej poprawie i w jądrach regularnie włącza się pewne zapobieganie. Podejrzewam również, że łatane jądro w czasie rzeczywistym może być łatwo używane na Raspberry Pi, a podłączony sprzęt i sterowniki mogą być starannie wybrane.

Moim standardem w zakresie niezawodności jest to, że przez większość czasu powinien on pozostawać w granicach normalnej tolerancji szeregowej 9600 bodów. Obecnie nie jestem pewien, ile błędów można tolerować w praktyce, ale w protokole znajdują się retransmisje i potwierdzenia, więc jest on co najmniej nieco tolerancyjny.

Tak więc moje pytania to:

  • Czy oprogramowanie użytkowe może niezawodnie bit-bang 9600 bodów?
  • Czy to wymaga łatanego jądra w czasie rzeczywistym?
  • Ile błędów ogólnie należy się spodziewać?

Ponadto, czy jest jakiś przykładowy kod wykonujący szeregowanie za pomocą bitowego GPIO? Znam Arduino SoftSerial, ale o to chodzi.

Na wszelki wypadek, aby ograniczyć pytanie: nie jest to standardowy interfejs szeregowy, a UART Raspberry Pi jest już używany do innego łącza szeregowego. Nie życzę sobie odpowiedzi sugerujących zewnętrzny sprzęt (np. Gertboard lub Arduino) ani odpowiedzi zależnych od UART.

Nagi
źródło
1
+1 Dobre pytanie. Byłbym również zainteresowany odpowiedzią na to pytanie. Jednak pytanie reliabilitymoże zależeć od działania i oczekiwań.
Jivings
2
RS-232 potrzebuje napięć ujemnych, ale to łącze szeregowe nie wykorzystuje napięć RS-232. Nie wiem, ale uważam, że RPi wymaga standardowego konwertera TTL na RS-232, takiego jak MAX232 lub którykolwiek z wielu innych opcji. Jednak dla jasności, nie dotyczy to mojego pytania, chciałem tylko odpowiedzieć.
Nakedible
4
Istnieje również drugi UART na pinach 14 i 15 GPIO. Nie jest tak w pełni funkcjonalny jak główny
John La Rooy,
1
@Nakedible czy mógłbyś podzielić się swoją pracą? Mam podobny problem i chciałbym zobaczyć, jak zrobiłeś drugi serial przez SPI. dzięki MSP

Odpowiedzi:

15

W końcu to rozwiązałem, ale w dość niekonwencjonalny sposób. Porzuciłem bit-biting jako zbyt zawodny i próbowałem znaleźć inne rozwiązania, które pozwoliłyby mi na to samo bez dodawania dodatkowego sprzętu. Zastanawiałem się nad napisaniem sterownika jądra, który wyzwoli przerwanie w GPIO, a następnie ponownie skonfiguruję pin, aby był SPI i użył SPI do odczytania całego bajtu danych - ale wtedy wpadłem na lepszy pomysł.

Używam SPI do próbkowania linii z prędkością 20x szybkość transmisji. Całkowicie ignoruję piny SCLK i SS, podłączam linię RX do MISO, a linię TX do MOSI. To daje mi (1-bitowy) oscyloskopowy widok na linię RX i wyraźnie widzę bity przesyłane w linii szeregowej:

00 00 00 00 00 00 
00 00 00 00 01 FF 
FF FF FF FF 00 00 
01 FF FF FF FF FF 
FF FF E0 00 00 00 
00 00 07 FF FF FF 
FF FF 

Na tej podstawie proste jest zakodowanie, aby ustalić właściwe pozycje, z których należy próbkować rzeczywiste bity danych. Strona wysyłająca jest równie trywialna, wystarczy przekonwertować każdy bajt na długi strumień bitów z bitem początkowym i bitem stop.

Powodem, dla którego działa to lepiej niż bit-bit, jest to, że SPI ma swój własny zegar, który nie zamraża się z jądrem, a linie wysyłania i odbierania SPI mają 16-bajtowe FIFO do przesyłania, które są również niezależne od zawieszenia jądra. Dla prędkości 9600 bodów używam zegara SPI 250 kHz, co oznacza, że ​​mogę spać nawet milisekundę między napełnianiem i opróżnianiem FIFO bez żadnych błędów transmisji. Jednak, aby się pomylić po bezpiecznej stronie, używam snu 300 µs. Krótko przetestowałem, jak daleko mogę to przesunąć i przynajmniej zegar SPI 2 MHz był nadal użyteczny, więc to rozwiązanie można skalować również do wyższych prędkości transmisji.

Jedną brzydką częścią tego rozwiązania jest to, że sterownik SPI jądra nie obsługuje takiego przesyłania strumieniowego bitów. Oznacza to, że nie mogę tego zrobić, pisząc własny moduł jądra przy użyciu sterownika SPI jądra, a także nie mogę tego zrobić przy użyciu /dev/sdidev0.0 z poziomu użytkownika. Jednak na Raspberry Pi, SPI i inne urządzenia peryferyjne są dostępne bezpośrednio z przestrzeni użytkownika za pomocą mmap (): n / dev / mem, całkowicie pomijając kontrolę jądra. Nie jestem z tego powodu bardzo zadowolony, ale działa idealnie i daje dodatkową korzyść, że błędy segmentacji w przestrzeni użytkownika nie mogą spowodować awarii jądra (chyba że przypadkowo zepsują inne urządzenia peryferyjne). Jeśli chodzi o użycie procesora, 300 uśpienia wydaje się dawać mi około 7% ciągłego użycia procesora, ale mój kod jest bardzo nieoptymalny. Wydłużenie czasu uśpienia oczywiście obniża bezpośrednio użycie procesora.

Edycja: Zapomniałem wspomnieć, użyłem ładnej biblioteki bcm2835 do sterowania SPI z przestrzeni użytkownika, rozszerzając go w razie potrzeby.

Podsumowując: Mogę niezawodnie transmitować i odbierać łącze szeregowe 9600 bodów całkowicie z obszaru użytkownika, bezpośrednio przy użyciu układu SPI przez / dev / mem przy 250 kHz na Raspberry Pi.

Nagi
źródło
@ Nakedible - Czy możesz trochę opracować lub podać linki w części mmap (). Pracuję nad tym samym.
Jay K
Czy używasz również sprzętu SPI do przesyłania?
Gepard,
Tak, transmisja również działa.
Nakedible
3
Czy możesz podać instrukcje krok po kroku, jak wysyłać i odbierać kod oraz udostępniać zmodyfikowane pakiety, jeśli używasz czegoś, co sam załatałeś ... TIA!
valentt
10

Wydaje się, że przynajmniej bez poprawek w czasie rzeczywistym (CONFIG_PREEMPT_RT), Raspberry Pi nie może niezawodnie uderzyć w bit 9600 bodów.

Użyłem prostego testera opóźnień, który optymalnie skonfigurował wszystkie rzeczy związane z linuksem (harmonogram_fifo, priorytet 99, cpu_dma_latench 0us, mlockall). Próbowałem spać przez 100 µs (około 9600 bodów) i sprawdzałem przekroczenie opóźnienia w cichym systemie przez 2 minuty. Wyniki były następujące:

Min .: 12 µs Średnio: 24 µs Max: 282 µs

To wydawało się wspólnym rezultatem. Wartość maksymalna zmieniała się w wolniejszych pomiarach od 100 µs do 300 µs. Sprawdziłem również rozkład i wydaje się, że zdecydowana większość jest w zakresie 24 µs. Istnieje tylko kilka, które przekraczają 50 µs, ale prawie zawsze są pewne. Czasami występują również ogromne opóźnienia, takie jak 4000 µs, ale są one na tyle rzadkie, że można je przynajmniej zignorować.

Wydaje mi się, że maksymalne opóźnienia powinny wynosić poniżej 50 µs dla 9600 bodów, aby nie dostać błędów, a jakiekolwiek opóźnienie powyżej 100 µs powoduje całkowite braki w transmisji lub odbiorze.

To wszystko bez dotykania jeszcze pinów GPIO. Ponieważ nie mogłem uzyskać czystego przebiegu nawet w ciągu zaledwie 2 sekund, wydaje się bezpiecznie powiedzieć, że bez poprawek w czasie rzeczywistym Raspberry Pi nie może uderzyć w łącze szeregowe 9600 bodów bez generowania błędów przez dłuższy czas.

Później będę testować łatki w czasie rzeczywistym, jeśli będę mieć czas.

(Użyte narzędzie: http://git.kernel.org/?p=linux/kernel/git/clrkwllms/rt-tests.git;a=summary )

Aktualizacja: Jądro RPi zawiesza się przy uruchamianiu bez wykrycia karty SD, jeśli jest skompilowane z zestawem poprawek CONFIG_PREEMPT_RT. Może to być prosta rzecz do naprawienia, ale widząc niechlujne różnice w źródle RPi, myślę, że chcę poczekać, aż więcej będzie w głównym jądrze.

Testowanie tego jest więc zbyt trudne i porzucam to.

Nagi
źródło
0

Nie musisz gryźć. Możesz skonfigurować przerwanie lądowania użytkownika w rx gpio, aby wykryć upadek bitu startowego. następnie ustaw przerwanie czasowe, aby próbkować w środku bitów. Jest to możliwe dzięki modułowi jądra.

Lalo UY
źródło
Ale to wciąż trochę wali. Rzeczywiście, w ten sposób zwykle robisz bity.
Philippos