mardi 16 avril 2013

Introduction au PIC32, partie 4, assembleur

Même si c'est beaucoup plus simple et rapide de programmer en 'C', il faut avoir une connaissance minimale des instructions machine et de l'architecture des processeurs sur lesquels on travaille. Je jette toujours un coup d'oeil sur le listing assembleur généré par le compilateur. Dans cette chronique je présente un bref aperçu des particularités de l'architecture MIPS32. A ce sujet le premier programme que j'ai écris dans la partie 1 de cette série me réservais des surprises. Si vous vous intéressez à la progammation assembleur sur les PIC32 vous devez au mininum télécharger les 3 documents suivants:

Particularités

Il y a 32 registres généraux de 32 bits. Le registre R0 par contre est en lecture seul et rapporte '0' à la lecture.
Le registre R31 est utilisé pour conserver l'adresse de retour lors de l'appel d'une sous-routine. Appel qui se fait par l'instruction JAL Jump and Link. L'adresse de retour n'est pas l'instruction qui suis le JAL mais la deuxième.
Il n'y a pas de pointeur de pile, le registre R29 est utilisé à cet effet par le compilateur gcc. Il n'y a pas d'instructions POP et PUSH pas plus qu'il n'y a d'auto-incrément/décrément du pointeur de pile. La pile est donc gérée entièrement en software. Il n'y a pas d'instruction RET pour sortir d'une sous-routine il faut utiliser un J RA où RA est le registre qui contient l'adresse de retour, habituellement R31. C'est cohérent avec les instructions JAL et JALR.
Même si les registres à l'exception de R0 peuvent-être utilisés à n'importe quel usage, il y a une convention à respecter si vous voulez que votre code soi compatible avec celui généré par le compilateur. A ce sujet consultez la carte quick reference

Saute, mais pas tout de suite!

Dans la première partie après avoir écris le premier programme dans lequel il y a une boucle de délais for(i=0;i<16000000;i++);, j'ai jeté un coup d'oeil au résultat en assembleur et quelque chose m'a laissé plutôt perplexe. Ça ressemblais à une erreur d'assemblage et pourtant le programme était fonctionnel!

Voici le code source en 'C'
Et voici le résultat en assembleur:

Ce qui m'a laissé perplexe c'est la traduction de la boucle for:
Le compilateur a inversé ma boucle,au lieu de compter de 0 à 16 000 000 il compte à rebours. Pas de problème avec ça. Par contre l'instruction à l'adresse 9D000B64 ressemble à un boucle infinie. En effet la première fois que cette instruction est exécutée le compteur V0 est à 15 999 999. Donc l'instruction branche à l'adresse 9D000B64 c'est à dire sur elle-même. Boucle infinie donc. Le mystère est resté entier jusqu'à ce que je me mette à la lecture du MIPS32 architecture for programmers Volume 2: instructions set.

Avant de faire un branchement ou un saut les cores MIPS32 exécutent l'instruction qui suis immédiatement l'instruction J. Donc dans cette exemple le compteur V0 est effectivement décrémenté par l'instruction à l'adresse 9D000B68.

Si vous faites la lecture de code assembleur pour l'architecture MIPS32, vous constaterez que le compilateur place toujours une instruction NOP après l'instruction J. Et pour cause puisque cette instruction est exécutée avant de faire le saut. Pour toute instruction B* (branch) ou J* (jump) il faut tenir compte de l'avis suivant:
Processor operation is UNPREDICTABLE if a branch, jump, ERET, DERET, or WAIT instruction is placed in the delay slot of a branch or jump. Si on place une des instructions mentionnée après le mot UNPREDICTABLE après une instruction de branchement conditiel ou un saut le résultat est imprévisible. NOP...NOP...NOP.

Autre particularité, il y a 2 types d'instructions addition, ADD,ADDU,ADDI et ADDIU et soustraction SUB et SUBU. Notez que dans le code assembleur ci-haut le compilateur a utilisé la version ADDIU pour additionner une constante à un registre. Les instructions qui n'ont pas de 'U' à la fin génèrent une exception lorsqu'il y a un débordement arithmétique et le résultat de l'opération n'est pas inscris dans le registre distination. Si vous n'avez pas besoin de gérer ces exceptions faites comme le compilateur 'C' et utilisez les versions se terminant par un 'U'. Le résultat est exactement le même pour les 2 types d'instructions et les indicateurs booléens Z,N et OV seront ajustés de la même façon. Comme mentionné dans la litérature fournis par MIPS le suffixe 'U' pour unsigned a été mal choisi, ça veut plutôt dire pas d'exception.

