Quantcast
Channel: PICatout
Viewing all 192 articles
Browse latest View live

LED blanche sur 3 volt version 2

$
0
0

Dans l'article précédent je présentais une façon d'augmenter le voltage d'une lampe de poche de 3 volt pour obtenir le maximum d'intensité avec une LED blanche fonctionnant à 120ma. Le circuit d'hier utilisait une inductance. Aujourd'hui je présente un autre type de circuit qui lui permet de doubler le voltage par pompe de charge. Ce type de circuit n'utilise pas d'inductance mais 2 condensateurs.

Principe de fonctionnement

Il s'agit de charger le condensateur C1 et ensuite de transférer la charge accumulée dans C1 vers C2. Comme si on remplissais une chaudière d'eau pour ensuite la vidée dans une autre dont le fond est percé afin de garder son niveau d'eau constant.

Dans la première phase on place le transistor Q2 en conduction de sorte que C1 se charge à travers la diode D1. On bloque Q1 lorsque C1 est chargé. De la même façon que lorsqu'on doit vider une chaudière dans une autre il faut la soulevée au dessus de la première, il faut soulevé le voltage sur l'électrode négative de C1 pour qu'il se vide dans C2. Pour ce faire on met le transistor Q2 en conduction de sorte que le l'électrode négative de C1 est maintenant branchée sur le +3 volt de la pile. Donc le voltage aux bornes de C1 s'additionne à celui de la batterie et l'énergie accumulée dans C1 est transférée dans C2à travers D2. La diode D3 aide C2à demeuré chargée à un voltage près de 3 volt de sorte que C1 n'a pas besoin de fournir autant d'énergie pour augmenter le voltage de C1.

Ce circuit nécessite plus de composants que le précédent mais si le courant tirée par la charge était inférieur à 10ma on pourrait brancher le négatif de C1 directement sur une sortie du PIC10F200. Le PIC10F200 pouvant drainer ou sourcer 20ma ça permet un courant de charge moyen de la moitié de cette valeur.

Ce circuit est moins efficace en terme de conversion de voltage. En effet le système inductif permet de monter le voltage beaucoup plus que du double. Sans charge sur le circuit inductif je mesurait 35 volt à la sortie. Par contre avec ce circuit je ne crains pas de griller la LED et je n'utilise donc pas de rétroaction.

voltages aux différents points du circuit.

Voltage au bornes de la LED avec une alimentation de 3 volt. Le voltage maximum est de 4,72 volt et descend à 4,48 volt entre chaque recharge pour un voltage moyen de 4,60 volt.


Voltage à la jonction de Q1 et Q2


Voltage à la jonction de C1 et D1.


voltage aux bornes de la résistance R3. Le courant dans la LED est donc 1,72/15=115ma.

code source


indicateur de voltage barre-graphe

$
0
0

IL y a 2 semaines je présentais une alimentation variable éconmique et je concluais l'article en disant que je songeais à ajouter un indicateur de voltage barre-graphe. C'est chose faite.

Schématique

Description

La barre de LEDs contients 10 LEDs qui sont branchées en Charlieplexing sur GP0,GP1,GP2 et GP5. GP4 est configuré en entrée analogique pour la lecture du voltage de sortie de l'alimentation qui varie de 1,25 à 5,5 volt. Le circuit a son propre régulateur de tension 5 volt (7805) branché à la sortie du pont redresseur de l'alimentation.

L'échelle progresse par échelon de 0,5volt mais la logique du code est conçu de sorte que si la valeur lue tombe entre 2 LEDs, ces deux LEDs sont allumées. On a donc environ 0,25 volt de résolution.

Le convertisseur A/N du PIC12F675 a 10 bits de résolution mais le programme utilise seulement les 8 bits les plus significatifs. Ici le calcul pour déterminer quel LED allumée en fonction de la lecture est une simple règle de 3. LED=Vi*10/256. Le programme fait la multiplication par 10 mais il n'est pas nécessaire de faire la division puisque la variable PRODHI contient déjà le résultat de cette division après la multiplication.

Code source

moteur pas à pas

$
0
0

Dans cette article j'explique comment contrôler un moteur pas à pas . Pour ce démo j'ai utilisé un MSP430 launchpad et un C.I. L293D pour piloter le moteur. Comme moteur j'ai utilisé le mécanisme de transport de la tête de lecture d'un graveur de CD. On retrouve aussi le même type de mécanisme dans les lecteurs de disquettes.

schématique

Le moteur a 2 bobines et comme illustré ci-haut. Il s'agit d'un moteur bipolaire il y a donc 4 connections au moteur. Sur les moteurs unipolaire il y a en 6.

Description

En fait le programme pour contrôler un moteur pas à pas est simple. On a besoin d'un pont H pour chaque bobine et c'est le L293D qui contient 4 demi-pont H qui est utilisé dans ce but. Lorsqu'on alimente une bobine le rotor s'aligne sur le champ magnétique créé par la bobine. Si on alimente les 2 bobines en même temps le champ magnétique résultant est entre les 2 bobines et le rotor s'aligne donc entre celle-ci. Donc pour déplacé le rotor on déplace le champ magnétique du stator en alimentant alternativement les bobines.

Il y a plusieurs façon d'alimenter un moteur pas à pas mais celle qui donne le plus petit angle de rotation à chaque pas est la suivante:

pasa1b1a2b2
1+5v0v0v0v
2+5v+5v0v0v
30v+5v0v0v
40v+5v+5v0v
50v0v+5v0v
60v0v+5v+5v
70v0v0v+5v
8+5v0v0v+5v
Pour faire tourner le moteur en continu dans un direction il suffit de répéter ces 8 pas en boucle et pour le faire tourner dans l'autre sens on les répète à l'envers c'est à dire de 8 à 1. J'ai branché les 4 fils des bobinages sur le même port (PORT1) de sorte qu'il suffit de conserver les valeurs pour chaque pas dans un tableau indicé par la phase. Il faut écrire la valeur correspondant à la phase ou le moteur est rendu dans P1OUT. La valeur de chaque nombre dans le tableau correspond aux étapes de cette table.

Un moteur pas à pas il n'y a pas de rétroaction comme pour le cerveau moteur donc on ne connaît pas la position du moteur. Il faut compter les pas et habituellement on utilise des commutateurs en fin de course pour indiquer que la limite est atteinte. Mais si la charge est trop grande et que le moteur glisse le compteur de pas n'est plus valide il faut voir à ce que la charge ne dépasse pas la capacité du moteur. Il faut aussi tenir compte de l'inertie. Plus la charge est grande plus le moteur va prendre de temps pour s'aligner sur la nouvelle position. Donc le délais de pas doit-être augmenter avec la charge. Comme au départ je ne connaissait pas les caractéristiques du moteur j'ai commencer les tests avec un délais long (50msec), pour le réduire progressivement jusqu'à 1msec. Mais à 1 msec le moteur vibre. A 2msec c'est parfais. Mais si je mettais une charge sur le chariot il faudrait probablement augmenter ce délais.

Pour ce démo j'ai simplement vérifier à quelle vitesse je pouvais déplacer le chariot de tête du graveur en envoyant 200 impulsions dans un sens et ensuite 200 dans l'autre.

déplacement lent, 50msec/pas déplacement rapide 2msec/pas.

Conclusion

Comme on le voie un pilote de moteur pas à pas c'est simple. Il serait facile d'adapter ça en assembleur pour n'importe quel microcontrôleur.

présentation des PIC24

$
0
0

Dans cette article je fais une brève présentation des micro-contrôleurs PIC24 de Microchip.

Architecture

Comme les autres PIC les PIC24 sont basés sur une architecture Harvard mais là s'arrête la ressemblance. Il s'agit de micro-contrôleurs 16 bits avec 16 registres généraux. Ils possèdent un multiplicateur en hardware ainsi qu'un support à la division.

Structure interne

Le compteur ordinal a 23 bits. Les instructions son encodées sur 24 bits et le compteur peut adresser 4Mi. Notez cependant que le bit le plus faible du compteur ordinal est toujours à zéro, les adresses impaires ne peuvent-être accédées. La mémoire est organisé en mots de 24 bits mais l'alignement du compteur ordinal se fait comme s'il s'agissait de 2 mots de 16 bits. Donc dans les faits le compteur ordinal est incrémenté de 2 à chaque instruction. Il n'y a pas d'octets perdus cependant car l'octet le plus lourd (bits 24-32) n'est pas implémenté en mémoire. Cette arrangement est pour rendre comptable l'adressage avec la mémoire RAM qui elle est organisée en mots de 16 bits.

Les registres généraux sont nommés W0à W15. W15 sert de pointeur de pile, celle-ci étant en mémoire RAM. IL y a un registre SPLIM qui détermine la taille de la pile. S'il y a débordement de la pile l'exception 3 stack error est déclenchée. Notez la présence du registre RCOUNT, c'est un compteur de boucle qui fonctionne avec l'instruction assembleur REPEAT. Cette instruction utilisée avec les instructions DIV.SD, DIV.SW, DIV.UD et DIV.UW permet de faire une division en seulement 19 cycles machine. Les boucles REPEAT sont limitées à une seule instruction, celle qui suis le REPEAT. Mais comme l'instruction MOV encode la source et la destination par indirection via registre auto-incrémenté, une seule instruction permet de copier un bloc de mémoire d'un endroit à un autre ou vers un SFR de périphérique.

