Ich habe das endlich gelöst, aber auf eine ziemlich unorthodoxe Weise. Ich habe Bit-Banging als zu unzuverlässig aufgegeben und versucht, andere Lösungen zu finden, die es mir ermöglichen, dasselbe zu tun, ohne zusätzliche Hardware hinzuzufügen. Ich habe überlegt, einen Kerneltreiber zu schreiben, der einen Interrupt auf GPIO auslöst und dann den Pin neu als SPI konfiguriert und SPI zum Lesen eines gesamten Datenbytes verwendet - aber dann habe ich eine bessere Idee.
Ich benutze SPI, um die Leitungen mit 20x der Baudrate abzutasten. Ich ignoriere die SCLK- und SS-Pins vollständig, verbinde die RX-Leitung mit MISO und die TX-Leitung mit MOSI. Dies gibt mir einen (1-Bit) Oszilloskop-ähnlichen Einblick in die RX-Leitung und zeigt deutlich, welche Bits in der seriellen Leitung übertragen werden:
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
Daraus ist es eine einfache Sache der Codierung, die richtigen Positionen herauszufinden, von denen die tatsächlichen Datenbits abgetastet werden sollen. Die sendende Seite ist ebenso trivial, ich muss nur jedes Byte in einen langen Strom von Bits konvertieren, wobei das Start- und das Stoppbit enthalten sind.
Dies funktioniert besser als Bit-Banging, da SPI über einen eigenen Takt verfügt, der nicht mit dem Kernel einfriert, und die SPI-Sende- und Empfangsleitungen über ein 16-Byte-FIFO für die Übertragung verfügen, das auch unabhängig von Kernel-Einfrierungen ist. Für 9600 Baud verwende ich einen 250-kHz-SPI-Takt, was bedeutet, dass ich zwischen dem Füllen und Entleeren der FIFOs auch nur eine Millisekunde lang schlafen kann, ohne dass Übertragungsfehler auftreten. Um jedoch auf der sicheren Seite zu sein, benutze ich 300 µs Schlafzeit. Ich habe kurz getestet, wie weit ich dies schieben kann, und mindestens ein 2-MHz-SPI-Takt war noch verwendbar, sodass diese Lösung auch auf höhere Baudraten skaliert.
Der hässliche Teil dieser Lösung ist, dass der Kernel-SPI-Treiber eine solche Übertragung von Streaming-Bits nicht unterstützt. Dies bedeutet, dass ich dies nicht tun kann, indem ich mein eigenes Kernelmodul mit dem Kernel-SPI-Treiber schreibe, und ich kann es auch nicht tun, indem ich /dev/sdidev0.0 aus dem Benutzerland verwende. Auf dem Raspberry Pi können Sie jedoch über mmap (): n / dev / mem direkt auf das SPI und andere Peripheriegeräte zugreifen, wobei die Kernel-Steuerung vollständig umgangen wird. Ich bin nicht sonderlich zufrieden damit, aber es funktioniert einwandfrei und es hat den zusätzlichen Vorteil, dass Segmentierungsfehler im Userland den Kernel nicht zum Absturz bringen können (es sei denn, es kommt versehentlich zu Problemen mit den anderen Peripheriegeräten). Was die CPU-Auslastung anbelangt, scheinen mir 300 µs Ruhezustand ungefähr 7% der CPU-Auslastung konstant zu geben, aber mein Code ist sehr unoptimal. Eine längere Ruhezeit senkt offensichtlich die CPU-Auslastung.
Edit: Ich habe vergessen zu erwähnen, dass ich die nette bcm2835- Bibliothek verwendet habe, um die SPI vom Userland aus zu steuern und sie bei Bedarf zu erweitern.
Zusammenfassend lässt sich sagen, dass ich auf einer seriellen 9600-Baud-Verbindung zuverlässig und vollständig vom Userland aus senden und empfangen kann, indem ich den SPI-Chip direkt über / dev / mem bei 250 kHz auf dem Raspberry Pi verwende.
reliability
könnte jedoch von den Maßnahmen und Erwartungen abhängen.