Rasprinter

Bonjour à toutes et à tous !

Entrons directement dans le vif du sujet, car il y a beaucoup de choses à dire.

Cela fait plusieurs mois que je travaille sur un projet visant à apporter de nouvelles fonctionnalités à mon imprimante. Il s’agit d’une imprimante multifonction assez classique, qui peut imprimer, numériser et photocopier. Jusque-là, cela suffisait, surtout lorsque je vivais dans un logement qui m’imposait d’avoir l’imprimante et l’ordinateur sur le même bureau (et donc à portée de câble USB).

Les déménagements dans des logements plus spacieux et le partage de l’imprimante ont apporté une contrainte principale : la nécessité de déplacer l’ordinateur vers l’imprimante (ou réciproquement) pour pouvoir numériser ou imprimer. Si je déplace un ordinateur portable jusqu’à l’imprimante, je me retrouve proche d’elle mais dans une position inconfortable pour l’utiliser. Inconfort d’autant plus accentué si l’ordinateur est lourd et que le pavé tactile est capricieux (i.e. presque tous les pavés tactiles qui m’ont été donné d’utiliser).

Pour résoudre ces contraintes j’ai donc décidé de m’atteler à un projet, baptisé Rasprinter.

Qu’est-ce que Rasprinter ?

Il s’agit d’une Raspberry Pi, connectée à l’imprimante et au réseau local, qui fait office d’intermédiaire entre l’utilisateur et l’imprimante. Debian étant une distribution GNU/Linux axée serveur, son dérivé, Raspbian, est donc tout désigné pour l’usage que je vais en faire.

Pour l’impression à distance, il existe déjà des serveurs d’impression faciles à mettre en place (ce que j’ai fait). Reste à trouver une solution pour la numérisation à distance.

Il existe une API libre pour la gestion des scanners, nommée SANE. Il s’agit d’une librairie qui prend en charge plusieurs scanners et permet de les piloter depuis une machine bénéficiant d’un OS type Unix (GNU/Linux, BSD, etc). Elle est déjà implémentée dans bon nombre de logiciels de numérisation, comme Simple Scan. XSane permet la numérisation à distance via un serveur saned mais je n’ai jusque-là pas réussi à en mettre un en place. De plus, cela impose de faire plusieurs voyages entre l’ordinateur et l’imprimante à chaque changement de page ou de document.

J’ai donc opté pour une solution visant à ajouter un petit écran tactile sur le Raspberry Pi pour piloter la numérisation en restant à côté de l’imprimante sans s’encombrer d’un ordinateur. L’écran que j’ai choisi a cependant une contrainte : il ne permet d’afficher que via le framebuffer de Raspbian, c’est à dire qu’il ne propose pas le rendu via la puce graphique du Raspberry Pi et empêche donc l’utilisation de certaines librairies graphiques. Une librairie permettant le rendu via le framebuffer est SDL, une librairie multimédia très largement utilisée. C’est donc cette librairie que j’ai décidé d’utiliser pour l’interface graphique (dans son ancienne version, la version 2 ne permettant plus d’utiliser le framebuffer).

J’ai également fait appel à la librairie GraphicsMagick, un fork de la librairie ImageMagick, pour permettre la conversion des images scannées brutes en d’autres formats (comme la fusion des images dans un document PDF).

Essais et mise en oeuvre

La première fonctionnalité à tester fut l’utilisation de l’API de SANE pour la numérisation. Le site de SANE liste de manière détaillée les périphériques pris en charge par l’API. Mon imprimante étant officiellement prise en charge et Simple Scan offrant un rendu plus que correct, je n’avais pas de raison de douter du bon fonctionnement de l’API avec elle. Après quelques essais, j’ai pu réaliser un programme qui initialise la librairie, liste les périphériques et se connecte au premier qu’elle trouve (si elle n’en trouve aucun, le programme s’arrête) avant de lancer une numérisation et d’enregistrer le résultat dans une image. Pour le format de l’image, j’ai décidé de m’orienter vers Portable pixmap, un format d’image brute, sans compression, assez simple à implanter dans un programme. Les images sont nommées en fonction de la date et l’heure, dans un format proche d’ISO 8601 (que j’encourage vivement à utiliser !). Après quelques tâtonnements, j’ai finalement pu obtenir un rendu correct (mais… on va y revenir) d’un document numérisé.

