我终于解决了这个问题,但是以一种非常非常规的方式。由于过于可靠,我放弃了位冲击,并试图找到其他解决方案,这些解决方案使我能够在不增加硬件的情况下实现同一目标。我当时正在考虑编写一个内核驱动程序,该驱动程序将在GPIO上触发一个中断,然后将该引脚重新配置为SPI,并使用SPI读取整个数据字节-但是我有了一个更好的主意。
我使用SPI以20倍的波特率对线路进行采样。我完全忽略了SCLK和SS引脚,将RX线连接到MISO,将TX线连接到MOSI。这给了我一个类似于RX线的(1位)示波器视图,并清楚地看到了串行线中正在传输的位:
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
由此,只需进行编码即可找出从中采样实际数据位的正确位置。发送端也同样琐碎,我只需要将每个字节转换为包含起始位和停止位的长位流即可。
它比位敲打更好的原因是SPI有自己的时钟,不会与内核冻结,并且SPI发送和接收线具有16字节的FIFO用于传输,这也与内核冻结无关。对于9600波特,我使用的是250kHz的SPI时钟,这意味着我可以在填充和耗尽FIFO之间睡眠甚至一毫秒,而不会发生任何传输错误。但是,为了安全起见,我正在使用300 µs的睡眠时间。我简短地测试了我可以将其推进多远,并且至少仍可以使用2MHz的SPI时钟,因此该解决方案也可以扩展到更高的波特率。
该解决方案的一个丑陋部分是内核SPI驱动程序不支持这种流式传输位。这意味着我无法通过使用内核SPI驱动程序编写自己的内核模块来执行此操作,并且也无法通过在用户域中使用/dev/sdidev0.0来执行此操作。但是,在Raspberry Pi上,可以通过mmap():n / dev / mem直接从用户域访问SPI和其他外围设备,而完全绕开了内核控制。我对此并不满意,但是它可以完美地工作,它还带来了一个额外的好处,即用户域中的分段错误不会使内核崩溃(除非偶然与其他外设弄乱了)。至于CPU的使用,300 µs的睡眠似乎可以持续为我提供约7%的CPU使用率,但是我的代码不是很理想。延长睡眠时间显然会直接降低CPU使用率。
编辑:忘了提一下,我使用了不错的bcm2835库来从用户区控制SPI,并在需要时进行扩展。
因此,总而言之:通过在Raspberry Pi上以250kHz的频率/ kHz / dev / mem直接使用SPI芯片,我可以完全从用户域可靠地在9600波特串行链路上进行发送和接收。
reliability
可能取决于行动和期望。