lundi 1 janvier 2024

stm8 terminal

Puisque je viens de mettre à jour le projet stm8 terminal vers la version 1.0R14. Je vais parler de la génération d'un signal composite au standard NTSC à partir d'un microcontrolleur 8 bits.

Qu'est-ce que le standard NTSC

Ce standard prend son origine aux U.S.A. en 1941 pour la première version noir et blanc et en 1953 pour la version couleurs. NSTC est l'acronyme de National Television System Committee. Il s'agit d'un signal vidéo composite, c'est à dire que toute l'information est contenue dans un seul signal, i.e. synchronisation, luminance et chrominance. Il s'agit d'un signal analogique qui à l'origine était transmit par radiodiffusion.

Générer un signal NTSC monochrome comme pour le projet stm8 terminal est relativement simple et peut se faire même avec le plus petit des microcontrolleurs 8 bits. Dans le passé j'ai réalisé ce genre de signal sur un PIC12F322 pour le projet pong. Comme le projet stm8 terminal ce projet était réalisé entièrement en assembleur.

Comme à l'époque où a été créer ce standard, la télévision était entièrement analogique, l'image était recréé grâce à un tube cathodique par balayage d'un faisceau d'électrons sur une surface phosphorencente appliquée sur la face avant du tube cathodique. Chaque image est constituée de 2 trames entrelacées de 262,5 lignes et le taux de répétition est de 30 images par seconde (60 trames).

Ce qui complique un peu la génération de ce signal est justement cet entrelacement de trames car les trames paires et impaires sont décalées d'une demi-ligne l'une rapport à l'autre.

Le balayage commence en haut à gauche de l'écran en allant vers la droite et se termine en bas à droite.

Balayage entrelace affichage trames.svg
Par <a href="//commons.wikimedia.org/wiki/User:Cdang" title="User:Cdang">Christophe Dang Ngoc Chan</a> — <span class="int-own-work" lang="fr">Travail personnel</span>, CC BY-SA 3.0, Lien

Chaque ligne horizontal de balayage débute par une impulsion de synchronisation qui dure 4,7µSec. La partie visible qui affiche l'information vidéo dure 52µSec.

Une autre complication est la façon dont la synchronisation verticale qui indique le début de chaque trame est générée. Habituellement j'utilise une minuterie avec sortie PWM pour générer le signal de synchronistation. Hors la synchronisation verticale oblige à reprogrammer la minuterie pour réduire la période de la minuterie de moitier lors de la synchronisation verticale. En plus cette synchronisation se fait en 3 étapes. pré-égalisation, synchronisation, post-égalisation. Et chacune de ces phase oblige une reprogrammation de la minuterie et pour finir il faut revenir à la génération pleine ligne à la fin de la synchronisation. Comme les trames sont décalées d'une demi-ligne l'une par rapport à l'autre, le signal est différent entre trame paire et impaire.

Pourquoi NTSC plutôt que VGA

Pourquoi ne pas utiliser le standar VGA au lieu du standard NTSC ce serait tellement plus simple à générer comme signal, signal de synchronisation plus simple, pas de trames entrelacées. En effet c'est plus simple sauf que la fréquence du signal vidéo est plus élevée. en VGA pour générer 640 pixels par ligne il faut une fréquence de 25.1Mhz (pixel clock frequency). Pour le signal VGA on ne dispose que de 25,4µSec visible pour afficher tous les pixels d'une ligne de balayage. La moitié de ce qu'on dispose en NTSC.

Le mcu du stm8 terminal utilise une fréquence système de 20Mhz et le périphérique SPI utilisé pour sérialiser les pixels sur la ligne vidéo fonctionne à 10Mhz ce qui permet d'afficher 496 pixels par ligne de balayage (62 caractères par lignes). En utilisant le standard VGA on ne pourrait qu'afficher 254 pixels soit 31 caractères par ligne.

Ce que j'ai dit jusqu'ici ne concerne que le NTSC noir et blanc. S'il faut ajouter la couleur à ça, c'est d'autant plus complexe même si j'ai réussi à le faire sur un microcontrôleur PIC12F1572 pour mon projet breakout, ce qui sans me vanter m'a demander de faire preuve d'imagination avec l'utilisation des périphériques.

Donc avec un microcontrôleur à une fréquence système de 48Mhz ou plus il est plus intéressant d'utiliser le standard VGA cependant. Mais en bas de cette fréquence vaut mieux s'en tenir au NTSC malgré la difficulté de générer un tel signal.

Difficultés de la génération d'un signal de qualité.