Fichier en assembleur dans un projet XC32

Pour des raisons de performances il se peut que certaines parties de votre projet soit en assembleur. Si c'est le cas évitez de farie ceci:

Si vous faites ça MPLABX va créé un fichier avec l'extension .s. Si vous voulez vous simplifier la vie créer un fichier vide (empty file...) et nommez le avec une extension .S. Un 'S' majuscule plutôt que minuscule. Les fichiers avec un 'S' majuscule sont traités par le pré-processeur du compilateur ce qui n'est pas le cas des fichiers avec un 's' minuscule. Le traitement par le pré-processeur 'C' va vous permettre d'utiliser les macros et fichiers d'entête des librairie 'C' sans vous casser la tête. Même si votre projet XC32 ne contient qu'un fichier assembleur .S le même d'initialisation du processeur que celui utilisé dans un projet écris en 'C' sera utilisé. Vous n'aurez donc pas plus d'initialisation à faire que si vous écriviez en 'C'.

exemple de projet

Je ne vous présenterai pas de code exemple personnel puisque Microchip en fourni un exemple que vous pouvez télécharger à partir de cette page.

dimanche 14 avril 2013

Introduction aux PIC32MX, partie 3, la mémoire

mémoire virtuelle

Les PIC32 ont une organisation mémoire différente des autres PIC. Du point de vue du core il n'y a qu'un espace d'adressage linéaire de 4Go. Cette espace englobe la mémoire flash, la RAM, le régistres de configureations, les périphériques. En fait tout ce qui est accessible par un programme. Cet espace mémoire n'existe pas réellement c'est une mémoire virtuelle. Pour faire le lien entre mémoire virtuelle et la mémoire flash, RAM, les SFRs, les registres de configuration de démarrage et le reste il y a un circuit qu'on appelle un gestionnaire de mémoire. Dans le cas des PIC32 ce gestionnaire est un table de transcription fixe. Cette table convertie l'adresse virtuelle en addresse physique.

La mémoire virtuelle elle-même est divisée en segments. Voici la représentation qu'en fait Microchip dans son manuel de spécification, section 3.

Seul les segments 0 et 1 sont utilisés sur les PIC32. Ces 2 segments pointent vers la même mémoire physique, la seule différence est que le segment 1 n'utilise pas la mémoire cache alors que le 0 peut l'utiliser.

2 modes de fonctionnement

Vous-vous demandez peut-être si c'est important de connaître ces détails, dans les parties 1 et 2 de cette introduction on s'est très bien passé de cette information. Et si vous rester au niveau de base vous n'avez en effet pas besoin de connaître ces détails. Au démarrage le MCU est en mode noyau et y reste à moins que le programme d'initialisation le configure pour qu'il passe en mode utilisateur.

Mais qu'est-ce au juste que ces modes de fonctionnement. Comme tous les processeurs avancés les PIC32 ont 2 niveaux de fonctionnement, le mode noyau et le mode utilisateur. Ces modes différents existent pour proteger le système. En mode noyau (kernel), le programme a accès à toutes les instructions et à tout l'espace mémoire. En mode utilisateur (user), le programme n'est pas autorisé à accéder aux périphériques et à la mémoire réservé au noyau. De plus certaines instructions sont interditent d'exécution. Si un programme utilisateur essaie d'exécuter une instruction interdite ou d'accéder un périphérique une exception est déclenchée, le MCU repasse en mode noyau et la routine qui gère ce type d'exception est exécutée. Ce qui ce passe à ce moment dépend entièrement du logiciel noyau. le MCU peut-être réinitialisé, entré dans une boucle infinie, afficher un message d'erreur, etc. D'une façon ou d'une autre le progamme utilisateur est interrompu. Ce genre de protection est utilisé sur tous les systèmes d'exploitations modernes. Par exemple lorsqu'on parle du noyau Linux, on parle justement de la partie du système d'exploitation qui est protégée contre les accès utilisateurs et qui contrôle le système à la base. Seul le noyau a accès aux ressources du système. Mais alors comment les programmes utilisateurs font pour utilisés les périphériques? A travers à appel au noyau qui porte le nom de SYSCALL. Mais cette question dépasse le cadre de cette chronique.