J’ai donc intégré cela à mon programme. Programme qui se base sur une machine à états, chaque état désignant une étape dans le processus de numérisation et un écran différent à proposer à l’utilisateur. C’est à partir de ce moment-là que j’ai commencé à mettre en place l’interface utilisateur, en concevant les différents écrans et les actions permettant de se déplacer d’une étape à l’autre. Concrètement, l’interface est très simpliste et se compose de zones cliquables correspondant à une action à effectuer. La procédure est implantée comme suit :

  • un écran permettant de lancer la numérisation d’un nouveau document. Un simple « clic » (il s’agit d’un écran tactile) à n’importe quel endroit de l’écran permet de démarrer la procédure ;
  • un écran de transition, avertissant l’utilisateur que la numérisation est en cours (sans compter le bruit du scanneur…). L’utilisateur ne peut pas passer cet écran manuellement, c’est le programme qui passe à l’écran suivant à la fin de la numérisation de la page ;
  • un écran d’aperçu pour montrer à l’utilisateur le rendu de la numérisation pour qu’il puisse vérifier si celle-ci est correcte. Comme pour le premier écran, un simple « clic » permet de le quitter ;
  • un écran offrant deux options : enregistrer toutes les pages numérisées dans un fichier PDF ou numériser une autre page. Le programme garde une trace de toutes les numérisations tant que le document n’a pas été sauvegardé ;
  • un écran de transition pour avertir l’utilisateur que l’enregistrement du document est en cours. Cet écran transite automatiquement vers l’écran de départ pour permettre le lancement de la numérisation d’un nouveau document.
  • un écran dédié à l’affichage d’un message d’erreur en cas de problème (non complètement implanté).

Vous noterez que je parle de « clics  » pour un écran tactile. En fait, pour la gestion d’un contact sur l’écran, cette version de SDL n’offre que la gestion de la souris, ce qui exclue la possibilité d’actions à plusieurs doigts (ce que l’écran choisi, un PiTFT 2.8″ capacitif, ne supporte pas de toute façon). Ce qui est bien dommage car, l’écran étant assez petit, l’aperçu ne permet de voir qu’une version très réduite de l’image ce qui empêche de constater d’éventuels défauts de l’image numérisée. La possibilité de zoomer l’image aurait été la bienvenue.

Enfin, j’ai procédé à la prise en main de GraphicsMagick pour la conversion des images pixmap en un fichier PDF. GraphicsMagick, à l’instar d’ImageMagick, est un framework très puissant qui mérite bien son nom. À l’aide de fonctions et/ou de commandes très simples, il est possible d’effectuer toutes sortes d’opérations très intéressantes sur des images, quelles qu’elles soient. Ici, nous n’utiliserons qu’une seule des possibilités de cette librairie : la conversion. En un code très concis, la librairie nous permet de lire plusieurs images, les mettre dans une liste et enregistrer ces images en un seul document. La puissance de GraphicsMagick est telle que le format des images ou du document PDF n’est à expliciter nulle part autre que dans le nom des fichiers. La librairie s’occupe d’identifier les formats et s’adapte en conséquence.

Résultats

L’interface, bien que simpliste, permet bien de numériser plusieurs pages d’un document. La conversion des images en un seul document se fait sans problème. Cependant, deux points majeurs sont à relever : les documents numérisés sont enregistrés dans le dossier temporaire du Raspberry Pi (/tmp) sans être transmis directement à l’utilisateur et la qualité des documents n’est pas suffisante.

Concernant le premier point, il est pour l’instant possible de récupérer les documents numérisés à l’aide d’un logiciel client SFTP (FileZilla fait cela très bien). Pour plus de commodités, j’ai pris soin de configurer la connexion pour qu’elle pointe directement par défaut vers le dossier /tmp du côté Raspberry Pi et vers le dossier Documents de mon répertoire utilisateur du côté local.

