Le Raspberry Pi peut-il frapper de manière fiable une série de 9600 bauds et existe-t-il un exemple de code?

29

Je me demande dans quelle mesure il est possible d'utiliser le bitbanging pour piloter une série à 9600 bauds via les broches GPIO du Raspberry Pi.

De toute évidence, Linux n'est pas une très bonne plate-forme pour le bitbang, car il existe un grand nombre de pilotes et d'autres interruptions qui peuvent bloquer le processeur pendant de longues périodes (1 à 10 ms). Cependant, la situation s'est beaucoup améliorée récemment et une certaine préemption est désormais régulièrement activée dans les noyaux. Je soupçonne également qu'un noyau corrigé en temps réel peut facilement être utilisé sur le Raspberry Pi, et le matériel et les pilotes connectés peuvent être sélectionnés avec soin.

Ma norme de fiabilité est qu'elle devrait rester dans la plupart des tolérances série de 9600 bauds. Je ne sais pas actuellement combien d'erreurs sont tolérables dans la pratique, mais il y a des retransmissions et des accusés de réception dans le protocole, il est donc au moins quelque peu tolérant.

Donc, mes questions sont:

  • Le logiciel Userland peut-il une vitesse de bit bang 9600 baud fiable?
  • Cela nécessite-t-il un noyau corrigé en temps réel?
  • À combien d'erreurs dois-je m'attendre en général?

De plus, y a-t-il un exemple de code faisant du serial via GPIO bit banging? Je connais Arduino SoftSerial, mais c'est tout.

Juste au cas où, pour limiter la question: ce n'est pas une interface série standard et l'UART de Raspberry Pi est déjà utilisé pour une autre liaison série. Je ne souhaite pas de réponses qui suggèrent du matériel externe (comme Gertboard ou Arduino) ni des réponses qui dépendent de l'UART.

Nakedible
la source
1
+1 Bonne question. Je serais également intéressé par la réponse à cette question. Cependant, la question de reliabilitypourrait dépendre de l'action et des attentes.
Jivings
2
RS-232 a besoin de tensions négatives, mais cette liaison série n'utilise pas de tensions RS-232. Je ne sais pas, mais je crois que RPi nécessite le convertisseur TTL vers RS-232 standard, comme le MAX232 ou l'une des dizaines de choix. Cependant, juste pour être clair, ce n'est pas pertinent pour ma question, je voulais juste répondre.
Nakedible
4
Il existe également un deuxième UART sur les broches GPIO 14 et 15. Ce n'est pas aussi complet que le principal
John La Rooy
1
@Nakedible pourriez-vous s'il vous plaît partager votre travail? J'ai un problème similaire et j'aimerais voir comment vous avez fait une deuxième série via spi. merci MSP

Réponses:

15

J'ai finalement résolu cela, mais d'une manière assez peu orthodoxe. J'ai abandonné le bit banging comme trop peu fiable et j'ai essayé de trouver d'autres solutions qui me permettraient de faire la même chose sans ajouter plus de matériel. J'envisageais d'écrire un pilote de noyau, ce qui déclencherait une interruption sur GPIO, puis reconfigurerait la broche en SPI et utiliser SPI pour lire un octet de données entier - mais j'ai eu une meilleure idée.

J'utilise SPI pour échantillonner les lignes à 20 fois la vitesse de transmission. J'ignore entièrement les broches SCLK et SS, connecte la ligne RX à MISO et la ligne TX à MOSI. Cela me donne une vue semblable à un oscilloscope (1 bit) dans la ligne RX et clairement voir les bits transmis dans la ligne série:

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 

À partir de cela, il suffit de coder pour déterminer les positions correctes à partir desquelles échantillonner les bits de données réels. Le côté d'envoi est tout aussi trivial, j'ai juste besoin de convertir chaque octet en un long flux de bits avec le bit de début et le bit d'arrêt inclus.

La raison pour laquelle cela fonctionne mieux que le bit banging est que SPI a sa propre horloge qui ne se fige pas avec le noyau et que les lignes d'envoi et de réception SPI ont un FIFO de 16 octets pour le transfert qui est également indépendant des blocages du noyau. Pour 9600 bauds, j'utilise une horloge SPI à 250 kHz et cela signifie que je peux dormir même une milliseconde entre le remplissage et la vidange des FIFO sans aucune erreur de transmission. Cependant, pour me tromper, j'utilise des périodes de sommeil de 300 µs. J'ai brièvement testé jusqu'où je pouvais pousser cela et au moins une horloge SPI à 2 MHz était toujours utilisable, donc cette solution s'adapte également à des taux de transmission plus élevés.