Les PIC32 possèdent donc tous les mécanismes nécessaires pour faire tourner un système d'exploitation protégé. D'ailleurs certains font tourner un système BSD unix sur des platines de développement duinomite.

exécution en mode utilisateur

Puisqu'il y a 2 modes de fonctionnement il faut que la mémoire aussi bien flash que RAM soit partionnée entre noyau et utilisateur. Comme je le mentionnais plus haut, au démarrage le MCU est en mode noyau et toute la mémoire lui est réservée. Supposons qu'à ce moment on fait passé le MCU en mode utilisateur c'est le plantage assuré car aucune mémoire n'a été réservé pour l'utilisateur. Donc avant de passer en mode utilisateur il faut partionner la mémoire flash et la mémoire RAM pour en attribuer une partie au mode utilisateur. Il y a plusieurs registres qui permette cette configuration.

  • BMXCON, gère les paramètres d'exceptions, et de mémoire cache. On peut l'ignorer pour le moment.
  • BMXPUPBA, addresse de début de la partition mémoire flash réservée aux programmes utilisateur.
  • BMXDUDBA, adresse de début de la partition mémoire RAM réservée aux données utilisateurs.
  • BMXDKPBA, adresse de début de la partition mémoire RAM réservée aux programmes noyau exécutant en RAM.
  • BMXDUPBA, adresse de début de la partition mémoire RAM réservée au programmes utlisateurs exécutants en RAM.
  • BMXPFMSZ, dimension de la mémoire flash en Ko.
  • BMXDRMSZ, dimension de la mémoire RAM en Ko.
  • BMXBOOTSZ, dimension de la mémoire flash du secteur boot.
Le contenu des registres BMXxxxBA sont des valeurs relative au début du segment. Les partitions commencent sur des multiples de 2 Ko. Donc pour le PIC32MX110F016B qui n'a que 4Ko de RAM on ne peut que créer 2 partitions RAM et les partitions noyau et utilisateurs ne peuvent être superposées.

programme démo

Ce programme démo montre comment exécuter une sous-routine en mémoire RAM. Le MCU demeure en mode noyau mais la mémoire RAM est partitionnée en 2 partie de 2Ko chacune. La partie du bas qui commence à l'adresse virtuelle 0xA0000000 est réservée au données et la partie du haut à l'adresse virtuelle 0xA0000800 est réservée pour l'exécution de routines en mémoire RAM.

J'ai simplement repris le programme de la partie 2 de cette introcution au PIC32MX et apporté quelques modifications. La première chose à faire est de partitionner la RAM au début du programme comme indiqué ici. Il est important lorsqu'on cré la partition BMXDKPBA (partition exécution en mémoire RAM pour le noyau) de définir BMXDUDBA et BMXDUPBA à une valeur supérieur à BMXDKPBA sinon le programme va planté. Les partitions utilisateurs sont au dessus des partitions noyau. Inclure dans votre programme source:

#include <sys/attribs.h>
Pour qu'une fonction soient copiée dans la mémoire RAM au démarrage du MCU il faut lui donner un attribu __longramfunc__ comme ici.
On peut aussi donné un attribu __ramfunc__ à une fonction en RAM mais celle-ci ne pourra-être appellée que par une autre fonction qui est en RAM et non par une qui est en mémoire FLASH. Dans ce démo comme la function square() est appellée par la prcédure main() qui est en mémoire flash elle doit avoir l'attribu __longramfunc__.

linker script

Malheureusement ceci ne suffit pas. Pour que ça fonctionne il faut modifier les scripts du lieur ld.exe, le programme qui est appellé par l'IDE pour créer l'exécutable final. Les scripts utilisés par défaut réserve toute la mémoire RAM pour les données. Il faut donc en créer deux autres pour informer le lieur que la mémoire RAM est maintenant partionnée et que les functions qui sont dans les sections *.ramfunc* doivent-être préparée pour copie dans la RAM.

Copiez les 2 fichiers suivant dans votre dossier projet *.X.

  • C:\Program Files (x86)\Microchip\xc32\v1.20\pic32mx\lib\ldscripts\elf32pic32mx.x
  • C:\Program Files (x86)\Microchip\xc32\v1.20\pic32mx\lib\proc\32MX110F016B\procdefs.ld