En ce qui concerne le deuxième point, il n’était pas facile de s’en rendre compte au départ et je n’ai pas encore trouvé de solution à ce problème. Ma copine m’a fait remarquer que ce défaut se voyait surtout sur des documents manuscrits. En comparant le rendu d’un document numérisé avec ma solution et un autre numérisé directement à l’aide de Simple Scan, il est flagrant de remarquer que la qualité est bien meilleure avec Simple Scan. A priori, je pense que le problème peut venir du fait que je n’effectue aucune configuration du scanneur (j’utilise les paramètres par défaut). Peut-être que le code source de Simple Scan peut me donner quelques indices sur la manière de procéder ou me donner d’autres pistes de réflexion.

Mise à jour du 21 mai 2018 : Le paramètre en question est tout simplement la résolution. Par défaut, celle-ci est configurée à 75dpi, la plus basse. En regardant dans les paramètres de SimpleScan, la résolution est configurée à 300dpi (pour les images, 150dpi pour le texte mais cette résolution est insuffisante pour nos besoins). J’ai configuré Rasprinter avec cette résolution et le rendu est parfait. Seulement, cela ralentit considérablement la numérisation et l’écriture des fichiers.

J’ai également ajouté une interface en ligne de commande pour des tests via SSH (qui normalement permet l’utilisation de X11 et donc l’affichage de l’interface graphique mais la configuration du Raspberry Pi pour fonctionner avec cet écran rend cette fonctionnalité capricieuse).

Pour l’instant je ne vais pas partager le code source. J’attends de le remanier complètement, pour corriger les bugs et intégrer la gestion complète des erreurs.

Mise à jour du 15 juin 2018 : J’ai pu identifier la cause de la lenteur lors des enregistrements d’images. Il se trouve que les images enregistrées par mon programme sont bien plus lourdes que celles que je parviens à obtenir avec d’autres logiciels comme SimpleScan. J’ignore s’il s’agit d’une compression faite par SimpleScan (peut-être n’utilise-t-il pas de format d’image brute, comme je le fais, mais un format compressé comme le PNG). Toujours est-il que j’ai là une piste intéressante à investiguer. Pour l’instant je ne travaille plus à ce projet, ayant d’autres impératifs, mais je m’y remettrai dès que possible.

Enfin, le programme est très minimaliste et il est facile d’imaginer l’implantation d’autres fonctionnalités ou d’apporter bon nombre d’améliorations. L’écran tactile n’étant pas incompatible avec l’affichage d’un environnement graphique, il serait peut-être plus avisé d’utiliser une librairie dédiée aux interfaces graphiques comme Qt, GTK ou wxWidgets pour réaliser une interface plus agréable à l’œil, voire plus efficace. Ou encore, permettre l’enregistrement des documents numérisés sur une clé USB branchée au Raspberry Pi, parcourir cette même clé USB pour imprimer des documents qui s’y trouvent, ajouter une interface en ligne de commandes pour effectuer les tests directement depuis mon ordinateur, ou alors permettre l’accès direct via WiFi voire via Bluetooth (pour l’instant le Raspberry Pi est seulement connecté au réseau local via Ethernet et donc uniquement accessible via celui-ci). J’aimerais aussi retravailler tout le code source, ne serait-ce que pour le simplifier en utilisant le C++ au lieu du C et ainsi le rendre plus modulable.

Conclusion

Même s’il est loin d’être parfait, je suis assez content de ce projet. J’ai appris beaucoup de choses et j’ai pu constater que malgré un emploi du temps pas forcément évident j’ai pu réaliser une première mouture fonctionnelle. Loin de moi l’idée de laisser ce projet en l’état, au risque qu’il prenne la poussière. Je compte bien corriger ses défauts majeurs et y apporter toujours plus de nouveautés. Et en parallèle, travailler sur d’autres projets, soit à base de Raspberry Pi, soit avec d’autres cartes. Concernant ce dernier point, je compte réaliser un petit tour d’horizon des différentes cartes dont je dispose, certaines relativement connues comme Arduino ou (feu) Intel Edison tandis que d’autres sont des cartes récupérées ça et là. Il y a de quoi s’amuser. ;-)