Contrairement à la plupart des MCU à registres comme les AVR, MIPS ou MSP430, les PIC24 peuvent utiliser une donnée en mémoire RAM comme argument d'une opération ALU, dans ce cas W0 alias WREG est utilisé comme autre argument. Le résultat peut-être enregistré dans WREG ou la variable en RAM. Ce type d'instruction même s'il exige une lecture et possiblement une écriture dans la RAM s'exécute en un seul cycle. Comme pour les PIC32MX les instructions à 3 arguments sont aussi possible comme ADD Wb,Ws,Wd qui additionne le contenu de Wb avec Ws et met le résultat dans Wd.

Interruptions

Les interruptions multi-niveaux sont supportées. Il y a 2 tables de vecteurs d'interruptions contenant chacune 126 entrée. Les 8 premières entrées sont réservés aux exceptions (trap). Ces tables commencent à l'adresse 0x000004 et se terminent à l'adresse 0x000200. L'interruption 0 est à l'adresse 0x14 pour la table principale et 0x114 pour la table alternative. Chaque table occupe 256 octets incluant les trap et le reset vector. La table alternative sert au débogage. Chaque entrée de table contient une adresse de 24 bits qui est le point d'entrée de l'ISR servant le vecteur correspondant. La priorité d'interruption est par ordre inverse du numéro de vecteur. Le vecteur 0 a la plus haute priorité et le 117 la plus faible.

Modèle de programmation

Programme démo en C

Plusieurs modèles de PIC24 sont disponibles en format PDIP-14, PDIP-20 et PDIP-28. Pour ce démo j'ai choisi un PIC24F16KA101-I/P ainsi qu'une matrice de LED 8x8. La matrice de LEDs est organisée en 8 lignes par 8 colonnes. Les cathodes sont connectées sur les colonnes et les anodes sur les lignes. Le PIC24F16KA101 possède 16Ko de mémoire flash et 1,5Ko de RAM et les périphériques qu'on retrouve le plus souvent sur un MCU, minuteries, convertisseur A/N, UART, I2C. Il fonctionne à une fréquence maximale de 32Mhz. Il peut-être programmé avec un PICKIT 2.

L'environnement de développement est MPLABX avec le compilateur XC16 disponible en version gratuite avec optimisation de code limité comme c'est l'habitude chez Microchip.

Le démo est une implémentation de l'automate cellulaire appelé Jeux de la vie. Le démo joue 3 configurations en séquence. Il passe à la configuration suivante après 16 générations. C'est la variable max_gen qui détermine le nombre de générations calculées pour chaque configuration initiale.

schématique

La grille de LED est multiplexé 1 LED à la fois. Le rapport cyclique de chaque LED est donc de 1/64 on obtient néanmoins une intensité d'affichage satisfaisante. Pour ne pas interrompre le multiplexeur d'affichage le calcul de la génération suivante est intercalé dans le multiplexeur par appel de la fonction next_gen(). Une seule cellule est recalculée par appel. Il y a 2 tableaux contenant l'état de l'univers, lifea et lifeb. Tandis qu'un tableau est affiché le calcul de la génération suivante est enregistré dans l'autre. La variable grid détermine quel est le tableau à afficher. Le tableau est commuté à tous les 100 cycles du multiplexeur d'affichage (variable k dans display_grid()). Ce délais donne un peu plus 0,5 seconde entre chaque commutation.

Pour la Configuration bits settings j'ai procédé comme je le fais avec les PIC32MX. C'est à dire que j'utilise le menu Window - PIC memory views - configuration bits. Après avoir configuré les bits dans cette grille j'utilise le bouton generate source code to Output et ensuite un copier-coller vers mon fichier source.

Pour ce démo le MCU fonctionne à 8Mhz sur l'oscillateur interne mais cette fréquence peut-être augmentée à 32Mhz grâce au PLL inclus dans le MCU.

Code source du démo

Vidéo du démo

Conclusion

Dans un prochain article je vais présenter un démo en assembleur.

surprise et perplexité.

$
0
0

J'étais en train d'écrire une routine de division en assembleur sur PIC32MX lorsque j'ai réalisé que le core MIPS M4K n'a pas de registre d'état. Ça fait des mois que je travaille avec des PIC32MX et jusque là je n'avais pas porté attention à ce fait. En réalité c'est le seul processeur que je connais qui n'en possède pas et j'en ai vu pas mal ces 40 dernières années. Comme cette routine de division utilise un décalage vers la gauche sur 2 mots de 32 bits j'étais un peut perplexe. En effet habituellement lorsqu'on fait un décalage à gauche ou à droite sur plusieurs mots on utilise une instruction qui récupère le bit qui sort dans le carry bit et de là on le récupère pour le faire entrer dans le mot suivant. C'est du moins comme ça que ça se passe sur la majorité des micro-processeurs. J'ai donc du adapter ma routine en vérifiant l'état du bit avant de faire le décalage et faire un branchement conditionnel en fonction de l'état de ce bit. La routine nécessite donc plus d'instructions que sur un processeur ARM par exemple qui laisse le dernier bit sorti dans carry bit du registre d'état.

C'est encore une preuve que les habitudes nous rendent aveugle. Ce n'est pas que je n'avais pas étudier le modèle de programmation du core M4K mais je prenais pour acquis que tout processeur à un registre d'état ce qui fait que cette absence n'était pas passé au niveau conscient puisqu'il n'avais pas été requis dans ma programmation jusque là.

cube 4x4x4 avec PIC24F16KA101

$
0
0

Je poursuis mes expérimentations avec le PIC24F16KA101. Cette fois je présente un cube de LED 4x4x4 réalisé avec des LEDs vertes de 3mm. Le cube de LED fait 3cmx3cmx4cm, (oui je sais, pas vraiment cubique). Pour ce cube j'ai utilisé la méthode de multiplexage dite Charlieplexing car elle nécessite moins de E/S, 9 lieu de 16 comme ce fut le cas pour la matrice de LED 8x8 utilisé dans ma présentation du PIC24F16KA101. Voici un court vidéo du démo en action.

méthode de fabrication du cube

Sur une planche j'ai percé 16 trous de 3mm de diamètre sur une grille de 3cm x 3cm. Comme il faut 9 E/S pour contrôler 64 LEDs en Charlieplexing j'ai soudé ensemble les cathodes par groupe de 8. Il y a donc 2 cathodes par plan. Les cathodes des LEDs qui sont soudées ensemble se font face. La première étape est de relier ces cathodes par un fil comme illustré ici.

Ensuite on relie les anodes 2 par 2 comme indiqué dans le tableau ci-bas. Sauf qu'il y a toujours 2 anodes de reliées à la cathode du groupe opposé. Ainsi p1a2 est relié à p1c1 et p1a9 est relié à p1c1. Les informations concernant un plan sont groupées par couleur. Au final il ne devrait y avoir que 9 fils qui relie ce plan au plan suivant. J'ai assemblé les 4 plans pour ensuite assembler ceux-ci ensemble. Ce travail demande beaucoup d'attention car le câblage est très enchevêtré. Si vous faites ce montage je vous conseille de vérifier chaque plan avant de faire l'assemblage final.
Comme les fils s'entrecroisent j'ai utiliser des manchons en plastique pour recouvrir les fils afin d'éviter les court-circuits, ce n'est pas la meilleur idée que j'ai eu, ça grossi le diamètre des fils et masque les LEDs qui sont en arrière plan. Et j'ai choisi une couleur différente pour chaque plan pour éviter de les confondre. Si c'était à refaire je m'y prendrais autrement.

J'ai créé un schéma du circuit mais pour le montage j'ai utiliser un tableau moins confondant que ce câblage spaghetti.

Les lignes du tableau représente les électrodes qui sont reliées l'une à l'autre. p indique le numéro du plan. c indique le numéro de cathode et a indique le numéro d'anode. Ainsi p1a2 est l'anode de la LED 2 du plan 1. Les LEDs sont comptées de gauche à droite et du haut vers le bas lorsqu'elles sont plantées dans la planche.

Code source du démo

PIC24F16KA101 et MSP430G2553

$
0
0

J'ai consacré plusieurs articles sur ce blog au msp430 launchpad parce que je suis très enthousiasmé par ce produit considérant son prix. Mais à ce moment là je n'avais jamais travaillé avec les PIC24 et franchement ceux-ci me plaisent encore plus. Dans cette article je fais une comparaison entre les deux.

comparatif PIC24F16KA101 et MSP430G2553 format PDIP-20

paramètrePIC24F16KA101MSP430G2553
architecture16 bits Harvard modifiée16 bits Von Neumann
FLASH Ko1616
RAM bytes1536512
EEPROM bytes512256
(information memory)
Tcy nSec62,562,5
encodage24bits16bits
interruptionsmulti-niveaux,
priorité programmable
multin-iveaux
priorité fixe
multiplicationmatériellelogicielle
divisionmatérielle/logiciel 19Tcyentièrement logicielle
registres 16 bits16
2 réservés
stack (W15) et frame (W14)
16
4 réservés
compteur ordinal (R0)
états (R1)
pointeur de pile(R2)
constantes (R3)
gestions des exceptionsoui
8 vecteurs 0-7
oui
2 vecteurs 30-31
nombre d'arguments
instructions assembleur
0,1,2,30,1,2
UART2
support LIN
2
support LIN
IrDAouioui
minuteries32
PWM15
ADC10bits
9 canaux 500KSPS
10bits
8 canaux 200KSPS
cap senseoui
9 entrées
oui
16 entrées
SPI11
I2C11
RTCCouinon
CRCouinon
LFOouioui
crystal 32Khzouioui
crystal HFouinon
E/S digitales1816
boot loader ROMnonoui
prix Digikey.ca3,30$CAN3,19$CAN