Dans la vue Projects de MPLABX cliquez avec le bouton droit sur le dossier linker Files et choississez Add existing Item...
Ajoutez les 2 fichiers que vous venez de copier. Maintenant qu'ils sont ajouter au projet ouvrez-les dans l'éditeur.

Commençons par prodefs.ld à la ligne 9 mettez en commentaire les 2 lignes OPTIONAL comme suit. n'utilisez pas le // pour mettre en commentaire le linker ne reconnait pas cette syntaxe. Descendez maintenant à la ligne 53 section MEMORY. Modifiez la ligne 53 et ajotutez en une autre. le kseg1_data_mem avait une longueur de 0x1000 on la réduit à 0x800.
On a ajouté un nouveau segment kseg1_prog_mem. La mémoire RAM est maintenant partionnée en 2 part égales de 2Ko.

Maintenant on va modifier le fichier elf32pic32mx.x pour y définir une nouvelle section mémoire appellée .ramfunc. Après la ligne 43, apreès la définition de .app_excpt, insérez la section suivante:

Compiler et tester

Maintenant il ne vous reste plus qu'à compiler le projet et à l'injecter dans le PIC32MX110F016B. Avant de démarrer le MCU lancer Terminal et connectez-vous au port série à 115200BAUD. Réinitialisez le MCU.

La LED branchée sur PB6 devrait clignoter. Les 2 autres devraient être éteintes mais vous pouvez basculer leur état à partir du terminial avec les touches 'r' et 'b' comme dans la partie 2. De plus si vous saisissez un nombre entier dans le terminal suivit de la touche ENTER le carré de ce nombre sera affiché dans le terminal. La fonction carre() s'exécute dans la mémoire RAM et non FLASH.

Qu'elle différence ça fait que la fonction s'exécute en RAM? C'est plus rapide que la mémoire flash. Dans le cas de ce démo ça ne fait aucune différence mais il peut y avoir de bonnes raisons d'exécuter en RAM plutôt qu'en flash. Si vous utilisez par exemple un MCU avec 128Ko de RAM et que vous installez RetroBSD dessus vos programmes chargés à partir d'une carte SD seront exécutés en RAM. Une méthode de développement qui est intéressante consiste à installer un petit programme moniteur sur le MCU et de faire du développement en connectant le MCU au PC via un port série. Le moniteur est conçu pour recevoir des fonctions compilées, les installés et exécutés en RAM. De cette façon on peut vérifier la validité du code sans avoir à l'inscrire en flash, réduisant l'usure de celle-ci. C'est aussi plus rapide et plus souple que le cycle compilation + flash.

Présentement je serais tenté d'installer un système FORTH sur un PIC32MX et de faire du développement interactif de cette façon.

mardi 9 avril 2013

Introductuion au PIC32, partie 2

Quelques précisions

Le datasheet intitulé PIC32MX1xx/2xx family datasheet qu'on retrouve en haut de cette page n'est qu'un résumé, pour chaque section il y a un autre document PDF disponible plus bas sur cette même page. Heureusement grâce aux librairies on n'a pas besoin de lire toute cette documentation en détail, il suffit de si référer pour éclaircir certains points lorsque la lecture des fichiers d'entête n'est pas suffisante.

En ce qui concerne la fréquence maximale, d'après la page mentionnée ci-haut la fréquence maximale des MCU des familles PIC32MX1xx/2xx est de 40Mhz pourtant dans le datasheet à la page 1 c'est indiqué que le core peut fonctionné à 50Mhz. Que faut-il croire? Quoi qu'il en soit j'ai configuré le PLL pour 50Mhz et ça semble fonctionné sans problème pour le moment mais ça ne veut rien dire, pour un programme plus complexe ça pourrait posé problème, c'est donc à surveiller.

En bas de cette page il y a des notes applicatives avec le code source disponible. Inutile de téléchargé le document pdf car il est inclus avec la package source.

J'ai oublié de mentionner certains faits importants à propos des PIC32 dans la première partie. Premièrement ces MCU fonctionnent à une tension maximale de 3,6 Volt et le core lui-même fonctionne à 1,8 Volt. Il y a donc à l'intérieur du MCU un régulateur de tension de type LDO (Low Drop Output) et la broche marquée Vcap (20 sur les DIL-28) est la sortie de ce régulateur. Il faut mettre un condensateur de 100nF entre les broches 19 (Vss) et 20 (Vcap) le plus prêt possible des broches. J'ai vu sur certains schéma un condensateur de 10uF, mais ce n'est pas nécessaire. Si vous voulez mettre un électrolytique entre 19 et 20 mettez quand même un 100nF céramique. Voir la section Quickstart dans le datasheet.

