dimanche 29 juin 2014

LPC810, partie 8, pong

Pour mettre en pratique ce que j'ai appris sur le LPC810 j'ai réalisé un jeux PONG sur le LPC810. Le montage est si simple qu'il tiens sur une carde perma-proto 1/4.

Montage

schématique

Liste du matériel

  • 1 MCU LPC810M021FN28, U1
  • 1 carte perma-proto 1/4.Un produit de bonne qualité. Peut-être soudé sur les 2 faces, pratique pour les SMD 0805 (voir photo du prototype ci-haut).
  • 2 jack RCA phono, J1 jaune (vidéo), J2 blanc (audio)
  • 1 LED rouge 3mm, D2
  • 2 résistances 120 ohm 1/8 watt, R3,R8.
  • 1 résistance 620 ohm 1/8 watt, R1.
  • 1 résistance 680 ohm 1/8 watt, R2.
  • 2 résistances 1Kohm 1/8 watt, R6, R7.
  • 2 résistance 10Kohm 1/8 watt, R4, R5.
  • 2 potentiomètre 10Kohm linéaire, P1, P2.
  • 1 condensateur céramique 2,2nF, C1
  • 4 condensateurs céramique 100nF, C2,C3,C4,C5. J'ai utilisé un format SMD 0805 car c'est juste de la bonne dimension pour être souder entre deux rangés adjacentes.
  • 1 diode schottkey 1N6263, D1
  • 2 boutons momentanés SW1, SW3.
  • 1 porte pile avec commutateur pour 2 piles AA ou AAA
  • 1 barrette de pin header 100mil.

Démo

Description du programme

L'ensemble du projet LPCXpresso est sur https://github.com/Picatout/lpc810-pong sous licence GPLv3. Vous pouvez télécharger le fichier zip et l'importer dans l'IDE LPCXpresso. Le projet doit être compilé avec l'option -O2 sinon il n'y a pas suffisamment d'espace flash.

Le jeux est contrôlé par des potentiomètres de 10Kohm. Chaque joueur en a un. Le pointage apparait en haut de l'écran. Le gagnant est celui qui atteint 21 points le premier. La balle est collée sur la raquette du joueur qui a le service. Le joueur doit déplacer sa raquette pour lancer la balle. La direction que prend la balle est tirée au hasard. ATTENTION! au démarrage le potentiomètre que est branché sur la broche 5 doit-être en position Vdd. S'il est en position Vss le bootloader va entrer en mode programmation au lieu de démarrer le jeux.

Le fichier hardware.c Initialise les périphériques utilisés. Le PLL permet de monter la fréquence à 30Mhz. Le SCT génère le signal vidéo et le signal audio. Le MRT est utilisé pour les délais. Le comparateur analogique est utilisé pour lire les potentiomètres. Il y a deux gestionnaires d'interruption d'utilisé, l'un pour la génération audio MRT_IRQHandler() situé dans hardware.c, l'autre pour la génération des signaux vidéo SCT_IRQHandler() situé dans tvout.c.

Le fichier tvout.c génère un signal NTSC composite monochrome. La résolution est de 64x56 pixels et le video_buffer occupe 448 octets RAM des 1024 disponible. Comme toujours j'utilise un PWM pour la synchronisation et un périphérique SPI pour sérialiser les pixels vidéo.

Les potentiomètres qui contrôle les raquettes des joueurs sont branchés sur les 2 entrées du comparateur analogique ACMP_I1 et ACMP_I2. Ce comparateur contient une référence de voltage programmable sur 32 niveaux. J'ai donc décidé d'utiliser cette référence en conjonction avec le comparateur pour créer un convertisseur A/N à 5 bits. Le convertisseur est de type SAR (Successive Approximation Register). Pour son fonctionnement voir la routine read_pot() dans le fichier hardware.c. La routine fait une division binaire de L'intervalle pour obtenir un résultat en 5 comparaisons.

Tout le code est écris en 'C' et peut-être adapter facilement à un autre MCU LPC. Le fichier tvout.c est particulièrement utile pour l'utilisation dans d'autres projets avec sortie vidéo NTSC. Il peut être adapté facilement au standard PAL en modifiant les constantes dans le fichier tvout.h


Liens vers les articles précédents:

  1. github du jeux pong
  2. Introduction au LPC810 (ARM M0+)
  3. LPC810, partie 2, anatomie
  4. LPC810, création d'un projet
  5. LPC810, partie 4, programmation
  6. LPC810, partie 5, module SCT et PWM

mercredi 25 juin 2014

LPC810, partie 7, les interruptions

