Alla fine l'ho risolto, ma in un modo abbastanza poco ortodosso. Ho abbandonato il bit-banging come troppo inaffidabile e ho cercato di trovare altre soluzioni che mi permettessero la stessa cosa senza aggiungere altro hardware. Stavo pensando di scrivere un driver del kernel, che avrebbe innescato un interrupt su GPIO e quindi riconfigurato il pin in SPI e avrei usato SPI per leggere un intero byte di dati, ma poi ho avuto un'idea migliore.
Uso SPI per campionare le linee a 20 volte la velocità di trasmissione. Ignoro del tutto i pin SCLK e SS, collego la linea RX a MISO e la linea TX a MOSI. Questo mi dà una vista simile ad un oscilloscopio (1 bit) nella linea RX e vedere chiaramente i bit che vengono trasmessi nella linea seriale:
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
Da questo, è una semplice questione di codifica capire le posizioni corrette da cui campionare i bit di dati effettivi. Il lato di invio è altrettanto banale, ho solo bisogno di convertire ogni byte in un lungo flusso di bit con il bit iniziale e il bit di stop inclusi.
La ragione per cui funziona meglio del bit-banging è che SPI ha il suo clock che non si blocca con il kernel e che le linee di invio e ricezione SPI hanno un FIFO a 16 byte per il trasferimento che sono anche indipendenti dai blocchi del kernel. Per 9600 baud, sto usando un clock SPI a 250 kHz e questo significa che posso dormire anche per un millisecondo tra il riempimento e lo svuotamento degli FIFO senza errori di trasmissione. Tuttavia, per sbagliare sul sicuro, sto usando 300 µs di sonno. Ho testato brevemente fino a che punto avrei potuto spingerlo e almeno un clock SPI da 2 MHz era ancora utilizzabile, quindi questa soluzione si adatta anche a baud rate più alti.
L'unica parte brutta di questa soluzione è che il driver SPI del kernel non supporta tale trasferimento di bit di streaming. Questo significa che non posso farlo scrivendo il mio modulo del kernel usando il driver SPI del kernel, e non posso nemmeno farlo usando /dev/sdidev0.0 dalla terra dell'utente. Tuttavia, su Raspberry Pi, SPI e altre periferiche sono accessibili direttamente da userland da mmap (): n / dev / mem, ignorando del tutto il controllo del kernel. Non sono terribilmente contento di questo, ma funziona perfettamente e offre l'ulteriore vantaggio che gli errori di segmentazione nell'area utente non possono causare l'arresto anomalo del kernel (a meno che non si verifichino casualmente con le altre periferiche). Per quanto riguarda l'uso della CPU, 300 µs di sleep sembrano darmi circa il 7% di utilizzo costante della CPU, ma il mio codice è molto non ottimale. L'aumento della durata del sonno ovviamente riduce direttamente l'utilizzo della CPU.
Modifica: ho dimenticato di menzionare, ho usato la bella libreria bcm2835 per controllare la SPI da userland, estendendola dove necessario.
Quindi, per riassumere: posso trasmettere e ricevere in modo affidabile su un collegamento seriale da 9600 baud interamente dall'area utente utilizzando direttamente il chip SPI tramite / dev / mem a 250kHz sul Raspberry Pi.
reliability
potrebbe dipendere dall'azione e dalle aspettative.