Biens que le PIC24F16KA101 peut fonctionner avec un oscillateur 32Mhz alors que le MSP430G2553 est limité à 16Mhz leur Tcy est le même car le PIC24F16KA101 utilise 2 cycles d'oscillateur par Tcy. La mémoire flash est de 16Ko dans les 2 cas mais le PIC utilise 3 octets par instructions alors que le MSP430 en utilise 2. Cependant le fait que le PIC24 peut utiliser un argument en mémoire RAM pour plusieurs de ses instructions il n'est pas forcément désavantagé en ce qui concerne la densité de code, pour savoir ce qu'il en est il faudrait faire des comparaisons sur plusieurs échantillons de code.

Il est évident que la multiplication en matériel ainsi que le support matériel à la division est un gros avantage en faveur du PIC24.

On a aussi un avantage pour le PIC24 en ce qui concerne le convertisseur ADC qui peut lire 500000 échantillons par seconde contre 200000 pour le MSP430. Par contre le MSP430 bénéficie d'un canal DMA sur le convertisseur.

Du côté de la gestion des exceptions et des interruptions le PIC24F est nettement avantagé par la programmabilité de la priorité des interruptions et les registres shadow. Mais il utilise beaucoup plus de mémoire flash pour les 2 tables de vecteurs, 512 octets contre 192 pour le MSP430.

Le code généré par le compilateur C de l'IDE CCS5 de TI et le compilateur XC8 de Microchip avec niveau d'optimisation à 1 est de bonne qualité dans les 2 cas mais encore là il faudrait faire des tests comparatifs entre les 2 pour avoir une idée exacte de la performance de chacun. Évidemment pour les programmes utilisant multiplications et divisions le PIC24F est avantagé.

Conclusion

Si les PIC 8 bits souffrent d'une architecture archaïque, surtout les versions baseline et midrange les plus gros défauts ayant été corrigés sur les PIC18, ce serait une erreur de se détourner des PIC24 à cause de leur petits frères à 8 bits. Les PIC24F n'ont rien d'archaïque et ont de nombreux avantages par rapports aux MSP430. Bien que mon expérience avec ces MCU est récente je trouve jusqu'ici l'expérience satisfaisante. Se familiariser avec une famille de MCU demande du temps. Il est donc avantageux de demeurer avec le même fournisseur car la nomenclature est uniforme et la programmation des périphériques est très semblable d'un famille à l'autre. L'effort est donc moindre. Et les coûts aussi puisque le même programmeur peut servir pour toutes les familles de MCU 8,16,32 bits et l'économique pickit 2 peut aussi programmer certains PIC24F comme c'est le cas pour le PIC24F16KA101. Si vous travaillez avec des PIC24/dsPIC30/dsPIC33 et qu'ensuite vous passez au PIC32MX vous allez trouver la transition facile surtout si vous programmez en C. Et si vous aimez travaillez avec des cartes de développement sans soudure, Microchip est le manufacturier qui offre la plus large gamme en format PDIP et ce sur toutes les familles 8,16,32 bits.

Les mcu les plus vendus

$
0
0

Juste par curiosité j'ai entré dans Google l'expression most successfull mcu et j'ai obtenu parmis les résultats ceci. A la page 5 de cette article Microchip affirme avoir vendu 7 millard de MCU de la famille PIC16. Aucun autre vendeur n'affiche un nombre aussi élevé. Il faut dire que Microchip a été le premier sur le marché des MCU ce qui leur donne un énorme avantage car les ingénieurs ont tendance à se cantonner dans ce qu'ils connaissent déjà.

Ce qui m'a le plus surpris est le résultat du sondage auprès des lecteurs à la dernière page. Les PIC16 scorent moins de 10% alors que les MSP430 obtiennent le meilleur résultat à plus de 40%. Texas Instruments affirme avoir vendu 500 million de msp430 launchpad. Il faut dire qu'à moins de 5$ au moment de son lancement c'était difficile d'y résister même à 10$US c'est encore un bon prix. Ce fut un excellent coup de marketing de la part de TI. Depuis on voit les concurrents qui s'ingénient à les imiter en offrant des cartes à bas prix eux aussi. Tant mieux pour les hobbyistes que nous sommes.


PIC24, les interruptions

$
0
0

Dans cet article je traite des interruptions sur les PIC24. A titre de démo je vais utiliser le cube de LEDs 4x4x4 présenté dans un article précédent. J'ai modifié le firmware de sorte que l'affichage du cube est maintenant géré par une une interruption. J'ai aussi ajouter une interface RS-232 en utilisant le périphérique UART1. La réception des caractères est aussi géré par une interruption.

Les interruptions sur PIC24/dsPIC30/dsPIC33 sont multi-niveaux avec priorité programmable. Multi-niveaux (nested en anglais) signifie qu'une interruption en cours peut-être interrompue par une autre de priorité supérieure. Lorsque l'interruption de niveau supérieur se termine l'exécution continu dans le code d'interruption de niveau inférieur qui a été interrompu. chaque table d'interruption (la principale et l'alternative) contient 126 vecteurs. C'est vecteurs sont des adresses qui correspondent au point d'entré de la routine qui sert l'interruption (ISR entry point). Les 8 premiers vecteurs servent a gérer les exceptions. En fait actuellement sur les PIC24 seulement 4 sont utilisés les autres sont réservés. Les 118 autres vecteurs sont pour les périphériques. Encore là plusieurs de ces vecteurs sont marqués comme reserved et donc inutilisés. La table des vecteurs est disponible dans le document suivant: répertoire_installation/microchip/xc16/v1.20/docs/IntVectors/PIC24F_Interrupt_Vectors.html.

Le système d'interruption des PIC24 fonctionne de façon très similaire à celui des PIC32MX on n'est donc pas dépaysé si on connais déjà ce dernier. Il y a cependant des différences, entre autre il n'y a pas de sous-priorité. Les niveaux de priorité sont de 0-15 pour le CPU mais les niveaux programmables par l'utilisateur sont de 0-7 car les niveaux 8-15 sont réservés aux exceptions (traps). La priorité est dans l'ordre numérique c'est à dire que la plus basse est 0 et la plus haute est 15. Ça peut sembler évident mais sur certains MCU c'est l'inverse.

Si 2 interruptions de priorité égales sont déclenchées en même temps c'est l'ordre dans la table des vecteurs qui détermine laquelle sera servie en premier, plus petit IRQ# en premier.

Le CPU ne réponds qu'aux interruptions de niveau supérieur au niveau pour lequel il est programmé. C'est à dire qu'il y a 3 bits dans le registre d'état (SR), IPL0, IPL1 et IPL2 qui contrôlent le niveau de réponse. le registre core control register (CORCON) contient un 4ième bit IPL4. Si IPL4 est réglé à 1 le CPU ne répond plus qu'aux exceptions. A la mise sous tension ou après un RESET matériel ou logiciel le niveau de réponse est à zéro.

Configuration des interruptions

Il y a plusieurs registres spéciaux associés à la configuration des interruptions:

  • INTCON1 Contrôle global des interruptions. Contient le bit NSTDI qui sert à activer/désactiver les interruptions multi-niveaux. Contient aussi les bits qui indiquent la source d'une exception.
  • INTCON2 Contient le bit ALTIVT. Lorsque ce bit est à 1 la table de vecteurs alternative est utilisée (débogage). Contient aussi le bit DISI qui indique que les interruptions sont temporairement désactivés pour un nombres de cycles déterminé par le contenu du registre DISICNT (utile dans les sections de code critiques). Contient aussi les bits INTnEP qui détermine la polarité de réponse des interruptions externes (transition positive/négative).
  • SRL<7:5> IPL2:IPL0 contrôle le niveau de réponse du CPU
  • CORCON<3> IPL3 contrôle le niveau de réponse du CPU
  • IECn ces SFR servent à activer/désactiver l'interruption sur un périphérique particulier. Chaque périphérique a 1 bit qui lui est assigné dans un de ces registres.
  • IPCn ces SFR servent à déterminer le niveau de priorité de l'interruption. Chaque périphérique a 3 bits qui lui sont assigné dans un de ces registres.
  • IFSn ces SFR servent d'indicateurs booléens pour les interruptions (interrupt flag). Chaque interruption a 1 bit qui lui est réservé dans 1 de ces registres.

Si on veut que le CPU réponde à toutes les interruptions on laisse les IPLx à zéro (valeur par défaut). Mais cette valeur peut-être modifier en cours d'exécution selon les besoins.

Pour configurer une interruption sur périphérique il faut:

  1. Déterminer à quel IPCn est associé le périphérique et programmer le niveau de priorité désiré. Niveau 4 par défaut.
  2. Déterminer à quel IFSn est associé le périphérique et remettre à zéro son bit IFS.
  3. Déterminer à quel IECn est associé le périphérique et mettre à 1 sont bit IE pour rendre active l'interruption.