Sur ce, je vous laisse et je vous dis à bientôt.

Nicolas SAN AGUSTIN

Programmateur de PIC – Lecture

Bonjour à toutes et à tous !

Cela fait quelques mois que j’essaye d’écrire un nouvel article mais quelques difficultés n’ont pas joué en ma faveur. Notamment le fait que j’ai longtemps bataillé avec mon programmateur de PIC pour obtenir des résultats présentables. Malgré tout, je suis content de pouvoir enfin présenter la suite du projet, même s’il ne s’agit pas des fonctionnalités que j’espérais.

En effet, j’ai désespérément tenté d’écrire un programme sur le PIC depuis mon Arduino, toujours sans succès (pour l’instant). Je me suis donc orienté plutôt vers la lecture, une décision en grande partie motivée par la lassitude de devoir sans cesse brancher et débrancher l’Arduino ou mon programmateur me permettant de lire la mémoire du PIC (parce que ce serait trop simple de pouvoir avoir tout branché en même temps !).

Principe

La méthode de lecture de la mémoire est la même pour tous les blocs mémoire, excepté pour la mémoire EEPROM, et s’avère assez simple. Elle consiste simplement à affecter l’adresse de l’octet que l’on souhaite lire et à envoyer une commande du type « Table Read » pour démarrer la lecture. Contrairement aux commandes que l’on envoyait pour l’effacement, il faut procéder ainsi :

  1. Envoyer une instruction sur 4 bits (par exemple « 1001 » pour « Table Read, Post-Increment » pour ne pas avoir à réaffecter l’adresse pour lire l’octet suivant) ;
  2. Placer la broche de données (PGD) à l’état bas et envoyer 8 oscillations d’horloge (PGC), ce qui équivaut à l’envoi d’un octet avec une valeur nulle (0) ;
  3. Changer l’orientation de la broche de données : PGD est maintenant une entrée ;
  4. Générer un cycle d’horloge pour la lecture de chaque bit de données ;
  5. Une fois l’octet lu, reconfigurer la broche de données en tant que sortie.

Il suffit de procéder ainsi pour chaque octet que l’on souhaite lire. Pour rappel, l’adressage mémoire pour le PIC18F2550 est :

  • Code exécutable : 0x00000 à 0x007FFF ;
  • ID locations : 0x200000 à 0x200007 ;
  • Registres de configuration : 0x300000 à 0x30000D ;
  • Identifiant du périphérique (Device ID) : 0x3FFFFE et 0x3FFFFF.

Mise en pratique

En terme de code, cela donne à peu près ça :

/*
 * Send a command to the chip on 8 bits (plus 8 bits to receive)
 */
char receive(char command)
{
    int i;
    char read = 0x00;
    boolean bit;

    /* Command */
    for (i = 0; i < 4; i++)
    {
        if ((command & 1) == 1)
        {
            digitalWrite(PGD, HIGH);
        }
        else
        {
            digitalWrite(PGD, LOW);
        }
        digitalWrite(PGC, HIGH);
        digitalWrite(PGC, LOW);
        command = command >> 1;
    }

    /* None Byte */
    digitalWrite(PGD, LOW);
    for (i = 0; i < 8; i++)
    {
        digitalWrite(PGC, HIGH);
        digitalWrite(PGC, LOW);
    }

    pinMode(PGD, INPUT);

    /* Byte to receive */
    for (i = 0; i < 8; i++)
    {
        digitalWrite(PGC, HIGH);
        bit = LOW;
        bit = digitalRead(PGD);
        if (bit == HIGH)
        {
            read |= (1 << i);
        }
        else
        {
            read &= ~(1 << i);
        }
        digitalWrite(PGC, LOW);
    }

    pinMode(PGD, OUTPUT);
    digitalWrite(PGC, LOW);

    return read;
}

/*
 * Read a byte in the memory
 */
