Finalmente resolví esto, pero de una manera poco ortodoxa. Abandoné los golpes de bits como demasiado poco confiables e intenté encontrar otras soluciones que me permitieran hacer lo mismo sin agregar más hardware. Estaba considerando escribir un controlador de kernel, que desencadenaría una interrupción en GPIO y luego reconfiguraría el pin para que fuera SPI y usara SPI para leer un byte de datos completo, pero entonces tuve una mejor idea.
Yo uso SPI para muestrear las líneas a 20x la velocidad de transmisión. Ignoro los pines SCLK y SS por completo, conecto la línea RX a MISO y la línea TX a MOSI. Esto me da una vista tipo osciloscopio (1 bit) en la línea RX y veo claramente los bits que se transmiten en la línea serie:
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
A partir de esto, es una simple cuestión de codificación averiguar las posiciones correctas desde las cuales tomar muestras de los bits de datos reales. El lado de envío es igualmente trivial, solo necesito convertir cada byte en una larga secuencia de bits con el bit de inicio y el bit de parada incluidos.
La razón por la que esto funciona mejor que el bit-banging es que SPI tiene su propio reloj que no se congela con el núcleo y las líneas de envío y recepción de SPI tienen un FIFO de 16 bytes para la transferencia que también son independientes de las congelaciones del núcleo. Para 9600 baudios, estoy usando un reloj SPI de 250 kHz y eso significa que puedo dormir incluso un milisegundo entre el llenado y el drenaje de los FIFO sin ningún error de transmisión. Sin embargo, para errar en el lado seguro, estoy usando 300 µs duerme. Probé brevemente hasta qué punto podía empujar esto y, al menos, un reloj SPI de 2MHz todavía era utilizable, por lo que esta solución también se escala a velocidades de transmisión más altas.
La única parte fea de esta solución es que el controlador SPI del núcleo no admite dicha transferencia de bits de transmisión. Esto significa que no puedo hacer esto escribiendo mi propio módulo del kernel usando el controlador SPI del kernel, y tampoco puedo hacerlo usando /dev/sdidev0.0 desde la tierra del usuario. Sin embargo, en el Raspberry Pi, mmap (): n / dev / mem puede acceder directamente al SPI y otros periféricos directamente desde userland, sin pasar por completo el control del núcleo. No estoy muy contento con esto, pero funciona perfectamente y brinda el beneficio adicional de que las fallas de segmentación en el país de usuario no pueden bloquear el kernel (a menos que se meta accidentalmente con los otros periféricos). En cuanto al uso de la CPU, los durmientes de 300 µs me parecen proporcionar aproximadamente un 7% de uso constante de la CPU, pero mi código es muy poco óptimo. El aumento de la duración del sueño obviamente reduce el uso de la CPU directamente.
Editar: Olvidé mencionar que utilicé la bonita biblioteca bcm2835 para controlar el SPI desde el país de usuario, extendiéndolo donde fue necesario.
Entonces, para resumir: puedo transmitir y recibir de manera confiable en un enlace serial de 9600 baudios completamente desde el país de usuario usando directamente el chip SPI a través de / dev / mem a 250kHz en la Raspberry Pi.
reliability
podría depender de la acción y las expectativas.