Exemple: TIMER1, niveau d'interruption 7 (le plus élevé).

Création de la routine de service d'interruption

Les routines d'interruption (ISR) ne doivent avoir aucun arguments et ne retournent aucune valeur pour des raisons évident elles sont donc de la forme void isr(void). Pour qu'une fonction soit reconnue comme ISR ou gestionnaire d'exception il faut lui attribué l'attibut interrupt. Exemple:
L'attribut no_auto_psv réduit le préambule de l'interruption donc son délais de réponse (latency) en évitant la sauvegarde du registre PSV. Pour les MCU avec 16Ko de mémoire flash ou moins ce registre est toujours à zéro donc inutile de le sauvegarder. Même chose si l'application n'utilise pas la fonction program space visibility2.
Les indicateurs booléens d'interruptions ne sont pas remis à zéro automatiquement il faut le faire avant de quitter la routine ISR sinon l'interruption va se déclencher en boucle.

Il existe 2 macros pour simplifier la déclaration des routines d'interruptions:
_ISR
_ISRFAST
Dans l'exemple ci-haut j'aurais plus déclarer:
_ISRFAST _T1Interrupt(void);

Désactivation globale des interruptions

Contrairement aux autres MCU PIC, les PIC24 n'ont pas de bit GIE dans INTCON ou ailleurs. Pour désactiver les interruptions de niveau 0-6 temporairement ont peut utiliser l'instruction DISI en assembleur. Pour les désactiver de façon permanente on peut modifier le niveau de réponse du CPU avec la macro SET_CPU_IPL(ipl). En mettant ipl=7 toutes les interruptions sont désactivées.

Programme démo

Ajout d'une interface RS-232 au cube.

Ce démo utilise le cube de LED 4x4x4 avec la même animation que dans l'article de présentation du cube sauf que l'affichage du cube est dirigé par une interruption sur le TIMER1 à 200µsec d'intervalle. l'ensemble du cube est donc mise à jour environ 78 fois par seconde. De plus la variable anim_delay est incrémentée en boucle dans la même ISR et est utilisée pour contrôler la vitesse de défilement de l'animation. Avec la constante ANIM_DLY servant de modulo. Pour la valeur 1000 on a un intervalle 0,2 seconde entre chaque configuration. Le programme comprends en plus 2 autres interruptions, l'une pour la transmission des caractères sur UART1 et une autre pour la réception des caractères. Transmission et réception utilisent une file circulaire, rxbuff pour la réception et txbuff pour la transmission. A partir d'un ordinateur en utilisant un émulateur de terminal on peut arrêter l'animation en envoyant le caractère 's' et la redémarrer avec le caractère 'r'. Le MCU répond à chaque commande en envoyant le message " OK\n\r".


notes

1) Le family reference manual des PIC24F contients 66 sections disponibles ici.

2) la fonction program space visibility permet de rendre accessible en lecture une section de la mémoire flash en mémoire RAM. Cette fonction est utile lorsqu'il y a des tables de données en mémoire flash. Le registre PSVPAG détermine l'adresse de départ du segment flash à rendre visible dans la RAM. Cette adresse doit-être un multiple de 16Ko. Cette section sera visible dans la RAM à partir de l'adresse 0x8000. Lorsqu'on programme en 'C' le compilateur s'occupe de ces détails. Par exemple dans le démo ci-haut la chaîne de caractère " OK\n\r" est conservée en mémoire flash et le compilateur fait ce qu'il faut pour que UartPrint() puisse l'accéder via le program space visibility.

mise à jour firwmare cube 4x4x4

$
0
0

J'ai mis à jour le firwmare du PIC24F16KA101 qui contrôle le cube 4x4x4.
version 0.3

  • Les routines UART ne fonctionnaient pas bien il y avait des erreurs overrunà la réception.
  • Ajout de 3 animations supplémentaires.
  • À partir du PC l'interface UART permet de ralentir (s) et accélérer (f) les animations.
  • Les touches 1-4 permettent de passer d'une animation à l'autre.
    1. plane_sweep
    2. random_led
    3. drop
    4. running_led

code source

chandelle électronique

$
0
0

Ce matin je suis tombé sur cet article. L'auteur de ce blog a analysé le signal d'une chandelle électronique afin de reproduire l'algorithme. Une chandelle électronique est en fait une LED dont l'intensité varie de façon aléatoire de manière à imiter le scintillement d'une vrai chandelle. Je n'ai jamais utiliser une telle LED j'étais donc curieux de voir de quoi il en retourne. J'ai donc examiner le code de l'auteur du blog cpldcpu. Le code est écris en 'C' pour un ATtiny mais je me suis dis que c'était un projet idéal à exécuter sur le plus simple et économique des MCU disponible sur le marché, soit le PIC10F200. En m'inspirant du code source original j'ai écris ma propre version en MPASM pour le PIC10F200. Rien de plus simple comme montage, un PIC10F200 avec une LED jaune directement branchée sur la broche GP2.

J'ai utilisé le même PRNG basé sur un LFSR de 32 bits. Cependant j'ai fait 2 modifications. D'abord dans le code original il y a un bogue, la variable randflag n'est jamais remise à zéro à l'intérieur de la boucle while. Donc une fois qu'elle a été mise à 1 elle y reste et devient inutile.

L'algorithme original autorise des fluctuations d'intensité pouvant descendre jusqu'à 25% (si le bogue est corrigé1 sinon ça peut descendre jusqu'à zéro). J'ai essayé ça mais je n'étais pas satisfait du résultat. Je trouvais les écarts d'intensité trop grand par rapport à une vrai chandelle. J'ai donc modifier le programme de la manière suivante. D'abord je me suis débarrassé de randflag qui est inutile dans ma version. Deuxièmement j'ai fait en sorte que le rapport cyclique du PWM varie de 50% à 100% au lieu de 25% à 100%. Je trouve ainsi que l'effet est plus ressemblant à une chandelle.

Code source en MPASM

On a donc une modulateur d'intensité pwm_control avec une résolution de 4 bits . L'intensité est contrôlée par variation du rapport cyclique d'une onde rectangulaire dont la fréquence est d'environ 440hz. On a un générateur de nombres pseudo aléatoire lfsr_rand. Pour cette application on ne garde que les 5 bits les moins significatifs qu'on conserve dans rand_val. Le délais pour contrôler la fréquence du modulateur PWM est assuré par la minuterie TIMER0. l'intensité de la LED est modifiée à tous les 32 cycles PWM par la variable frame_cntr qui est incrémentée modulo 32. Pour la valeur du rapport cyclique pwm_val on procède comme suis. Si la valeur de rand_val est inférieur à 16 on utilise la valeur maximale soit 15 (LED à intensité maximale). Si La valeur est >=16 on divise rand_val par 2 et on utilise cette valeur comme rapport cyclique. Donc statistiquement l'intensité de la LED devrait-être à son maximum 50% du temps et pour les 50% restant n'importe quelle valeur entre 50% et 100%.


1) pour corriger le bogue du code 'C' disponible sur github remplacez la ligne:

if ((RAND&0x0c)!=0) randflag=1; // only update if valid 
par
if ((RAND&0x0c)!=0) randflag=1;else randflag=0; 

LED scintillante

$
0
0

Cet article fait suite au précédent intitulé chandelle électronique. Ce matin je me suis mis dans la tête de fabriquer une LED scintillante aussi ressemblante que possible à celles made in China. Je suis plutôt satisfait du résultat. Voici donc les instructions pour sa fabrication.

Le matériel nécessaire est réduit un PIC10F200 en format SOT23-6 et une LED 5mm (T1-3/4).

  1. programmation du PIC10F200, méthode de l'araignée. Soudez 5 fils sur les pattes 1,2,3,5 et 6. En vous servant de pinces de test dont l'autre extrémité est terminé par un broche qui peut-être insérée dans le connecteur du PICKIT 2 ou 3, reliez les fils au programmeur.
  2. Lorsque la programamtion est complétée, coupez les broches 1,3,6 du PIC10F200 à raz le boitiez et dessoudez des broches les autres fils. Ensuite coincez la LED à l'envers dans une pince et posez le PIC10F200 sur la base de la LED avec la broche 4 (GP2) en contact avec l'anode. La broche 2 (Vss) en contact avec la cathode. Soudez ces 2 broches.
  3. Coupez le fil d'anode de la LED juste au dessus de la soudure, conservez le bout de fil. Si vous avez un forêt de 0,7mm ou moins, percez un petit trou peut profond juste à côté de la broche Vdd. Entrez l'extrémité du fil coupé dans ce trou. Si vous n'avez pas fait de trou ce n'est pas essentiel, appuyez simplement le fil sur le côté de la broche Vdd et maintenez la en place avec du ruban adhésif. Soudez le fil à la broche Vdd.
    C'est terminé pour la soudure le résultat est le suivant.
  4. Solidifiez ça avec de l'époxy. Enroulez du ruban adhésif tout le tour du corps de la LED en le laissant dépasser de la base pour former un cavité qui va accueillir l'époxy. Remplissez la cavité d'époxy.
  5. Lorsque l'époxy est séchée enlevez le ruban adhésif et le travail est terminé. Assurez-vous que la broche soudée sur Vss est plus courte que celle sur Vdd. C'est la méthode habituelle pour identifier le négatif des LED. Le travail est terminé il ne reste qu'à brancher ça sur une alimentation 3 à 5 volt DC.
    Si on regarde la LED par le dessus, à moins de faire très attention, on ne se rend pas compte que la LED a été modifiée.