char read_byte(unsigned long address)
{
    /* MOVLW <Addr[21:16]> */
    send(CORE_INSTRUCTION, (char) ((address >> 16) & 0x000000FF), 0x0E);
    /* MOVWF TBLPTRU */
    send(CORE_INSTRUCTION, 0xF8, 0x6E);
    /* MOVLW <Addr[15:8]> */
    send(CORE_INSTRUCTION, (char) ((address >> 8) & 0x000000FF), 0x0E);
    /* MOVWF TBLPTRH */
    send(CORE_INSTRUCTION, 0xF7, 0x6E);
    /* MOVLW <Addr[7:0]> */
    send(CORE_INSTRUCTION, (char) (address & 0x000000FF), 0x0E);
    /* MOVWF TBLPTRL */
    send(CORE_INSTRUCTION, 0xF6, 0x6E);

    return receive(TABLE_READ_POST_INC);
}

Note : la procédure send qui est appelée est en fait send_command, que nous avons abordé dans l’article sur l’effacement. Je trouve plus propre d’avoir des fonctions se nommant simplement send et receive.

Conclusion

Je sais, c’est assez court comme article après quatre mois d’absence. Il faut dire que cette fonctionnalité est assez simple à mettre en œuvre et que les précédents articles étaient longs essentiellement à cause de tous les détails qu’il fallait aborder en guise d’introduction. Une fois ces principes élémentaires assimilés, le reste devient plus simple.

Je regrette de ne pas avoir encore pu obtenir d’écriture fonctionnelle. Je persévère quand même et j’espère parvenir assez vite à des résultats encourageants. Pour ne pas laisser le site en plan trop longtemps, je prévois d’écrire des articles sur d’autres sujets (sur le Raspberry Pi, notamment), et je ne doute pas qu’ils pourront vous intéresser.

Sur ce je vous laisse et vous dis à bientôt.

Nicolas SAN AGUSTIN

Programmateur de PIC – Bulk Erase

Bonjour à toutes et à tous !

Comme promis, voici la suite du projet de programmateur de PIC. Nous avons vu la dernière fois une présentation sommaire du programmateur de PIC et de ses fonctionnalités. Aujourd’hui nous allons nous atteler à la première d’entre elles : l’effacement intégral de la mémoire.

Tout d’abord, je vais vous présenter le principe de fonctionnement de l’effacement et de quelques cas d’utilisation. Ensuite, nous allons voir ensemble la constitution du protocole de communication. Enfin, nous verrons par quelle trame ordonner au microcontrôleur l’effacement complet de sa mémoire.

Principe

L’effacement de la mémoire est généralement un pré-requis à l’écriture dans la mémoire. Cela afin de se débarrasser des résidus de programme qui pourrait subsister en se contentant de simplement écrire par-dessus le programme précédent.

Il a également une autre utilité. Le PIC18F2550, entre autres, dispose en effet d’un mécanisme de protection de l’accès à la mémoire. Pour pouvoir réécrire un programme dans une zone mémoire protégée, il faut procéder à l’effacement de la mémoire qui va également réinitialiser les droits d’accès de toutes les zones de mémoire.

Nous ne verrons ici que l’effacement intégral, plus simple à mettre en œuvre.

Chronogrammes et programme

Nous avons vu brièvement dans le précédent article le déroulement de la programmation du microcontrôleur. Je vous propose ici de voir les premières étapes plus en détail.

N.B. : Dans le doute, je préfère ne pas mettre ici les diagrammes présents dans la documentation de Microchip. Toutefois, comme « il est toujours bien plus facile de comprendre avec un dessin », je vous invite à consulter directement le document concerné. Afin de vous faciliter la recherche, j’indiquerai pour chaque diagramme le numéro de la figure et la page.

Entrée en mode Program/Verify

Lors de la programmation du microcontrôleur, la tension d’alimentation se situe entre 3,0V et 5,5V (si le microcontrôleur fonctionne avec son oscillateur interne) ou entre 2,0V et 5,5V (si un oscillateur externe est utilisé) et ce quel que soit le mode d’alimentation utilisé (Low-Voltage ou High-Voltage). De plus, toutes les broches doivent être à l’état bas, sauf VDD.

  • Low-Voltage (page 17, paragraphe 2.6, figure 2-16)

