UART+IRQ+LED

Vous savez récupérer des octets sur le port série et piloter l'afficheur de LED. L'objectif de cette partie est de pouvoir envoyer des trames par le port série depuis un PC à la carte de TP, et de les afficher en temps réel. Si on arrête d'envoyer des trames, la dernière en date doit rester affichée. Et on doit pouvoir recommencer à envoyer des trames sans avoir à redémarrer le programme sur la carte.

Le port série

❎ Les échanges sur le port série seront faits à la vitesse de 38400 bauds, 8 bits, pas de parité, 1 bit de stop (38400 8N1). Pour cela, modifiez le protoytpe de la fonction uart_init de la façon suivante : void uart_init(int baudrate).

À chaque fois que la carte est débranchée et rebranchée, le port série du PC doit être reconfiguré en 38400 8N1 en exécutant sh ./stty.sh (programme fourni ici).

Pour envoyer le contenu d'un fichier bidule.bin sur le port série, on utilisera la commande suivante : cat bidule.bin > /dev/ttyACM0.

Format des trames envoyées par le PC

Les fichiers qu'on affichera contiennent une ou plusieurs trames au format suivant :

  • Chaque début de trame est indiqué par l'octet 0xff.
  • Chaque octet différent de 0xff fait partie d'un pixel.
  • Les pixels sont stockés dans l'ordre naturel : d'abord ceux de la première ligne en commençant par la gauche, puis ceux de la deuxième ligne, etc.
  • Chaque pixel est composé de 3 octets, d'abord R, puis G puis B (chacun compris entre 0x00 et 0xfe).

Ainsi, une trame qui allume le pixel en haut à gauche en bleu sera composée des octets suivants : 0xff 0x00 0x00 0xfe 0x00 ... 0x00.

Le fichier de test one_frame.bin contient une seule trame et permet d'afficher l'image suivante (toutes les LED éteintes, sauf les 3 en haut à droite en RVB) :

Vous pouvez voir le contenu du fichier en question par la commande od -tx1 -v one_frame.bin.

Architecture du code

Vous devez pouvoir afficher une image en même temps que vous recevez des pixels. Il va donc falloir faire en sorte que deux tâches s'exécutent en parallèle :

  1. une qui reçoit les trames et les stocke à un endroit approprié en mémoire,
  2. l'autre qui rafraichit en permanence l'écran.

Vous allez donc faire en sorte que le port série fonctionne sous interruption, de façon à ce qu'une IRQ soit générée dès qu'un octet est reçu. Votre handler d'IRQ traitera cet octet et mettra à jour au fur et à mesure la trame qui est affichée. On n'attendra pas qu'une trame soit complètement reçue pour mettre à jour l'affichage, ni que les trois composantes d'un pixel soient reçues entièrement.

Remarque : la trame affichée est un objet partagé entre la tâche de réception et celle d'affichage. Des problèmes de concurrence peuvent donc se poser, si on envoie une ligne à l'afficheur alors qu'on n'a reçu qu'une ou deux des trois composantes d'un pixel. On ne s'en préoccupera pas ici, l'œil n'étant pas assez rapide pour le voir. Ce problème sera abordé en SE202 et SE302.

Travail

❎ Définissez un objet global qui contiendra la trame affichée. Il sera modifé par le handler d'IRQ du port série, et lu par la tâche d'affichage.

❎ Écrivez la tâche de réception du port série (handler d'IRQ) qui traitera les octets reçus.

❎ Écrivez la tâche d'affichage qui affiche en permanence la trame courante (normalement vous l'avez déjà écrite dans le TP…).

Pour tester votre code, vous avez trois fichiers à votre disposition. Par ordre de difficulté croissante :

  • one_frame.bin qui contient une seule trame,
  • many_frames.bin qui contient plusieurs trames pour avoir un affichage animé,
  • final.bin qui contient lui aussi plusieurs trames et vous permettra de tester la robustesse de votre code face à des trames potentiellement mal formées. Ce fichier vous affichera aussi un message vous donnant l'URL de la suite de cette partie.

❎ N'oubliez pas le tag UART_IRQ_1 !

Gestion des erreurs de transmission

❎ Faites en sorte que si votre UART reçoit une erreur de transmission (FE ou OR), alors la trame courante est ignorée et qu'on se remette en attente de la prochaine trame.

❎ N'oubliez pas le tag UART_IRQ_2 !