Court vidéo pour conclure ça.

générateur de nombre pseudo aléatoire

$
0
0

Je suis en train de lire A new kind of science de Stephen Wolfram. Au chapitre 4 il mentionne qu'il a fait l'expérience suivante.
soit un nombre entier N

  1. Si N est impaire on l'incrémente
  2. On multiplie N par 3 et on divise par 2
  3. On recommence à l'étape 1
C'est ce qu'on appelle une fonction itérative, la valeur de f(N)=f(N-1)*3/2

Wolfram mentionne que le bit le plus faible de cette fonction itérative est aléatoire. C'est à dire qu'il est impossible de prédire si le nombre à la position x de la suite de nombres générés par itération sera pair ou impaire.

Écrire une fonction pour multiplier un nombre par 3 pour ensuite le diviser par 2 est simple à réaliser même en assembleur sur un petit MCU comme le PIC10F200. Puisque les générateurs de nombres pseudo aléatoire sont très utiles en informatique j'ai voulu vérifier si je pouvais réellement obtenir une suite de bits aléatoires avec cette fonction. J'ai donc pris un PIC10F200 et un petit haut-parleur que j'ai branché sur GP2.

J'ai écris 4 versions de la fonction f(N)= f(N-1)*3/2 pour les de différentes grandeurs d'entiers, 8 bits, 16 bits, 24 bits et 32 bits.

façon simple de tester un PRNG