L’entrée dans le mode Program/Verify consiste à indiquer au microcontrôleur que l’on se place en mode Low-Voltage via la broche PGM avant d’alimenter la broche de programmation VPP avec la tension adéquat (entre 2,0V et 5,5V pour le mode Low-Voltage).

Dès lors, nous commençons par placer la broche PGM à l’état haut. Après un court délai minimum de 2 microsecondes, on peut placer la broche VPP à l’état haut puis attendre encore 2 microsecondes minimum avant de transmettre des données via les broches PGD (données) et PGC (horloge).

  • High-Voltage (page 16, paragraphe 2.5, figure 2-14)

Si on utilise le mode High Voltage, la tension de programmation se situe entre VDD+4,0V (donc entre 6/7V et 9,5V, selon l’oscillateur utilisé) et 12,5V et la broche PGM n’est pas du tout utilisée. On commence par alimenter le microcontrôleur et attendre un délai minimum de 100 nanosecondes. Avant de placer la broche de tension de programmation à l’état haut.

Important : le temps de montée de la broche VPP ne doit pas dépasser une microseconde !

Comme pour le mode Low-Voltage, ce n’est que 2 microsecondes après le moment où VPP atteint l’état haut que l’on peut commencer à transmettre via PGD et PGC.

Une routine pour effectuer cette entrée en « Program/Verify mode », en mode Low-Voltage, ressemblerait à ceci :

/*
 * Enter Program/Verify mode
 */
void enter_program_verify_mode()
{
    digitalWrite(PGM, HIGH);
    delayMicroseconds(2);
    digitalWrite(VPP, HIGH);
    delayMicroseconds(2);
}

Sortie du mode Program/Verify

  • Low-Voltage (page 17, paragraphe 2.6, figure 2-17)

Il faut avant tout que les broches PGD et PGC soient et restent à l’état bas, signe qu’elle ne transmettent plus. On peut alors placer VPP puis PGM à l’état bas. Aucun délai n’est à respecter.

  • High-Voltage (page 16, paragraphe 2.5, figure 2-15)

Comme pour le mode Low-Voltage, il faut avant tout que les broches PGD et PGC soient et restent à l’état bas. On peut alors, sans attendre, placer VPP à l’état bas, tout en respectant un temps de descente d’une microseconde maximum. Puis, dans un délai maximum de 100 nanosecondes, on doit placer VDD à l’état bas.

Une routine pour effectuer la sortie du « Program/Verify mode », en mode Low-Voltage, ressemblerait à ceci :

/*
 * Exit Program/Verify mode
 * Power is on (VCC and Programming voltage)
 */
void exit_program_verify_mode()
{
    digitalWrite(PGD, LOW);
    digitalWrite(PGC, LOW);
    digitalWrite(VPP, LOW);
    digitalWrite(PGM, LOW);
}

Envoi de trame (page 18-19, paragraphe 2.7, figure 2-18)

Comme je l’ai dit dans l’article précédent, les commandes sont formés de 20 bits : 4 bits pour l’instruction et 16 bits pour le payload. Ici, je vous détaille comment ces commandes sont transmises.

La transmission s’effectuant en Little-Endian, le plus faible des 4 bits est transmis en premier jusqu’à celui le plus fort. On en fait de même avec les 16 bits, en commençant par le plus faible des 16 et en terminant par le plus fort.

Pour chaque bit, l’horloge doit se placer à l’état haut, la broche de données doit se placer ou être déjà placé à l’état haut pour transmettre un 1 et à l’état bas pour transmettre un 0. Enfin, on place l’horloge à l’état bas pour amorcer l’émission du bit.

Une période d’horloge doit durer au minimum 100 nanosecondes, avec un minimum de 40 nanosecondes pour chaque état d’horloge. Ceci est valable lorsque le microcontrôleur est alimenté en 5V. Lorsque VDD = 2V au lieu de 5V, ce temps minimum doit être décuplé (400 nanosecondes et 1 microseconde au lieu de 40 et 100 nanosecondes).