La seule partie laide de cette solution est que le pilote SPI du noyau ne prend pas en charge un tel transfert de bits en continu. Cela signifie que je ne peux pas faire cela en écrivant mon propre module de noyau en utilisant le pilote SPI du noyau, et je ne peux pas non plus le faire en utilisant /dev/sdidev0.0 depuis le pays utilisateur. Cependant, sur le Raspberry Pi, le SPI et les autres périphériques sont accessibles directement depuis l'espace utilisateur par mmap (): n / dev / mem, contournant entièrement le contrôle du noyau. Je ne suis pas terriblement satisfait de cela, mais cela fonctionne parfaitement et cela offre l'avantage supplémentaire que les erreurs de segmentation dans l'espace utilisateur ne peuvent pas planter le noyau (à moins de jouer avec les autres périphériques par accident). En ce qui concerne l'utilisation du processeur, une veille de 300 µs semble me donner environ 7% d'utilisation du processeur en permanence, mais mon code n'est pas optimal. L'augmentation de la durée de veille réduit évidemment l'utilisation du processeur directement.

Edit: J'ai oublié de mentionner, j'ai utilisé la belle bibliothèque bcm2835 pour contrôler le SPI depuis l'espace utilisateur, en l'étendant si nécessaire.

Donc, pour résumer: je peux transmettre et recevoir de manière fiable sur une liaison série à 9600 bauds entièrement à partir de l'espace utilisateur en utilisant directement la puce SPI via / dev / mem à 250 kHz sur le Raspberry Pi.

Nakedible
la source
@ Nakedible - Pouvez-vous élaborer un peu ou fournir des liens sur la partie mmap (). Je travaille sur la même chose.
Jay K
Utilisez-vous également le matériel SPI pour effectuer la transmission?
Cheetah
Oui, la transmission fonctionne également.
Nakedible
3
Pouvez-vous s'il vous plaît donner des instructions étape par étape sur la façon dont vous pouvez envoyer et recevoir du code, et partager les packages modifiés si vous utilisez quelque chose que vous avez corrigé vous-même ... TIA!
valentt
10

Il semblerait qu'au moins sans les correctifs en temps réel (CONFIG_PREEMPT_RT), le Raspberry Pi ne peut pas bang bang de manière fiable une série de 9600 bauds.

J'ai utilisé un testeur de latence simple qui a configuré toutes les choses côté Linux de manière optimale (sched_fifo, priorité 99, cpu_dma_latench 0us, mlockall). J'ai essayé de dormir pendant 100 µsec (environ 9600 bauds) et de vérifier les dépassements de latence sur un système silencieux pendant 2 minutes. Les résultats ont été:

Min: 12 µs Avg: 24 µs Max: 282 µs

Cela semblait être le résultat commun. Le maximum variait dans des mesures plus lentes entre 100 µsec et 300 µsec. J'ai également vérifié la distribution et il semble que la grande majorité soit de l'ordre de 24 µs. Il n'y en a que quelques-uns qui dépassent les 50 µs, mais il y en a presque toujours. Il y a aussi parfois des latences énormes, comme 4000 µsec, mais elles sont assez rares pour être ignorées, du moins pour l'instant.

Je suppose que les latences maximales devraient être inférieures à 50 µsec pour 9600 bauds afin de ne pas obtenir d'erreurs et toute latence supérieure à 100 µsec fait complètement manquer un bit en émission ou en réception.

Tout cela sans même toucher encore les broches GPIO. Étant donné que je ne pouvais pas obtenir un fonctionnement propre, même avec seulement 2 secondes, il semble sûr de dire que sans correctifs en temps réel, le Raspberry Pi ne peut pas mordre une liaison série à 9600 bauds sans générer d'erreurs pendant une période sérieuse.

Je testerai les correctifs en temps réel plus tard si j'en ai le temps.

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

Mise à jour: le noyau RPi se bloque au démarrage sans détecter la carte SD s'il est compilé avec l'ensemble de correctifs CONFIG_PREEMPT_RT. C'est peut-être une chose simple à corriger, mais en voyant les différences désordonnées dans la source RPi, je pense que je veux attendre jusqu'à ce que cela soit plus dans le noyau principal.

Donc, tester cela est trop difficile et je l'abandonne.

Nakedible
la source
0

Vous n'avez pas besoin de mordre. Vous pouvez configurer une interruption de terre utilisateur dans le rx gpio pour détecter la chute du bit de démarrage. puis définissez une interruption temporelle pour échantillonner au milieu des bits. Un module noyau faisant cela est faisable.

Lalo UY
la source
Mais ça cogne encore un peu. En effet, c'est la façon dont vous faites habituellement des coups.
Philippos