Les broches 26 et 27 nommées AVss et AVdd correspondent à l'alimentation pour les circuits analogiques, comparateurs et convertisseur A/N. Encore une fois on doit-mettre un condensateur 100nF entre 26 et 27 le plus près possible des broches. De plus au lieu de raccorder la broche 27 (AVdd) directement à V+ de l'alimentation on peut mettre une petite résistance de 10 ohm pour améliorer le filtrage. Un MCU qui fonctionne à 50Mhz ça produit beaucoup de fluatuations parasites sur l'alimentation.

Certaines broches tolèrent une tension de 5 volt. Celle-ci sont indiquées en gris sur l'image suivante:

Certaines sorties numériques peuvent-être configurées en mode drain ouvert. En plus des résistances pullup configurables il y a aussi des pulldown configurables. Voir la section I/O ports du datasheet à ce sujet.

Les entrées/sorties numériques ont 3 registres associés SET, CLEAR, INVERT. Ces registres fonctionnent de la façon suivante, les bits qui sont mis a 1 sont affectés par l'opération ceux qui sont à zéro demeurent inchangés. Pour chacune de ces fonctions il y a une macro. Dans le programme présenté dans la première partie, la macro mPORTBToggleBits() prend simplement la valeur qu'on lui donne en argument pour la mettre dans le registre INVERT. les 2 autres macros mPORTBSetBits() et mPORTBClearBits() font la même chose pour les registres SET et CLEAR. La première mets les bits sélectionnés à Vdd et la deuxième les mets à Vss. Donc dans le programme de la première partie mPORTBToggleBits(BIT_6|BIT_7|BIT_8) inverse ces bits chaque fois qu'elle est invoquée.

Il y a dans le PIC32MX110F016B beaucoup plus de périphériques qu'il n'y a de broches. On ne peut donc pas tous les utilisés en même temps. Associé à chaque port il y a un mutliplexeur d'entrées et un autre pour les sorties. Le rôle de ces multiplexeurs est d'assigner les périphérique aux broches. La déclaration des macros nécessaires à cette configuration se trouve dans le fichier pps.h (Peripheral Pin Select). Dans cette deuxième partie nous allons configurer le périphérique UART2 sur les broche PB10 (21) et PB11 (22).

Matériel requis

En plus du matériel de la première partie on a besoin d'un adapteur USB - rs232 - TTL. Si vous avez déjà un adapteur USB-RS232 il ne vous manque que l'adapteur de niveaux de tension entre celui-ci et le PIC. Le programme démo de cette partie va communiqué avec le PC hôte dans les 2 sens. A la réinitialisation il va lui envoyé le message "hello from blinky!". Ensuite il va répondre à des commandes pour allumer et éteindre les LEDS.

interface UART

Un nouvel élément est apparut au début de la procédure main. SYSTEMConfig() permet d'optimiser la configuration du MCU en fonction de la fréquece Fsys. Le premier argument est la fréquence en Hertz et le deuxième argument représente des valeurs de configurations combinées avec l'opérateur booléen OU. Il y a 3 paramètres qui peuvent-être ajustés. SYS_CFG_WAIT_STATES configure les cycles d'attente pour l'accès à la mémoire flash. Cette mémoire a une vitesse d'accès maximale de 30Mhz donc si le MCU fonctionne à plus de 30Mhz il faut insérer des cycles d'attente entre chaque lecture de la mémoire flash. SYS_CFG_PB_BUS sert à ajuster le diviseur de l'horloge du bus périphérique qui est limité à 80Mhz. Utile donc seulement pour les MCU qui fonctionnent au delà de cette fréquence. SYS_CFG_PCACHE configure la mémoire cache pre-fetch ceci ne concerne pas vraiment les 1xx/2xx car ils ne possèdent pas de cache pre-fetch seul des 3xx/4xx/5xx/6xx/7xx ont une telle mémoire. Vous trouverez plus d'information à ce sujet dans le fichier d'entête system.h et la documentation fournie par Microchip.