Dans cet article je discute des interruptions sur les LPC81x. En fait le contrôleur d'interruption fait partie du coeur ARM Cortex M0+, donc son fonctionnement est le même sur tous les MCU qui utilise les CPU M0 et M0+.

Le contrôleur d'interruption

Dans le jargon de la compagnie ARM ce contrôleur s'appelle NVIC pour Nested Vector Interruptupt Controller. Comme son nom l'indique il s'agit d'un contrôleur multi-niveaux utilisant une table de vecteurs. Par multi-niveaux on entend qu'une interruption en cours peut-être interrompue par une autre interruption de priorité supérieure. Il y a 4 niveaux de priorités numérotées de 0 à 3. 0 étant la plus haute et 3 la plus basse.

Lorsqu'on crée un projet utilisant CMSIS_CORE_LPC8xx dans LPCXpresso, on retrouve dans le dossier src du projet un fichier appelé cr_startup_lpc8xx.c et à l'intérieur de celui-ci se trouve la déclaration de toutes les fonctions de gestions d'interruptions.

Comme on le voit il s'agit d'alias pour un gestionnaire par défaut qui contient une boucle while infinie. Donc si dans votre programme vous activez une interruption sans créer un gestionnaire pour celle-ci, lorsque cette interruption sera déclenchée votre programme va bloqué.

Pour créer un gestionnaire d'interruption il suffit de créer une fonction avec le nom du gestionnaire définie dans ce fichier. Par exemple le gestionnaire pour le périphérique MRT (Multi Rate Timer) s'appelle MRT_IRQHandler.

Utilisation d'une interruption

A titre d'exemple on va créer un projet MRT-multi-blink et utiliser le périphérique MRT pour faire clignoter 3 LEDs à des vitesses différentes l'une de l'autre. Ce projet va utiliser une interruption MRT_IRQHandler. Le MRT contient 4 minuteries et chacune d'elle peut-être programmée pour un délais différent. Chaque fois qu'un délais expire l'interruption est déclenchée. Le gestionnaire vérifie qu'elle canal du MRT a déclenché l'interruption et inverse l'état de la LED correspondante.

Créez un nouveau "C project" appelé "MRT-multi-blink" et utilisez la librairie CMSIS_CORE_LPC8xx. Copiez le code source suivant dans le fichier main.c.

Analyse du programme.

Ce programme utilise le signal d'horloge par défaut soit l'IRC sans PLL. Donc le CPU tourne à 12Mhz. Dans la fonction main on commence par désactiver les fonctions fixes ACMP_I1, ACMP_I2, CLKIN, SWCLK et SWDIO. Ces fonctions sont activées/désactivés par des bits dans le registre LPC_SYSCON->PINENABLE0. la macro m_disable_function simplifie cette configuration.

Par défaut les périphériques sur le bus AHB sont désactivés. On doit donc les activés dans le registre LPC_SYSCON->SYSAHBCLKCTRL. Ici on active le clock pour les GPIO et pour le MRT. Se sont les 2 périphériques que ce programme utilise.

À la ligne suivante on met les broches sur lesquelles les LEDs sont branchées en mode sortie en mettant les bits correspondants dans LPC_GPIO_PORT->DIR0 à la valeur 1. un bit à 0 dans ce registre correspond à une entrée.

Ensuite on configure les 3 canaux du MRT. Le registre STAT contient un bit indicateur d'interruption. Pour mettre ce bit à zéro il faut écrire un 1 dans le bit (ce n'est pas une erreur, mettre 1 pour réinitialiser). Le registre CTRL contient le bit d'activation/désactivation en position 0. et 2 bits de sélection du mode de fonctionnement en position 2:1. On active le canal en laissant le mode à zéro qui correspond au mode interruption répétitive. Dans ce mode une interruption est déclenchée lorsque le compteur arrive à zéro (le comptage est à rebours). La valeur initiale est rechargée automatiquement et le cycle recommence. On obtient donc une interruption à intervalle régulier.

Le compteur démarre au moment ou on inscris la valeur de l'intervalle désiré dans le registre INTVAL.

On programme ensuite la priorité d'interruption au plus bas niveau avec la fonction NVIC_SetPriority(). Finalement on active cette interruption avec la fonction NVIC_EnableIRQ().

Le gestionnaire d'interruption est très simple. On vérifie quels sont les canneaux qui ont l'indicateur d'interruption à 1 et pour ceux-ci, on inverse l'état de la sortie GPIO associé.

Pour en savoir plus sur l'interface de programmation des interruptions il faut lire le fichier d'entête core_cm0plus.h qui fait parti de la librairie CMSIS_CORE_LPC8xx.