Concernant la broche de données, il doit s’écouler au moins 15 nanosecondes entre la mise à l’état souhaité et le passage de l’horloge à l’état bas. De même, le temps de maintien de la broche de données à l’état souhaité doit durer au moins 15 nanosecondes à partir du moment où l’horloge est passée à l’état bas.

Enfin, entre la fin de la transmission du dernier des 4 bits de commande et le début de la transmission du premier des 16 bits de payload, il doit s’écouler au minimum 40 nanosecondes. De même à la fin de la transmission du dernier des 16 bits de payload : avant de transmettre quoi que ce soit, il est nécessaire d’attendre au moins 40 nanosecondes.

N.B.: Je sais qu’il peut sembler ridicule d’exiger de patienter durant un temps aussi infime qu’une microseconde ou que quelques nanosecondes. Cependant, à l’échelle du microcontrôleur, ce n’est pas anodin. Si je prends par exemple la fréquence de l’oscillateur interne du PIC18F2550, qui est de 8 MHz, cela nous donne une instruction tous les 500 nanosecondes (TOSC= 1/f = 1/8000000Hz = 0,000000125 secondes = 125 nanosecondes ; TCY = TOSC * 4 = 125 * 4 = 500 nanosecondes par cycle instruction). C’est juste dans la plupart des cas et insuffisant lorsque VDD est de 2V. Dans notre cas, nous utilisons un Arduino cadencé à 16 MHz et le cycle d’instruction se fait le plus souvent en un ou deux cycles d’horloges au lieu de quatre (une particularité des microcontrôleurs AVR sur lesquels sont basés la plupart des cartes Arduino), ce qui amène le temps de cycle à 62,5 ou 125 nanosecondes.
Dans notre cas, cela devrait suffire, la plupart des délais à respecter n’atteignant pas 62,5 nanosecondes.

Voici à quoi ressemblerait la routine permettant d’envoyer une commande au microcontrôleur.

/*
 * Send a command to the chip
 */
void send_command(char command, char lowbyte, char highbyte)
{
    char i;

    /* Command */
    for (i = 0; i < 4; i++) 
    { 
        digitalWrite(PGC, HIGH); 
        if ((command & 1) == 1) 
        { 
            digitalWrite(PGD, HIGH); 
        } 
        else 
        { 
            digitalWrite(PGD, LOW); 
        } 
        digitalWrite(PGC, LOW); 
        command = command >> 1;
    }

    /* Low Byte */
    for (i = 0; i < 8; i++) 
    { 
        digitalWrite(PGC, HIGH); 
        if ((lowbyte & 1) == 1) 
        { 
            digitalWrite(PGD, HIGH); 
        } 
        else 
        { 
            digitalWrite(PGD, LOW); 
        } 
        digitalWrite(PGC, LOW); 
        lowbyte = lowbyte >> 1;
    }

    /* High Byte */
    for (i = 0; i < 8; i++) 
    { 
        digitalWrite(PGC, HIGH); 
        if ((highbyte & 1) == 1) 
        { 
            digitalWrite(PGD, HIGH); 
        } 
        else 
        { 
            digitalWrite(PGD, LOW); 
        } 
        digitalWrite(PGC, LOW); 
        highbyte = highbyte >> 1;
    }
    digitalWrite(PGC, LOW);
    digitalWrite(PGD, LOW);
}

Les commandes d’effacement

À présent, nous disposons de routines permettant l’envoi de commandes qui respectent les standards imposés par la documentation du composant. Nous pouvons à présent transmettre l’ordre d’effacement.

Mais qu’envoyer ? La documentation nous indique la séquence suivante :