Voyons voir maintenant l'assignation des périphériques aux broches du MCU. N'importe quel périphérique ne peut pas être assigné à n'importe quel broche, il y a des groupes de périphériques. Chaque groupe correspond à un multiplexeur. Il a 4 multiplexeurs d'entrées et 4 pour les sorties. La façon la plus rapide de savoir sur quel broche on peut assigné un périphérique particulié est de regarder dans le fichier d'entête pps.h. Nous voulons utilisé le UART2. Commençon par la sortie TX. En ce qui concerne le PPS les périphériques s'appelle des fonctions. On commence donc par rechercher à quel multiplexeur (groupe) appartient la fonction U2TX et découvre cette fonction dans le groupe 4, OUT_FN_PPS4_U2TX. OUT pour sortie, FN pour fonction, PPS4 le multiplexeur, et U2TX la fonction. Il suffit ensuite de choisir sur quel broche du groupe OUT_PIN_PSS4_ on veut assigné cette fonction. Dans notre programme on a choisi OUT_PIN_PPS4_RBP10. Donc on a dans le programme:

PPSOutput(4, RPB10, U2TX);
Le premier argument est le numéro du multiplexeur, le deuxième la broche et le 3ième la fonction
Si on regarde pour U2RX on a que le groupe est 2 et parmis les broches d'entrée disponible on a IN_PIN_PSS2_RBP11. Donc dans le programme on a:
PPSInput(2, U2RX, RPB10);
Ici le premier argument est le groupe, le deuxième la fonction et le 3ième la broche. Pourquoi est-ce que ce n'est pas dans le même ordre que pour PPSOutput? mystère!.

Maintenant que le périphérique a été assigné à des broches il faut le configuré. On consulte uart.h. Avec un nom comme UARTConfigure() on ne risque pas de se tromper c'est surement ce qu'il nous faut. Le premier argument est l'id du périphique ici UART2 et le deuxième est le mode de fonctionnement UART_ENABLE_PINS_TX_RX_ONLY, on n'utilise pas les lignes CTS et RTS seulement TX et RX. Maintenant il faut déterminé le nombre de bits , la parité et les stops.

UARTSetLineControl(UART2, UART_DATA_SIZE_8_BITS | UART_PARITY_NONE | UART_STOP_BITS_1);
On a choisi 8 bits, pas de parité et 1 seul stop bit.
La vitesse de communication est sélectionnée avec:
UARTSetDataRate(UART2, mGetPeripheralClock(), BAUD_RATE);
la fréquence du bus périphérique est nécessaire pour programmer le baud rate generator, d'ou le deuxième argument mGetPeripheralClock(). BAUD_RATE est défini à 38400 plus haut dans le fichier.
Finalement il ne reste qu'à activer le périphérique avec UARTEnable(UART2, UART_ENABLE_FLAGS(UART_PERIPHERAL | UART_RX | UART_TX)); la transmission et la réception sont activés indépendemment l'un de l'autre.

Une partie du code de ce démo a été emprunté sur le forum de eevblog. C'est le cas de la function serial_print(). Dans la boucle while externe qui transmet le contenu du buffer, on attend que le périphérique soit disponible pour la transmission, while(!UARTTransmitterIsReady(UART2)); et on transmet un caractère à la fois, UARTSendDataByte(UART2, *buffer++);. On sort de la bouche while externe lorsque le caractère est nul. Finalement on transmet un CRLF pour terminer la ligne.

Avant de réinitialisé le MCU utilisé un émulateur de terminal (il s'appelle simplement terminal dans windows 7) et connectez le au port sur lequel est branché le MCU. Au démarrage le programme envoie le message "hello from blinky!" et ensuite allume la LED qui est sur PB6 et dans la boucle while attend simplement une commande. Utilisez les touches 'r' et 'b'. Il n'y a que ces 2 commandes. 'r' bascule l'état de la LED branchée à PB7 et 'b' celle sur PB8.

Formidable nous venons de tuer un moustique avec un bazooka! En pratique ce genre de micro-contrôleur est assé puissant pour hébergé un petit système d'exploitation et dans un boitier plus compacte que le DIL-28 ferait très bien pour fabriquer un lecteur MP3 et autre gadget plus exigeant que de faire clignoter des LEDs. On pourrait l'utiliser pour fabriquer un petit ordinateur dans le style 1980 mais qui serait dopé comme un cycliste professionel. A ce sujet je vous invite à consulter la page de Goeff Graham au sujet du maximite, construit avec un PIC32MX795F512.