Finalmente resolvi isso, mas de uma maneira pouco ortodoxa. Abandonei o bit-banging por não ser muito confiável e tentei encontrar outras soluções que me permitiriam a mesma coisa sem adicionar mais hardware. Eu estava pensando em escrever um driver de kernel, o que acionaria uma interrupção no GPIO e, em seguida, reconfiguraria o pino como SPI e o usaria para ler um byte de dados inteiro - mas tive uma idéia melhor.
Eu uso o SPI para provar as linhas em 20x a taxa de transmissão. Ignoro completamente os pinos SCLK e SS, conecto a linha RX ao MISO e a linha TX ao MOSI. Isso me dá uma visão do osciloscópio (1 bit) na linha RX e vê claramente os bits sendo transmitidos na linha serial:
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 disso, é uma simples questão de codificação descobrir as posições corretas a partir das quais é possível amostrar os bits de dados reais. O lado de envio é igualmente trivial, só preciso converter cada byte em um longo fluxo de bits com o bit inicial e o final incluído.
A razão pela qual isso funciona melhor do que o bit-bang é que o SPI tem seu próprio relógio que não congela com o kernel e as linhas de envio e recebimento do SPI têm um FIFO de 16 bytes para transferência, que também é independente do congelamento do kernel. Para 9600 bauds, estou usando um relógio SPI de 250kHz e isso significa que posso dormir por até um milissegundo entre encher e esgotar os FIFOs sem erros de transmissão. No entanto, para errar no lado seguro, estou usando 300 µs dorme. Testei brevemente até que ponto eu poderia levar isso a sério e, pelo menos, um relógio SPI de 2MHz ainda era utilizável; portanto, essa solução também se adapta a taxas de transmissão mais altas.
A parte mais feia dessa solução é que o driver SPI do kernel não suporta essa transferência de bits de streaming. Isso significa que não posso fazer isso escrevendo meu próprio módulo do kernel usando o driver SPI do kernel, e também não posso fazer isso usando /dev/sdidev0.0 da área do usuário. No entanto, no Raspberry Pi, o SPI e outros periféricos são acessíveis diretamente da terra do usuário por mmap (): n / dev / mem, ignorando completamente o controle do kernel. Não estou muito satisfeito com isso, mas funciona perfeitamente e oferece o benefício adicional de que falhas de segmentação na região do usuário não podem travar o kernel (a menos que mexam com os outros periféricos por acidente). Quanto ao uso da CPU, 300 µs dorme parecem me fornecer cerca de 7% de uso constante da CPU, mas meu código é muito ruim. Aumentar a duração do sono obviamente diminui o uso da CPU diretamente.
Edit: Esqueci de mencionar, usei a boa biblioteca bcm2835 para controlar o SPI da userland, estendendo-a quando necessário.
Resumindo: posso transmitir e receber de forma confiável um link serial de 9600 baud inteiramente da terra do usuário, usando diretamente o chip SPI via / dev / mem a 250kHz no Raspberry Pi.
reliability
depender da ação e das expectativas.