Pour vérifier si un PNRG est vraiment aléatoire il existe une méthode simple quoique pas très scientifique. On écoute la série de bits produits et si le son est du bruit blanc, (le bruit produit par une chute d'eau), alors le générateur est bon. Il ne faut pas distinguer de cycles dans le bruit produit. Seulement un shhhhhh uniforme.

Donc la boucle principale du programme appelle la fonction itérative et vérifie si le résultat est pair ou impaire. Si c'est pair la sortie GP2 est mise à 1 sinon elle est mise à zéro. Le TIMER0 est utiliser pour créer un délais entre chaque appel de la fonction itérative pour que le spectre sonore soit dans l'audible.

Résultat

  • fn_iter8b. Mauvais on a l'impression d'entendre une seule tonalité
  • fn_iter16b. Mieux mais encore mauvais. On entend une tonalité mais il y a du chirp.
  • fn_iter24b. Beaucoup mieux. Beaucoup de bruit blanc mais lorsqu'on est attentif on détecte des cycles. Bon pour une application pas trop exigeante.
  • fn_iter32b. Très bien. Qualité suffisante pour des jeux vidéo ou autres application non critique.

J'ai aussi tester la fonction lfsr_prng que j'ai utilisé pour la chandelle électronique. Le résultat est presque aussi bon que pour fn_iter32b. L'avantage du lfsr_prng est qu'il nécessite moins d'instructions. Disons que sur un MCU 16 ou 32 bits fn_iter32b serait un bon choix car le nombre d'instructions nécessaires serait réduit de 50% ou moins.

code source

xmos startKIT

$
0
0

Au mois d'octobre via un lien sur le site hackaday.com j'ai découvert l'existence d'une compagnie localisée à Bristol (G.B.) et ses MCU xcore. Le lien faisait référence à ceci. A ce moment j'ai jeté un coup d'œil et j'ai lu la documentation de présentation des xcore. Je me suis dit alors que puisque la carte startKITétait offerte gratuitement je pouvais bien m'inscrire pour en avoir une.

Il y a quelques jours j'ai trouvé le startKIT dans ma boite à lettres. gracieuseté de XMOS, jeune compagnie fondée en 2005. Le marché des microcontrôleurs est déjà saturé d'offres et occupé par de gros joueurs. Quel sont les chances d'une startup de réussir dans ce domaine? Mais XMOS a maintenant plus de 8 ans et ils sont encore là. D'après leur site la croissance des ventes est de 30% semestre après semestre et ils ont trouvé des investisseurs prêt à y mettre 14 millions supplémentaire.

De tout évidence le produit a rencontré un besoin. Mais les géants du domaine ont surement remarqué son existence et je ne serais pas surpris de voir Microchip mettre en marché avant la fin de 2014 un produit en mesure de concurrencer les xcores. Il y a déjà des rumeurs à propos de PIC32 fonctionnant à 400Mhz et multi-cores. Après le multithread et multi-core sur les CPU d'usage général on l'a maintenant sur les MCU.

Qu'est-ce qui distingue les xcores?

Il sont multi-thread et muli-core (32 bits). La compagnie XMOS parle plutôt de multi-core logique et de tuile (tile). Les cores logiques ne sont pas des cores complets comme ceux du propeller de Parallax. Sure une tuile il peut y avoir jusqu'à 8 core logique mais il n'y a qu'une seule Unité Arithmétique et Logique (UAL). Un seul thread est en exécution à un moment donné. Un core logique n'est en fait qu'un ensemble de registres de sorte qu'il n'est pas nécessaire de sauvegarder les états en passant d'un thread à l'autre. Il n'y a donc pas de délais de commutation lorsque l'exécution passe d'un core logiqueà l'autre. Conceptuellement c'est très semblable à ce que Intel appelle le multi-thread sur ses CPU.

J'ai parlé de tuile (tile). Celle-ci est en fait un core complet, UAL, céduleur, mémoire RAM, minuterie, commutateur xCONNECT, accès aux périphériques, etc. Les xcores les plus puissants on jusqu'à 4 tuiles avec 8 cores logique chacune pour un total de 32 threads indépendants. Ces MCU ont une performance de 400 à 1000 MIPS/tuile dépendant des modèles.

Ce n'est pas la seule particularité des xcores. En autre les périphériques simples comme les UART, SPI, I2C et PWM sont définis en software. Au niveau hardware il n'y a que des sérialiseur et désérialiseur. Avec un MCU qui fonctionne à 500Mhz définir ces périphériques en software ne pose aucun problème. Pour les fonctions analogiques il y a un bloc ADC sur certains modèles. Le développeur n'a cependant pas à se tracasser avec la programmation de ces périphériques puisque XMOS les fournis gratuitement sous le nom générique xSOFTip.

xCONNECT

le xCONNECT est une autre particularité des MCU xcore. Il s'agit de lien de communication commutés entre tuiles, core et MCU. Il est donc possible de créer un réseau de MCU. Imaginez la puissance de calcul d'une carte avec 64 MCU xcore reliés en réseau. Les liens xCONNECT peuvent fonctionner en mode lent en utilisant 2 fils ou en mode rapide en utilisant 5 fils. Et je le rappelle il s'agit d'un réseau commuté. Chaque tuile possède un commutateur qui redirige les paquets au besoin à l'interne vers les cores et les autres tuiles mais aussi vers l'externe. Si vous avez envie de vous fabriquer une machine pour générer des bitcoins un réseau de xcore serait une solution intéressante.

startKIT

Pour en revenir au startKIT il s'agit d'une carte de 90mm X 50mm, avec un MCU XS1-A8A-64-FB96. Ce MCU possède une seule tuile avec 8 core logiques et un bloc convertisseur Analogique/numérique 10bits. La tuile contient 64Ko de RAM. Cette RAM contient programme et données. Il n'y a pas de mémoire flash sur ces MCU, les programmes sont enregistrés dans une mémoire flash externe avec interface SPI. Celle qui est sur la carte a une capacité de 256Ko et contient un programme démo préinstallé. Il y a un bootloader en ROM qui au démarrage du MCU charge en RAM le programme à partir de la flash SPI externe.

On retrouve sur la carte 11 LEDs dont 9 forment une grille 3x3. Un bouton momentané et 2 zone tactiles à 4 segments chacune, slide X et slide Y.

Au niveau de la connectique il y a un connecteur micro-USB qui sert à alimenter la carte, la programmer et la déboguer. Sur le côté opposé un il y a connecteur de type PCIe qui est utilisé avec des cartes vendu par XMOS. A côté du connecteur USB il y a 2 rangés de trous (J3) pour installer un connecteur compatible avec le Rasberry Pi. Il y a d'autres rangés de trous pour installer d'autre connecteurs J2 pour les entrées ADC, J8 pour les interfaces xCONNECT, J7 pour les GPIO et J6 pour les alimentations.

xTIMEcomposer studio

L'environnement de développement xTime composer studio est disponible gratuitement à condition de s'enregistrer. Il est basé sur Eclipse et fonctionne sous Windows, Mac et Linux.

A ce moment ci je n'ai encore rien programmé pour cette carte, j'en suis encore à lire la documentation et à me familiariser avec l'IDE. Je me demande encore ce que je vais faire avec une carte aussi puissante.

xcore, rien de nouveau sous le soleil

$
0
0

Ce matin en suivant des liens je me suis retrouvé à lire cet article sur wikipedia. Voici un paragraphe extrait de cet article.

Each PP included its own memory of 4096 12-bit words. This memory served for both for I/O buffering and program storage, but the execution units were shared by 10 PPs, in a configuration called the Barrel and slot. This meant that the execution units (the "slot") would execute one instruction cycle from the first PP, then one instruction cycle from the second PP, etc. in a round robin fashion. This was done both to reduce costs, and because access to CP memory required 10 PP clock cycles: when a PP accesses CP memory, the data is available next time the PP receives its slot time.

En gros les PP (Peripheral Processor) sont des CPU qui gérait les E/S sur les CDC-6600. N'est-il pas frappant de voir que cet ordinateur des années 60s utilisait le même mécanisme que les xcores. C'est à dire un ensemble de registres qui maintient les états et qui partagent la même unité centrale. C'est le multi-threading des xcore discuté dans mon article précédent.

En fait la lecture de cet article nous révèle qu'il n'y a vraiment rien de nouveau sous le soleil en ce qui concerne les CPU. Le CPU du CDC-6600 datant de 1965 (presque 50 ans) avait déjà une architecture RISC avec register file. 8 registres de 60 bits pour les opérandes, 8 registres de 18 bits pour les adresses et 8 registres de 18 bits pour les incrémenteur (adressage relatif et branchements). Les CDC-7600 eu possédait une technologie très semblable au superscalaire d'Intel, c'est à dire que plusieurs instructions pouvaient être exécutées en parallèle. Son instruction stackétait l'équivalent de ce qu'on appelle aujourd'hui un pipeline.

Puisque tout ce qu'on retrouve dans les CPU d'aujourd'hui existait déjà il y a 50 ans, alors qu'est-ce qui à progressé? Essentiellement la technologie des circuits intégrés. Un CDC-6600 qui était une grosse armoire nécessitant un système de refroidissement au fréon aujourd'hui tiendrais sur une petit morceau de silicium et couterait 1 ou 2$.

Bien sur au niveau des détails il y a eu quelques améliorations mais l'essentiel était déjà là. Je n'oserai plus utiliser le qualificatif de moderne pour qualifier les technologies xcore, propeller, hyper-threading, superscalar etc. La seule chose qui est moderne est notre capacité à intégrer sur des échelles de plus en plus microscopique. Certains processeurs sont maintenant gravés en 22 nanomètre, alors que le CDC-6600 était fabriqué avec des transistors bi-jonctions au silicium. C'était le premier ordinateur à utiliser des transistors au silicium et avec son clockà 10Mhz c'était l'ordinateur le plus puissant sur le marché. Un programme écris en FORTRAN pouvait exécuté 0,5 MFLOPS, le super-ordinateur de son époque.


C'est parti!

$
0
0

Dans mon article de présentation du startKIT j'écrivais que les gros joueurs du marché MCU ne tarderaient surement pas à réagir à l'émergence de XMOS sur le marché en ajoutant des multi coeurs fonctionnant dans les centaines de Mhz à leur ligne de produits. L'année 2014 commence avec TI qui annonce des dual cores fonctionnant à 200Mhz, capable de 400MIPS. Les autres ne devraient pas être long à suivre.

On voit de plus en plus dans les automobiles des ordinateurs de bord avec affichage graphique LCD. Il y a donc une demande pour des processeurs plus puissants. En augmentant la puissance des microcontrôleurs et en leur donnant le multi coeurs et des gestionnaires de mémoire le nombre de MCU utilisés dans une automobile va diminuer. Au lieu d'avoir un MCU pour chaque système, freins ABS, contrôle d'admission, appareillages électriques, GPS, etc. Il deviendra possible d'intégrer toutes les fonctions dans une seule unité centrale. Les manufacturiers automobile utilisent déjà des systèmes d'exploitation temps réel comme QNX car pour ce types d'applications il faut un noyaux système temp réel. Les noyaux comme Linux ne sont pas appropriés.

Début novembre 2008 le Dr Tanembaum, inventeur du système micro-noyau MINIX, annonçait la réception d'une subvention de 2,5M d'euros pour avancer le développement de Minix 3. Le système Minix 3 cible particulièrement les systèmes embarqués (Embedded). Il y une convergence entre les systèmes d'exploitations comme Minix et l'utilisation de MCU de plus en plus puissant dans l'automobile et ailleurs.

se faire les dents sur XMOS startKIT

$
0
0

Il y a quelques semaines je présentais le startKIT de XMOS. Depuis j'ai lu pas mal de documentation et cette semaine je me suis fait les dents sur le startKIT en écrivant ma première application. Ça n'a pas été facile de m'adapter aux contraintes du langage XC mais avec de la persévérance j'y suis finalement arrivé.

le jeux de la vie

Le jeux de la vie est un automate cellulaire sur 2 dimensions imaginé par le mathématicien John H. Conway en 1970. Soit une grille ou chaque carré (cellule) peut prendre 2 couleurs, disons noir ou blanc. A chacune des cellules de la grille on applique un ensemble de règles simples et on conserve le nouvel état de la grille et recommence la même opération à partir de ce nouvel état. Lorsque Conway a imaginé cet automate les ordinateurs personnels n'existaient pas encore, chaque itération était calculé à la main et dessiné sur du papier quadrillé. Heureusement on n'en est plus là. C'est beaucoup plus intéressant de confier ce travail à un ordinateur et d'afficher le résultat de chaque itération à l'écran.

J'ai choisi le jeux de la vie pour me faire les dents sur le startKIT car c'est une programmation simple, du moins lorsque c'est écris en 'C' en single thread. Lorsqu'on doit apprendre une nouvelle façon de faire c'est plus difficile comme je l'ai constaté.

La grille est toroïdale et de dimension 36x26. Par toroïdale on entends ceci. Imaginez que l'écran de l'ordinateur est une feuille de papier et qu'on le pli en cylindre pour que le haut vienne touché au bas et qu'ensuite on étire ce cylindre pour que le 2 bouts se touchent. On obtient ainsi une tore et les cellules sont à la surface de cette tore. L'univers ainsi créé est sans rebords mais de surface limité. Ainsi lorsqu'un glider se déplace sur la grille, au lieu de disparaître lorsqu'il arrive à un rebord de l'écran il réapparait de l'autre côté.

Voici un vidéo de la simulation en action. Sur la ligne du bas s'affiche le nombre d'itérations.

Le calcul de chaque itération étant trop rapide j'ai ajouter une délais 0,5 seconde entre chaque itération.

schématique

Code source

Description

Le tableau video_buffer est la mémoire vidéo qui contient l'information à afficher à l'écran. Cette mémoire contient les indices des caractères dans la table font. life_buffer contient l'état de l'univers de l'automate cellulaire. Ce tableau est double car lors du calcul d'une itération la version originale ne peut-être modifiée. On écris donc le résultat dans l'autre moitié et on alterne entre les deux moitiés à chaque itération.

Dans la procédure principale on initialise la grille life_buffer en utilisant le générateur pseudo-hasard rand(). La variable timer t n'est utilisée que pour initialiser le générateur pseudo-hasard.

Le générateur de signal NTSC utilise 3 ports de 1 bit, ntsc_sync_port génère le signal de synchronisation. ntsc_blvl_port détermine le niveau noir et finalement ntsc_video_port est la sortie des pixels vidéo.

interface

Dans le langage XC une interface permet de définir un canal de communication entre 2 threads. Dans ce programme il y a 2 threads next_gen et ntsc_video. Chacun de ces threads s'exécute sur un core différent. Comme la variable video_buffer et contrôlée par le thread ntsc_video le thread next_gen ne peut y accéder. Donc lorsque next_gen a terminé le calcul d'une itération il envoie le nouveau tableau au thread ntsc_video par l'interface cells_array. Cette interface ne contient qu'une seule fonction CopyCellArray(). le nouveau tableau est copié dans video_buffer pendant le vertical retrace donc il n'y a pas de glitches dans l'affichage.

Dans la fonction ntsc_video() il y a un select. Les select en XC servent à écouter un canal de communication en attente d'information. Pour chaque fonction de l'interface il doit y avoir un case correspondant dans le select. Chaque case doit-être terminé par un break ou un return. Pour éviter que le thread ne bloque sur l'attente du message fournis par CopyCellArray() on utilise un

default:break;
. Le default permet de sortie du select et continuer l'exécution du thread. S'il n'y avait pas de cette option le générateur NTSC ne fonctionnerais pas.

Une interface est un canal client/serveur. C'est à dire qu'un des thread est le serveur c'est à dire celui qui attends les requêtes distribuées via les fonctions de l'interface. Le client lui évidemment envoie les requêtes.

Dans la fonction main(),

interface cells_array ca;
déclare une variable d'interface ca qui est passé en arguments aux 2 threads qui utilisent cette interface. Notez comment le paramètre d'interface est déclaré dans chaque thread. client pour next_gen() et server pour ntsc_video().

void next_gen(client interface cells_array ca)

void ntsc_video(out port sync_port,
out port blvl_port,
out port video_port,
server interface cells_array cells)
Les interfaces sont des canaux de communications point à point donc la variable ca ne peut-être passée en argument qu'à un seul thread client et un seul thread server. Si un serveur a plusieurs clients il faut déclarer une variable différente pour chaque client. l'ensemble de ces variables peut-être gardé dans un tableau qui est passé en argument au serveur.

Conclusion

Bien que de se familiariser avec une nouvelle technologie demande des efforts le succès est toujours gratifiant. La version 0.2 de ce programme pourrait utiliser les 2 règles tactiles et le bouton qui sont sur la carte startKIT pour créer une interface utilisateur servant à initialiser la grille au lieu de le faire avec une fonction au hasard. A suivre.

introduction aux interruptions

$
0
0

Dans cet article j'explique ce que sont les interruptions et leur utilisation.

Un programme est une suite d'instructions qui s'exécutent l'une à la suite de l'autre et qui éventuellement boucle au début dans un cycle sans fin. Exemple:


; clignotement d'un LED branché sur GP0
; code assembleur pour MCU PIC baseline
; GP0 configuré en sortie
main
bsf GPIO, GP0
call delay
bcf GPIO, GP0
call delay
goto main ; boucle infinie
Une interruption est un mécanisme par lequel le déroulement normal d'un programme est interrompu pour exécuter une autre programme de durée limitée, après quoi l'exécution du programme principal se poursuis à l'endroit ou il a été interrompu. Le programme exécuté lors de l'interruption s'appelle une routine de service d'interruption par la suite je vais utiliser l'acronyme anglophone ISR pour Interrupt Service Rroutine.

Quel est l'utilité des interruptions?

  1. Besoin de répondre aux événements en un temps déterminé. real time
  2. Libérer le processeur du besoin de sonder les périphériques pour voir s'il y a un événement. polling
Une application doit souvent répondre à des événements externes asynchrones, c'est à dire que ces événements peuvent se produire à n'importe quel point de l'exécution du programme principal. Une broche d'entrée change d'état et on doit réagir à ce changement dans un délais minimum. Si on n'a pas d'interruption comme c'est le cas des PIC baseline on est obligé de sonder la broche à un intervalle régulier suffisamment court pour répondre aux contraintes de temps de réponse. Ça complique le programme principal comme on l'a vu dans cet article. Non seulement ça complique la structure du programme principal mais le temps consacré à ces coups de sonde est du temps perdu lorsqu'il n'y a aucun événement.

À partir de cette constatation l'idée de l'interruption est apparu rapidement dans l'histoire des ordinateurs. Il y a donc à l'intérieur des microprocesseurs et microcontrôleurs des circuits dont la tâche est de surveiller les événements programmés pour générer une interruption. Lorsqu'un tel événement se produit le gestionnaire d'interruption déclenche le mécanisme qui va interrompre le programme en cours pour exécuter l'interruption.

Du plus simple au plus complexe

Le dans le cas le plus simple il n'y a qu'un seul vecteur d'interruption. C'est le cas des mcu PIC 8 bits midrange et enhenced midrange. l'ISR doit-être installé à l'adresse 0x04. Par contre beaucoup de processeurs disposent d'une ou plusieurs tables de vecteurs d'interruptions. Chaque entrée de la table contient l'adresse de début de la routine d'interruption correspondant à la source (périphérique interne ou événement externe) de l'interruption. Par exemple les mcu Atmel AtTiny13a possèdent une table de 10 vecteurs situé en début de mémoire flash. La première entrée de cette table, à l'adresse 0, n'est pas un vecteur d'interruption c'est l'adresse de la routine d'initialisation. Les autres vecteurs correspondent à des périphériques ou des interruptions externes (changement d'état sur une broche).