Je m'en tiendrai ici qu'au monochrome. La façon la plus simple que j'ai trouvé de générer un signal NTSC est d'utiliser une minuterie avec périphérique PWM (Output Compare) pour générer le signal de synchronisation et la sortie MOSI d'un périphérique SPI pour sérialiser les pixels vidéo. Évidemment cette méthode utilisant un SPI n'est valide que pour les signaux vidéo monochromes.

La gigue

La précision de la synchronisation du signal vidéo est fondamentale pour obtenir un signal de qualité. Le problème est d'obtenir un délais constant au niveaux des interruptions vidéos. En effet le temps de réponse aux interruptions, ce qu'on appelle la latence, n'est pas constant. Alors que pour les µC PIC cette latence ne varie que d'un cycle pour les STM8 elle peut variée de plusieurs cycles car l'instruction en cours d'exécution au moment ou le signal d'interruption est généré doit-être complétée avant de répondre à l'interruption. Pour les µC PIC le temps d'exécution des instructions est constant sauf pour les sauts et appels de sous-routine qui prennent un cycle de plus. Pour les µC STM8 le temps d'exécution d'une instruction varie entre 1 et 5 cycles. Il y a donc une plus grande variabilité de la latence. Ce qui génère la gigue (jitter en anglais). Dans le cas d'un signal vidéo cette gigue est très visible et embêtante. Heureusement il y a une méthode pour la contrecarrer.

Dans la routine ntsc_video_interrupt qui est une interruption générée par la minuterie TIM1 qui synchronise le signal vidéo. j'utilise le code suivant pour annuler la gigue.


     ld a,TIM1_CNTRL 
    and a,#7 
    push a 
    push #0 
    ldw x,#jitter_cancel 
    addw x,(1,sp)
    _drop 2 
    jp (x)
jitter_cancel:
    nop 
    nop 
    nop 
    nop 
    nop 
    nop 
    nop
 

Le principe de fonctionnement est le suivant. Le compteur de la minuterie demeure régulié même si le temps de latence varie. Donc si au moment d'entrée dans la routine d'interruption on lit la valeur du compteur (il suffit de lire l'octet faible), le compte va varier en fonction de la latence. Si la latence prend 3 cycles supplémentaire le compte aura augmenter de 3. Comme cette latence ne variera jamais plus que de 5 cycles on ne tiens compte que des 3 derniers bits avec l'opératipon and a,#7. On charge le registre X avec l'adresse cible jitter_cancel. On fait un saut indirect en utilsant la valeur dans X indixé par la valeur des 3 bits les moins significatifs du compteur. Si le compte est nul 6 instructions nop seront exécutées avant de débuter l'envoie des pixels vidéo. Chaque nop s'exéctute en un seul cycle. Si le compte est de 7 le saut se fera après les nop. Il s'agit donc de créer un délais supplémentaire variable inversement proprotionnel à la durée de la latence pour annuller la variabilité de celle-ci. Ainsi l'envoie des pixels vidéo débute toujours au même point par rapport au début de la ligne de balayage.

Interférence

Un problème plus difficile à résoudre est celui des interruptions qui se produisent avant le début d'une ligne vidéo et n'ont pas le temps de se terminer avant le début de la ligne vidéo. La révision V1.0R14 de stm8 terminal avait pour but de corriger un tel problème causé par l'interruption de la minuterie TIM4 qui était générée à un intervalle de 1 msec. Cette interruption se produisait donc plusieurs fois pendant la période visible du signal vidéo ce qui produisait un glissement des pixels vidéos sur les lignes affectées par cette interruption. La seule façon que j'ai trouvé de régler ce problème est de simplement ne plus utiliser la minuterie TIM4. Celle-ci ne servait qu'à synchroniser le clignotement du curseur texte. Hors cette opération pouvait tout aussi bien être réalisée en utilisant l'interruption de la minuterie TIM1 qui sert pour générer la synchronisation vidéo. J'ai donc modifier le code dans ce but.

Problèmes non résolus

Il reste 2 problèmes qui interfèrent encore avec le signal vidéo et pour lesquels je n'ai pas de solution. Le premier est l'interruption de lecture du clavier PS/2 et le second est l'interruption sur réception d'un caractère sur le UART.

  1. Sur l'ordinateur pomme I qui utilise le stm8 terminal, si on écris un programme BASIC qui envoie en boucle des caractères au terminal sans arrêt on voit très nettement que la réception de ces caractères crée de l'interférence dans l'affichage vidéo.
  2. Lorsqu'on saisie une ligne au clavier sur le pomme I on voit aussi que le signal vidéo est perturbé mais dans une moindre mesure.

Aucun commentaire:

Publier un commentaire

Remarque : Seuls les membres de ce blogue sont autorisés à publier des commentaires.