Liens vers les articles précédents:

  1. Introduction au LPC810 (ARM M0+)
  2. LPC810, partie 2, anatomie
  3. LPC810, création d'un projet
  4. LPC810, partie 4, programmation
  5. LPC810, partie 5, module SCT et PWM
  6. LPC810, partie 6, system clock et PLL

lundi 23 juin 2014

LPC810, partie 6, system clock et PLL

Dans ce 6ième article consacré au LPC810 j'explique comment configurer le PLL avec le IRC pour obtenir une fréquence de fonctionnement différente du 12Mhz de l'IRC.

LPC810 peut fonctionner à une fréquence maximale de 30Mhz selon les spécifications. L'oscillateur interne IRC fonctionne à 12Mhz. C'est la fréquence à laquelle le MCU fonctionne par défaut. Cependant en utilisant le PLL et le diviseur du system clock on peut faire fonctionner le MCU à une fréquence supérieure ou inférieure à 12Mhz.

PLL

PLL est l'acronyme anglophone de Phase Lock Loop. Il s'agit d'un circuit qui permet de multiplier une fréquence qu'on injecte à l'entrée par une certaine valeur. Voici le diagramme fonctionnel du PLL inclus dans le LPC810.

FCLKIN est le signal injecté à l'entrée du PLL et qui sera multiplié. Dans le cas du LPC810 il n'y a que 2 possibilités soit soit l'oscillateur IRC soit un signal provenant de la broche CLKIN. sys_osc_clk n'est disponible que sur les LPC811 et LPC812.

Au coeur du PLL il y a un oscillateur contrôlé par courant, le CCO (Current Controlled Oscillator). La fréquence du CCO peut varier entre 156Mhz et 320Mhz. Le bloc PFD (Phase Frequency Detector) compare le signal d'entrée avec le signal de sortie du PLL qui est divisé par les blocs /2P et /M. Si les 2 signaux n'ont pas la même fréquence et phase un signal d'erreur à la sortie du PFD modifie la fréquence du CCO pour diminuer cette erreur.

Donc lorsqu'on utilise le PLL on doit choisir 2 diviseurs le /2*P et le /M. La sortie du CCO est divisée par la valeur 2*P où P peut prendre les valeurs 2,4,8 16. Le diviseur /M peut prendre n'importe quelle valeur entre 1 et 32.

Prenons un exemple, dans lequel on veut faire fonctionner le MCU à sa fréquence maximale en utilisant l'IRC et le PLL. La sortie du PLL FLCKOUT ne peut pas être de 30Mhz car aucune division entière de 30Mhz ne donne 12Mhz. Le plus petit commun multiple entre 12Mhz et 30Mhz est 60Mhz. On va donc programmer le PLL pour obtenir un FCLKOUT de 60Mhz et on va utiliser le diviseur du system clock pour diviser FCLKOUT par 2. Selon les spécifications la fréquence maximale pour FCLKOUT est de 100Mhz.

La fréquence du CCO doit-être maintenue entre 156Mhz et 320Mhz. Donc on doit choisir une valeur de P de sorte que 156<=2*P*60<=320. Si on choisi 2 pour P on a un Fcco=2*P*60=240Mhz on est donc dans l'intervalle.

Maintenant il suffit de choisir la valeur de /M simplement M=60/12=5.

On a donc nos 2 valeurs pour P=2 et /M=5. C'est le registre SYSPLLCTRL qui contient ces 2 diviseurs. les bits 4:0 contiennent MSEL. La valeur de /M=MSEL+1, donc pour 5 on met 4 dans SYSPLLCTRL[4:0]. La valeur PSEL est dans les bits 6:5. les valeurs de P sont:

bits [6:5]P
001
012
104
118

Pour diviser FCLKOUT pour obtenir Fsysclk=30Mhz on utilise le registre SYSAHBCLKDIV. Ce diviseur est compris entre 1 et 255. Dans cet exemple on va y mettre la valeur 2.

code exemple

Lorsqu'on modifie les diviseurs du PLL il faut mettre à zéro et ensuite à 1 le registre SYSPLLCLKUEN (System PLL Clock Update Enable) pour mettre à jour les nouvelles valeurs.

Ensuite on sélectionne le diviseur dans SYSAHBCLKDIV et on sélectionne la sortie du PLL comme source du main clock. Encore une fois il faut faire une séquence 0,1 dans le registre MAINCLKUEN (Main Clock Update Enable) pour compléter la mise à jour.


Liens vers les articles précédents:

  1. Introduction au LPC810 (ARM M0+)
  2. LPC810, partie 2, anatomie
  3. LPC810, création d'un projet
  4. LPC810, partie 4, programmation
  5. LPC810, partie 5, module SCT et PWM