L'avantage d'avoir un vecteur pour chaque interruption est qu'il n'est pas nécessaire de vérifier la source de l'interruption. Dans le cas des PIC si plusieurs interruptions sont activés, il faut au début de l'ISR vérifier quel est la source de l'interruption en lisant les indicateurs booléen (interrupt flag) associés à chaque source d'interruption. Non seulement ça exige du code supplémentaire mais ça rallonge le délais de réponse. Un autre avantage est la priorisation des interruptions.

Priorité et multi niveaux

S'il y a plusieurs sources d'interruption. Celles-ci peuvent avoir des priorités différentes. Les interruptions de haute priorité doivent-être servies avant celles de plus basse priorité. Mais si une interruption de basse priorité est en cours d'exécution au moment ou survient une interruption de plus haute priorité comment faut-il gérer ça? Dans bien des cas on ne peut se permettre d'attendre la fin de l'exécution de l'ISR courante, il faut que cette première interruption soit interrompue pour céder la place à celle de plus haute priorité. C'est ce qu'on appelle le multi-niveaux. L'exécution d'une ISR est interrompue et reprendra lorsque celle de priorité supérieure se terminera. Certains processeurs possèdent des mécanismes de priorisation des interruptions, c'est le programmeur qui choisi la priorité de chaque interruption. Sur les PIC24/dsPIC30/dsPIC33 et PIC32MX il y a des registres spéciaux pour configurer la priorité de chaque interruption. Si 2 interruptions de même priorité se produisent au même instant celle qui a le plus petit numéro de vecteur est servie en premier. Dans le cas des AtTiny13a, la priorité est fixé par le fabriquant. C'est l'ordre du vecteur dans la table qui détermine sa priorité. Le vecteur 1 a la plus haute priorité et le 10 la plus faible. Sur les PIC midrange et enhenced midrange il n'y a pas de priorité puisqu'il n'y a qu'un seul vecteur d'interruption.

Interruptions en file

Si une interruption de priorité plus basse ou égale se produit pendant l'exécution d'une interruption on peut réduire le temps de réponse de l'interruption suivante si on évite de restaurer l'état initial avant d'exécuter l'interruption suivante. Certains mcu possèdent un mécanisme pour faire ça. Si ce mécanisme n'existe pas il y a du temps perdu à restaurer les états qui seront resauvegardés par l'interruption qui est en file d'attente.

délais de réponse et variabilité

Le délais de réponse à une interruption est un facteur important à considérer dans une application. A partir du moment ou un événement se produit et le moment où l'ISR commence à servir cette interruption détermine le de réponse de l'interruption (latency). D'abords lorsqu'un interruption se produit il faut sauvegarder les états du programme en cours, c'est à dire le pointeur ordinal et les autres registres qui sont susceptibles d'être altérés par l'exécution de l'ISR. Bien sur ces états doivent-être restaurés avant de quitter l'ISR. C'est états sont sauvegardés généralement sur la pile des retours ou parfois dans des registres qui sont des doubles des registres originaux (shadow registers). Les PIC enhenced midrange sauvegarde le PC sur la pile et les registres WREG,STATUS,FSRx dans des copies en fin de mémoire RAM (bank 31). Pour les PIC midrangeà l'exception du PC tous les autres états doivent-être sauvegardés par programmation au début de l'ISR ce qui rallonge le temps de réponse. En effet la sauvegarde/restauration des états ne peut-être considéré comme une réponse à l'interruption, ce n'est qu'un préambule.

La variabilité (jitter) est la variation du temps de réponse d'une interruption à l'autre. Habituellement un MCU termine l'exécution de l'instruction en cours avant de répondre à une interruption. Si les instructions n'ont pas toutes la même durée d'exécution il y aura une certaine variation dans le temps de réponse. Dans les applications temps réel l'ingénieur doit-être en mesure d'évaluer cette variabilité et s'assurer qu'elle ne déborde pas des seuils de tolérance de l'application.

Bonnes pratiques

Comme chaque interruption suspend l'exécution du programme principal, il est de bonne pratique de réduire le temps passé dans l'ISR. Plus une ISR est courte mieux c'est, surtout si elle se produit fréquemment. Une interruption de haute priorité devrait duré encore moins longtemps car elle bloque aussi la réponse aux interruptions de plus basse priorité.

Dans les applications qui requiert la plus faible consommation d'énergie il est de bonne pratique de construire entièrement l'application sur des interruptions. Après la phase d'initialisation le mcu est placé en mode basse consommation et se sont les interruptions qui le réactive. Lorsqu'une interruption a terminée son travail elle remet le mcu en basse énergie en quittant. Prenons l'exemple des odomètres de vélo qui fonctionnent sur une petite pile au lithium. l'odomètre tombe en sommeil 1 minute après avoir détecté la dernier événement. Lorsque le capteur sur roue détecte une rotation ou que l'utilisateur presse un bouton l'odomètre se réactive pour afficher les données.

xmos startKIT, génération signal VGA

$
0
0

Je poursuis mes expérimentations avec le startKIT de XMOS. Après avoir expérimenter la génération d'un signal vidéo NTSC en noir et blanc, je suis passé en VGA 16 couleurs. Dans ce premier test il s'agissait simplement de générer les signaux nécessaires à la synchronisation verticale, horizontale et vidéo. La mémoire vidéo est constitué d'une seule ligne que le générateur répète 480 fois. Le maximum de pixels par ligne que j'ai pu obtenir en programmation 'C' est de 508 par ligne. Comme 1 octet du tableau video_buffer contient l'information de 2 pixels, ça donne un tableau de 254 octets.

Schématique

Schématique de l'interface entre la carte startKIT et le moniteur VGA.

image générée par le programme test

La ligne sombre est un artéfact causé par la photographie.

Code source

fichier d'entête du générateur vidéo.

générateur vidéo VGA 16 couleurs

programme test, barres de couleurs

Limitation mémoire

