¿Puede Raspberry Pi explotar de manera confiable una serie de 9600 baudios y hay un código de ejemplo?

29

Me pregunto qué tan factible es usar bitbanging para conducir una serie de 9600 baudios a través de los pines GPIO en la Raspberry Pi.

Obviamente, Linux no es una plataforma terriblemente buena para bitbanging, ya que hay una gran cantidad de controladores y otras interrupciones que pueden bloquear la CPU durante períodos prolongados de tiempo (1-10 ms). Sin embargo, la situación ha mejorado mucho recientemente, y ahora se habilita algo de preferencia regularmente en los núcleos. También sospecho que se puede usar fácilmente un kernel parcheado en tiempo real en la Raspberry Pi, y el hardware y los controladores conectados se pueden seleccionar con cuidado.

Mi estándar de fiabilidad es que debe permanecer dentro de las tolerancias en serie normales de 9600 baudios la mayor parte del tiempo. Actualmente no estoy seguro de cuántos errores son tolerables en la práctica, pero hay retransmisiones y reconocimientos en el protocolo, por lo que al menos es algo tolerante.

Entonces, mis preguntas son:

  • ¿Puede el software userland confiablemente bit-bang 9600 baudios de velocidad?
  • ¿Esto necesita un kernel parcheado en tiempo real?
  • ¿Cuántos errores debo esperar en general?

Además, ¿hay algún código de ejemplo en serie a través de golpes de bits GPIO? Sé de Arduino SoftSerial, pero eso es todo.

Por si acaso, para limitar la pregunta: esta no es una interfaz en serie estándar y el UART de Raspberry Pi ya se usa para otro enlace en serie. No deseo respuestas que sugieran hardware externo (como Gertboard o Arduino) ni respuestas que dependan del UART.

Desnudo
fuente
1
+1 Buena pregunta. También me interesaría la respuesta a esto. Sin embargo, la cuestión de reliabilitypodría depender de la acción y las expectativas.
Jivings
2
RS-232 necesita voltajes negativos, pero este enlace en serie no usa voltajes RS-232. No lo sé, pero creo que RPi requiere el convertidor TTL a RS-232 estándar, como MAX232 o cualquiera de las docenas de opciones. Sin embargo, para ser claros, esto no es relevante para mi pregunta, solo quería responder.
Desnudable
44
También hay un segundo UART en los pines GPIO 14 y 15. Sin embargo, no es tan completo como el principal
John La Rooy
1
@Nakedible, ¿podría compartir su trabajo? Tengo un problema similar y me gustaría ver cómo hiciste una segunda serie a través de spi. gracias MSP

Respuestas:

15

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.

Desnudo
fuente
@ Nakedible: ¿puede elaborar un poco o proporcionar enlaces en la parte mmap (). Estoy trabajando en lo mismo.
Jay K
¿Está utilizando el hardware SPI para hacer la transmisión también?
Cheetah
Sí, la transmisión también funciona.
Nakedible
3
¿Puede dar instrucciones paso a paso sobre cómo puede enviar y recibir código, y compartir los paquetes modificados si usa algo que parchó usted mismo ... TIA!
valentt
10

Parece que, al menos sin los parches en tiempo real (CONFIG_PREEMPT_RT), la Raspberry Pi no puede hacer un bit-bang confiable de una serie de 9600 baudios.

Utilicé un simple probador de latencia que configuró todas las cosas del lado de Linux de manera óptima (sched_fifo, prioridad 99, cpu_dma_latench 0us, mlockall). Intenté dormir durante 100 µsec (aproximadamente 9600 baudios) y verifiqué los excesos de latencia en un sistema silencioso durante 2 minutos. Los resultados fueron:

Mín .: 12 µseg Promedio: 24 µseg Máx .: 282 µseg

Este parecía el resultado común. El máximo varió en mediciones más lentas entre 100 µseg y 300 µseg. También verifiqué la distribución y parece que la gran mayoría está en el rango de 24 µseg. Solo unos pocos superan los 50 µseg, pero casi siempre hay algunos. A veces también hay latencias enormes, como 4000 µs, pero son lo suficientemente poco comunes como para ignorarlas, al menos por ahora.

Supongo que las latencias máximas deben estar por debajo de 50 µseg para que 9600 baudios no obtengan errores y cualquier latencia de más de 100 µseg hace que falte un bit en la transmisión o recepción.

Todo esto sin siquiera tocar los pines GPIO todavía. Dado que no pude obtener una ejecución limpia incluso con solo 2 segundos, parece seguro decir que sin parches en tiempo real, la Raspberry Pi no puede explotar un enlace serie de 9600 baudios sin generar errores durante un período de tiempo serio.

Probaré los parches en tiempo real más tarde si tengo tiempo.

(Herramienta utilizada: http://git.kernel.org/?p=linux/kernel/git/clrkwllms/rt-tests.git;a=summary )

Actualización: el kernel RPi se bloquea en el arranque sin detectar la tarjeta SD si se compila con el conjunto de parches CONFIG_PREEMPT_RT. Puede ser algo simple de solucionar, pero al ver las diferencias desordenadas en la fuente RPi, creo que quiero esperar hasta que haya más en el núcleo de la línea principal.

Entonces, probar esto es demasiado difícil, y lo estoy abandonando.

Desnudo
fuente
0

No necesitas golpear poco. Puede configurar una interrupción de tierra de usuario en el rx gpio para detectar la caída del bit de inicio. luego establezca una interrupción de tiempo para muestrear en el medio de los bits. Un módulo de kernel haciendo eso es factible.

Lalo UY
fuente
Pero eso sigue siendo un poco duro. De hecho, esa es la forma en que generalmente haces golpes.
Philippos