4-Bit Command (binary) Data Payload (hexadecimal) Core Instruction (assembly)
0000 0E 3C MOVLW 3Ch
0000 6E F8 MOVWF TBLPTRU
0000 0E 00 MOVLW 00h
0000 6E F7 MOVWF TBLPTRU
0000 0E 05 MOVLW 05h
0000 6E F6 MOVWF TBLPTRU
1100 3F 3F Write 3F3Fh to 3C0005h
0000 0E 3C MOVLW 3Ch
0000 6E F8 MOVWF TBLPTRU
0000 0E 00 MOVLW 00h
0000 6E F7 MOVWF TBLPTRU
0000 0E 04 MOVLW 04h
0000 6E F6 MOVWF TBLPTRU
1100 8F 8F Write 8F8Fh to 3C0004h
0000 00 00 Hold PGD low until erase completes
0000 00 00

Que nous dit cette séquence ? Tout simplement que l’on cherche à se positionner à l’adresse 0x3C0005 via le pointeur TABLPTR (dont j’ai parlé au précédent article) pour y inscrire la valeur 0x3F puis refaire de même avec la valeur 0x8F à l’adresse 0x3C0004.
On termine l’ordre par un NOP (une instruction assembleur dont le principe est de ne rien faire) pour que démarre l’effacement. Ce dernier se déclenche au front descendant d’horloge du 4ème bit de commande du NOP. Lors de l’opération d’effacement, la liaison série sera indisponible jusqu’à la fin (au moins 5 millisecondes). Pendant ce temps, l’horloge peut continuer à osciller, mais la broche de données doit impérativement rester à l’état bas.
Un délai supplémentaire d’une dizaine de microsecondes au minimum doit être opéré à la fin du temps d’attente avant de reprendre toute communication avec le microcontrôleur.

La documentation n’en dit hélas pas davantage sur ces instructions, hormis qu’aux adresses 3C0005h et 3C0004h se situent deux registres dédiés à l’effacement du microcontrôleur ainsi que les valeurs à utiliser pour ces registres selon les zones de mémoire que l’on souhaite effacer.

Mise en pratique

Après quelques tâtonnements, je suis parvenu à écrire un programme Arduino qui effectue l’effacement du microcontrôleur PIC18F2550 auquel il est relié. Pour m’assurer que l’effacement était bien effectif, le programme se termine par une petite boucle qui fait osciller VPP. Le programme précédent se contentant uniquement de garder une LED allumée, si la LED restait éteinte à la fin du programme, l’effacement était considéré comme réussi.

ArduinoICSP – Bulk Erase

Bien évidemment, j’ai revérifié après avec un autre programmateur si la mémoire était bien vierge, et ce fut le cas.
Notez également que j’ai choisi la broche 13 pour commander VPP. Ce n’est pas anodin : cette broche commandant également une LED intégrée à la carte Arduino, cette dernière me fournissait un témoin visuel que la broche était bel et bien en train de changer d’état.

Les broches VDD et VSS du PIC18F2550 sont connectées respectivement aux broches VCC 5V et GND de la carte Arduino.

La commande pour l’effacement se trouve ici n’être qu’un simple tableau qui est transmis par le programme principal en temps qu’arguments à la fonction d’envoi de trame. C’est largement suffisant et les autres opérations seront exécutées de la même manière, avec quelques subtilités cependant (je pense par exemple à l’écriture de programme).

Conclusion

C’est terminé avec l’effacement du microcontrôleur. Il s’agissait d’un article très consistant, ce qui explique pourquoi je ne le publie que maintenant. J’espère parvenir à poursuivre le projet et l’écriture des articles rapportant mes progrès. J’ai moins de temps depuis que j’ai retrouvé un emploi le mois dernier (je ne m’en plains pas, c’est un boulot qui me plaît et les progrès que j’y fais me servent pour ce projet, et réciproquement).

Je m’efforce également de ne pas digresser sur d’autres sujets, ce qui est assez difficile car j’ai déjà plein d’idées de projets que je meurs d’envie de partager ici. Il faudra patienter le temps que je finisse le projet de programmateur de PIC. Les autres qui viendront par la suite seront, je l’espère, plus concis que le programmateur bien que tout aussi intéressants (voire davantage).

Sur ce, je vous laisse et je vous dis à bientôt.

Nicolas SAN AGUSTIN

Copyright Nicolas SAN AGUSTIN 2018
Tech Nerd theme designed by Siteturner