Pour créer un bitmap de 480 lignes de 504 pixels il faudrait 480*504/2=120960 octets. Réduisant la résolution à 240x254 on a encore besoin de 30480 octets. Pour réduire le besoin de mémoire, on peut réduire la résolution encore plus ou encore utiliser un système de tuiles. Dans le prochain article je vais expliquer ce qu'est le système de tuiles et l'utiliser dans un démo bouncing balls. Le système de tuiles était la méthode utilisée au début des années 80, lorsque la mémoire était coûteuse et rare. Mon VIC-20 avec ses 5Ko de RAM pouvait afficher des graphiques de 160×160 pixels en 16 couleurs en utilisant un système de tuiles. Pour utiliser le système de tuiles je dois modifier le générateur vidéo et je serai peut-être obligé de sacrifier des pixels horizontalement.

mise à jour 2014-01-23

La version de l'interface physique entre la carte startKIT et le moniteur VGA présenté en début cet article a l'avantage d'être simple mais l'inconvénient que les couleurs de code 9 à 14 sont délavées. J'ai donc conçue une version améliorée qui corrige ce problème.


Références:
vga sur Psoc
vga timing

xmos startKIT, signal vga avec tuile

$
0
0

Cet article fait suite au précédent. J'explique ici comment fonctionne le système des tuiles afin d'économiser la mémoire vidéo.

Le système des tuiles était très utilisé dans les années 80 alors que les ordinateurs ne possédaient que quelqes Ko de mémoire RAM. Une quantité insuffisante pour conserver un bitmap complet de l'affichage vidéo. Dans cette chronique j'utilise un générateur vidéo VGA 16 couleurs d'une résolution de 640x480. Ce qui nécessiterais 153600 octets de RAM pour conserver un bitmap complet de l'affichage vidéo. Par contre en utilisant une table de tuiles de 8x8 pixels et un buffer vidéo contenant seulement des index de type unsigned char, la table peut contenir 256 tuiles différentes. La mémoire vidéo utilise donc 640/8*480/8=4800 octets par buffer. Il y a 2 buffer vidéo, video_buffer et working_buffer. Si on utilise une table de tuiles complète (256 tuiles) on a besoin de 32x356=8192 octets supplémentaire. Donc au total 17792 octets sont utilisés par le générateur vidéo sur les 64Ko disponibles. Énorme gain par rapport à 2 buffers de type bitmaps: 307200 octets.

Ce court vidéo montre une animation de 8 balles qui rebondissent sur la bande blanche qui entoure l'écran ainsi que l'une contre l'autre. Chaque balle d'une couleur utilise 4 tuiles.

Système de tuiles

Le système de tuiles est en fait très simple c'est le principe des polices de caractères à points fixes, mais plutôt que de contenir des caractères, la table contient (elle peut contenir aussi des caractères) des figures réparties sur plusieurs tuiles. Il suffit d'afficher ces groupes de tuiles dans l'ordre approprié pour former la figure désirée. Si on prend l'exemple du démo ci-haut. Les balles sont de 16x16 pixels. Puisque chaque tuile ne contient que 8x8 pixels, il faut 4 tuiles pour dessiner chaque balle. La mémoire vidéo est organisée comme une mémoire de terminal texte, elle ne contient de le numéro des tuiles et le générateur vidéo utilise ces numéros pour indexer la table des tuiles qui elle contient les pixels à afficher. Utilisé comme terminal texte ce système pourrait afficher 60 lignes de 80 caractères 8x8 mais tous les caractères doivent-être de la même couleur et le fond uniforme de couleur unique. Cependant on pourrait modifier facilement le générateur vidéo pour avoir des caractères en inverse vidéo.

représentation d'objets graphiques

Pour afficher des objets graphiques il faut créer des groupes de tuiles qui agencés ensemble dans le buffer vidéo formerons l'objet désiré.

Par exemple, la balle rouge est construite avec les tuiles {16,17,18,19}. Pour dessiner une balle rouge à l'écran à la position {4,20} on place dans video_buffer l'information suivante:


video_buffer[4][20]=16; // ligne 4, colonne 20
video_buffer[4][21]=17; // ligne 4, colonne 21
video_buffer[5][20]=18; // ligne 5, colonne 20
video_buffer[5][21]=19; // ligne 5, colonne 21
Pour former l'image d'une balle il faut que l'ordre et la position relative des tuiles soit respecter. Donc si on veut déplacer la balle vers la droite de 10 positions et de 5 position vers le bas, on ajoute 10 à la coordonnée x et 5 à la coordonnée y de chacune des tuiles du groupe.

On sauve énormément de mémoire avec ce système mais il comporte aussi de nombreuses limitations. D'abord il faut utiliser un fond d'écran uniforme. Dans le démo le fond est jaune pâle et donc les pixels des tuiles qui entourent le dessin des balles doivent-être de la même couleur sinon on voit un rectangle autour des balles. Si le fond d'écran n'était pas uniforme il faudrait des tuiles différentes pour chaque couleur de fond.

Le deuxième problème est qu'on ne peut couvrir la surface complète avec 256 tuiles, il y a donc des répétitions. On le constate dans les jeux vidéo du début des années 80 alors que les fonds d'écrans sont fait de tuiles qui se répètent.

Si vous observez attentivement les jeux vidéo de ces années vous remarquerez que les parties de l'écran ou se déplace les objets sont de couleur uniforme. Les corridors du labyrinthe ou se déplace PACMAN est de couleur uniforme et tous les objets qui apparaissent dans le labyrinthe sont construits avec des tuiles dont le fond est de même couleur.

Il est possible de modifier dynamiquement le contenu d'une tuile pour modifier les pixels de fond en fonction de la position de l'objet sur l'écran mais ces objets doivent n'avoir qu'une seule instance sinon chaque instance doit utiliser son propre groupe de tuiles. Comme dans ce démo ou il y a 4 tuiles pour chaque couleur de balle. Si on voulait créer 1 balle de chacune des 16 couleurs disponible il faudrait un total de 64 tuiles. Il ne faut pas oublier qu'on ne peut créer qu'un nombre limité de tuiles. Si l'index est de type unsigned char comme dans ce démo on est limité à 256 tuiles. Si on augmente trop le nombres de tuiles on perd tout avantage en terme de gain mémoire.

code source

le générateur vidéo a été créé comme un projet module nommé module-vga16t afin d'être facilement réutilisable dans d'autre applications. Il s'agit d'une première version qui sera vraisemblablement modifiée.

fichier d'entête vga16t_gen.h

fichier vga16t_gen.xc

Fichier d'entête tuiles.h

Fichier tuiles.xc

Fichier bouncing-balls.xc

640pixels en 25,6µsec

l'intervalle de temps disponible en VGA pour sortir les 640 pixels est de 25,6µsec. Même avec un core logique clocké à 100Mhz il ne serait pas possible de faire ça en 'C' si ce n'était d'une fonctionnalité des xcore, c'est à dire le buffered port. Voici le code qui sort les pixels.


int y = (line_cntr-23)/8;
int r = (line_cntr-23) % 8;
for (int x=0;x<SCREEN_WIDTH;x++){
int _8pixels=tuiles[video_buffer[y][x]][r];
rgb <: _8pixels;
}//for
Cette boucle for sort 1 ligne de 640 pixels. video_buffer contient les index des tuiles comme expiqué ci-haut. Ces index sont utilisés pour aller chercher l'informationn des pixels à afficher dans la table des tuiles. La table est organisé en 8 entiers de 32 bits par tuile. Chaque entier correspond à un segment de ligne de 8 pixels. En effet puisqu'on a besoin de 4 bits par pixel 32 bits peuvent contenir 8 pixels. La variable r indique quelle ligne de la tuile utiliser en correspondance avec la ligne vidéo courante. line_cntr est le numéro de ligne du frame vidéo (0-524). Puisque l'affichage commence à la ligne 23 de chaque frame on soustrait 23 pour avoir la bonne valeur de ligne dans buffer_video. Et puisque qu'une tuile s'étend sur 8 lignes on obtient r par (line_cntr-23) % 8. rgb est le port 4 bits qui envoie l'information pixel au moniteur. Comme vous le voyez on envoie l'entier au complet dans rgb car on a défini un buffered port de 32 bits pour rgb ainsi qu'un signal clock de 25Mhz.

Dans le fichier bouncing-balls.xc juste avant la fonction main on a les lignes:


out buffered port:32 rgb = XS1_PORT_4C; // J7-5:J7-8

clock pixclk= XS1_CLKBLK_1;
rgb est donc buffered sur 32 bits et on défini un signal d'horloge appellé pixclk.
À l'intérieur de main() on a les lignes suivantes:

configure_clock_rate(pixclk,XS1_TIMER_MHZ,4);
configure_out_port(rgb,pixclk,0);
start_clock(pixclk);
Pour le startKIT XS1_TIMER_MHZ vaut 100. 4 est un diviseur, donc le clock rate est de 100/4=25Mhz. On associe ensuite ce signal d'horloge au port rgb et finalement on démarre le signal. A partir de ce moment chaque fois qu'on envoie un entier de 32 bits vers rgb est il découpé en tranches de 4 bits à intervalle de 40 nsec sans que le core logique est besoin d'intervenir. Pendant ce temps la boucle for poursuit son exécution. Il n'y a donc aucun délais entre la envoie de chaque _8pixels.

Notez que les bits de poids faibles sortent en premier donc les bitmaps des tuiles est inversé comme dans un miroir (gauche-droite).


lien vers les sources des 2 projets:
projet bouncing-ball
projet module-vga16t

Viewing all 192 articles
Browse latest View live