dimanche 16 mars 2014

PICvision, module TVout

Le module TVout est celui qui génère le signal vidéo composite. Contrairement au module TVout du projet HackVision il est entièrement écris en 'C'. Ceci est rendu possible grâce à l'utilisation judicieuse d'un périphérique SPI pour sérialiser les bits vidéo. De plus comme le périphérique SPI du PIC24FJ64GA002 a une mémoire tampon de 8 octets le temps passé dans l'interruption qui génère les pixels vidéo est d'autant raccourci. En effet une fois que les 8 derniers octets de la ligne vidéo sont dans la mémoire tampon SPI on peut sortir de l'interruption et laisser le périphérique faire le travail. Les concepteurs de HackVision auraient put utiliser le périphérique SPI du AtMega328 pour la même utilisation mais il ne l'on pas fait. Cependant la mémoire tampon SPI du AtMega328 n'est que de un octet. Néanmoins en utilisant le SPI comme je l'ai fait ils auraient pu écrire leur TVout entièrement 'C' et réduire le nombre de cycles machine utilisés pour le vidéo.

Comme le code source est disponible sur le dépôt Github je ne vais reproduire ici que la partie du code étudié, soit les 2 interruptions. La première est celle qui contrôle le signal de synchronisation. La deuxième est celle qui envoie le contenu de video_buffer au périphérique SPI. Il est important que ces 2 interruptions soient le plus brèves possible étant données qu'elles se répètent plus de 15,000 fois par secondes, moins on y passe de temps plus il en reste pour le programme principal où est exécuté la logique du jeux.

Interruption _VSYNC_ISR

_VSYNC_ISR est la routine de service d'interruption qui contrôle le signal de synchronisation vidéo. Dans ce but un périphérique Output compare est utilisé en mode PWM. Il s'agit simplement de générer un signal à 15735 hertz pour le NTSC et 15625 hertz pour le PAL. Ce signal est de niveau 1 sauf pour l'impulsion de synchronisation horizontale qui elle est à zéro. Le signal sur la broche 16 du MCU a donc la forme suivante:

Cependant au début de chaque cadre (frame) le signal est inversé sur les 3 premières lignes vidéo pour assuré la synchronisation verticale:
la variable frame_line_cntr sert a compter les lignes et à partir du numéro de ligne on décide quand il faut activer et désactiver l'interruption qui envoie les pixels vidéo et quand il faut générer la synchronisation verticale. En mode progressif il y a 262 ligne en NTSC et 312 en PAL par cadre. Il faut donc faire des case différent pour le NTSC et le PAL.

Interruption _VIDEO_OUT_ISR

Cette routine de service d'interruption est celle qui prend le contenu du video_buffer et via le périphérique SPI sérialise les octets vers la sortie sur la broche 14 du MCU. Il s'agit d'une interruption de type Change Notification, c'est à dire qui est déclenchée chaque fois que le niveau change sur la broche 18. Entre la fin de l'impulsion de synchronisation horizontale et le début de l'envoie des pixels vidéo il y a un délais. On pourrait attendre à l'intérieur de l'interruption _VSYNC_ISR que ce délais soit écoulé et envoyer les pixels à partir de cette interruption. Ce serait du gaspillage de cycles machine. On utilise donc un deuxième périphérique Output compare qui lui aussi génère un signal semblable au premier sauf que l'impulsion de synchronisation est plus large. Elle débute 1µsec avant l'impulsion horizontal sync mais est 2 fois plus longue.

Cette impulsion est envoyée sur l'entrée digitale broche 18 du MCU et chaque fois qu'il y a un changement d'état l'interruption _VIDEO_OUT_ISR est déclenchée. Mais la transition qui nous intéresse est la montante. C'est pourquoi au début de _VIDEO_OUT_ISR on vérifie si la broche 18 est à 1
if (PIXDLY_INP){
Si c'est le cas on commence à envoyer les pixels du video_buffer en tenant compte du frame_line_cntr. Chaque ligne du video_buffer correspond à 1 ligne horizontale à l'écran.

Ces routines sont simples, en fait le plus gros du code de ce module est dans la définition des constantes et l'initialisation des périphériques.

Interface application

L'interface application (API) de ce module ne contient que 3 fonctions.

void video_init();
void wait_n_frame(unsigned n);
void blank_out();
La première fonction se passe de commentaire. wait_n_frame() est utilisée pour faire une pause dans le programme. Cette pause est en multiple de cadre. En mode NTSC un cadre dure 16,67msec. et en PAL 20msec.

blank_out() accepte 2 valeurs VIDEO_OFF et VIDEO_ON et sert exactement à ça. Cette fonction active et désactive la sortie des pixels vidéo mais sans désactiver le signal de synchronisation. On peut l'utiliser pour faire clignoter le contenu de l'écran au complet. exemple.

// clignotement de l'écran 5 fois par seconde
int delay;
if (video_mode==NTSC_MODE) delay=6;else delay=5; 
while(1){
    blank_out(VIDEO_OFF);
    wait_n_frame(delay);
    blank_out(VIDEO_ON);
    wait_n_frame(delay);
}

Aucun commentaire:

Publier un commentaire