lundi 12 septembre 2016

La machine à thé

Je me suis fabriqué une machine à infuser les sachets de thé. Dans cet article je fais une présentation détaillée du projet. Le code source et tous les autres documents sont disponible sur https://github.com/picatout/machineathe.

Sommaire

La machine à thé que j'ai fabriqué a pour fonction de prendre en charge le chronométrage de l'infusion du thé. Elle a un bras motorisé auquel j'attache le sachet. j'ajuste le chronomètre et pèse sur le bouton MARCHE. Le bras descend le sachet dans la tasse. lorsque le temps est expiré le bras soulève le sachet hors de la tasse et sonne l'alarme. De cette façon même si je suis occupé à autre chose et ne peu me déplacer immédiatement pour sortir le sachet de la tasse, le retrait automatique du sachet évite d'avoir à boire un thé trop concentré au goût amer.

Analyse du problème

Il faut donc un chronomètre et un bras motorisé. Il faut un bouton pour ajuster le temps et un affichage pour indiquer la durée. Il n'est pas nécessaire d'avoir une précision à la seconde. J'ai donc décidé que le temps pouvait-être incrémenté par multiple de 15 secondes. Pour l'affichage une barre de LED à 10 segments sert à indiquer le temps. Chaque segment représente un multiple de 15 secondes pour une durée maximale de 150 secondes. Un deuxième bouton sert à démarrer le chronomètre. Lorsque le temps est écoulé le sachet est retiré de la tasse, l'alarme sonne et finalement le microcontrôleur est mis en sommeil (sleep mode). L'interface utilisateur est donc simple, 2 boutons et un affichage barre de LED.

La procédure d'utilisation de l'appareil est la suivante:

  1. Attacher le sachet de thé au bras et placé la tasse sous le sachet.
  2. Peser sur le bouton MARCHE/ARRET pour sortir le MCU du mode sommeil. La mise en marche est signalée par un BEEP.
  3. Ajuster le temps avec le bouton TEMPS
  4. Peser à nouveau sur le bouton MARCHE/ARRET pour démarrer la séquence d'infusion.
Pendant l'infusion il est possible d'annuler en pesant à nouveau sur le bouton MARCHE/ARRET . Ce bouton est donc multi fonctions.
  • Allumer l'appareil
  • démarrer la séquence d'infusion
  • Annuler la séquence d'infusion

Le choix du microcontrôleur.

Rapidement dans ma réflexion j'ai choisi d'utiliser un servomoteur Hitec HS-422 que j'avais en ma possession ainsi que la barre de segments LED pour la même raison. Il me restait à choisir un microcontrôleur. le servomoteur se contrôle avec des impulsions dont la largeur varie entre 2,1 et 2,9 millisecondes. Ces impulsions doivent-être répétées 50 fois par secondes. Un périphérique PWM est tout désigné pour ce travail.

Pour l'alarme un petit haut-parleur alimenté par une tonalité ou une séquence de tonalités de fréquence différente peut-être utilisé. Encore là un périphérique PWM est tout désigné pour générer les tonalités.

Pour les 2 boutons il suffit de 2 entrées digitales.

Pour l'affichage des entrées/sorties 3 états (sortie 5 volt,sortie 0 volt, haute impédance) sont nécessaires. On peut cependant réduire le nombre nécessaire en utilisant une méthode de multiplexage appelée Charlieplexing. Le nombre de LEDs qui peuvent-être multiplexés par cette méthode est de N*(N-1). Où N est le nombre de broches utilisées. Avec 4 broches 3 états (tristate) on peut donc contrôler 12 LEDs. La barre en a 10.

On a donc besoins d'un MCU qui dispose de 2 périphériques PWM et d'au moins 8 E/S numériques et au minimum 2 minuteries, une pour les PWM et pour servir de chronomètre. Rien de plus que ça. Le plus petit PIC16F1xxx qui possède ces caractéristiques doit-être disponible dans un boitier PDIP 14 broches.

Les générations PIC 8 bits

Il y a 4 générations de PIC 8 bits. Les baseline mis en marché en 1990, les mid-range, les enhanced mid-range et finalement les extended c'est à dire les PIC18Fxxxx.

La famille enhanced mid-range est celle qui offre la plus grande variété de produits hormis les PIC18F qui sont inutilement coûteux pour ce projet. De plus il n'y a aucun PIC18F qui viens en format PDIP-14. Les enhanced mid-range ont 4 chiffres après le F, i.e. PIC1y(L)F1xxx où y est soit 2 ou 6.

En utilisant le sélecteur de MCU sur le site WEB de Microchip j'ai trouvé un bon candidat pour le rôle, le PIC16F1703. Voilà un petit MCU intéressant.

PIC16(L)F1703
mémoire flash 2048 instructions.
mémoire RAM 256 octets
11 entrées/sorties 3 états. 1 entrée seulement RA3/~MCLR/Vpp
Analog-to-Digital Converter (ADC) 10 bits
Fixed Voltage Reference (FVR)
Zero-Cross Detection (ZCD), utile dans les applications de contrôle de voltage AC. i.e thermostat, atténuateur d'éclairage.
Temperature Indicator
2 Capture/Compare/PWM (CCP/ECCP) Modules
Master Synchronous Serial Ports MSSP
2 Op Amp
Timer0, 8 bits avec pré-diviseur
Timer1, 8 bits avec gating et clocking externe
Timer2, 16 bits avec pré et post diviseur. Contrôle la période des CCP
Pour ce projets seul sont utilisés les 2 CCP et les 3 minuteries.

Schématique du projet

Voici la schématique du circuit produit avec KiCAD version 4.0.3.

Le montage est fait sur 2 cartes Schmartboard bread/proto 400 points, l'une pour le MCU et l'autre pour l'interface utilisateur. J'ai utilisé 6 broches pour le charlieplexing des LEDs au lieu de 4. Ces broches correspondes aux 6 broches du PORTC disponibles sur ce MCU. Puisqu'il n'y avait pas d'usage pour les 2 broches supplémentaire aussi bien les utilisées. Seule RA0 demeure inutilisée (sauf pour la programmation ICSP).

Assignation des ressources du MCU

ressource utilisation
TIMER0 Cette minuterie 8 bits est configurée pour chronométrer les délais cours par incrément de 4msec. Lorsqu'elle est active cette minuterie génère une interruption à intervalle de 4 millisecondes.
TIMER1 Cette minuterie 16 bits est configurée pour chronométrer les durées en secondes. Lorqu'elle est active cette minuterie génère une interruption à intervalle d'une seconde. C'est la minuterie utilisée pour contrôler la durée de l'infusion.
CCP1 (Capture/Compare/PWM) Ce périphérique est utilisé en mode PWM pour générer des tonalité audio-fréquence. Utilisé par la routine tone.
CCP2 Ce périphérique est utilisé en mode PWM pour génerer les impulsions de contrôle du servomoteur. La période est de 20 msec et la largeur des impulsions varie entre 2,1 et 2,9 msec par incrément de 32µsec.
PORTA RA5 entrée bouton MARCHE/ARRET
RA4 entrée bouton TEMPS
RA2 sortie PWM contrôle servomoteur
RA1 sortie PWM son
PORTC RC0-RC5 E/S 3 états pour le Charlieplexing des LEDS

Le logiciel

Le code source est écris entièrement en MPASM. Les enhanced mid-range possède un jeux de 49 instructions. Cette génération représente une amélioration considérable par rapport au 2 précédentes. Voici quelques comparaisons.

paramètremid rangeenhanced
pile8 niveaux16 niveaux. Peut-être manipulée.
FSRxFSR0 seulementFSR0,FSR1
interruptionsauvegarde/restauration manuel du contextesauvegarde/restauration automatique du contexte
accès RAMsegmentée seulementsegmenté ou linéaire indirecte
adressage indirectpas d'auto incrément/décrément. pas d'index relatifpour les instruction moviw et movwi pré/post incrément/décrément. index relatif.
branchement relatif à PC Pas de branchement relatif. Branchement relatif avec les instructions BRA et BRW
Il est plus intéressant de travailler avec les enhanced mid-range qu'avec les générations précédentes et pour la programmation en C le compilateur peu plus facilement optimiser le code binaire.

ordinogramme de l'application

Voici l'ordinogramme (flowchart) de l'application.

Pour la présentation qui suis référez-vous au fichier mat.asm

Initialisation du MCU

Chaque MCU fourni par Microchip a un fichier *.inc qui lui est associé. La première chose à faire est d'inclure ce fichier avec la directive


    include p16f1703.inc
Ensuite il faut configurer les paramètres de fonctionnement du MCU avec la directive __config. Puisque ce MCU a 2 locations de configuration _CONFIG1 et _CONFIG2 il faut une directive pour chacun. L'oscillateur interne est utilisé, le watch dog timer est désactivé et le master clear est activé. Pour la signification des constantes se référer au fichier p16f1703.inc. Elles sont définies à la fin du fichier.

Je définis toujours les constantes, les macros et les variables au début du fichier. Je fais un usage abondant de la directive #define. L'usage de noms symboliques spécifiques à l'application simplifie les mises à jour ultérieures.

Puisque le programme utilise moins de 16 octets de variables elles sont toutes placées dans la section udata_shr ce qui évite d'avoir à utiliser des directives banksel lorsqu'on veut accéder une variable. Il en résulte un code plus compacte et plus rapide. udata_shr correspond aux 16 dernières locations de chaque banque de la mémoire RAM. C'est 16 locations sont communes à toutes les banques. Voilà pourquoi on a pas besoin de la directive banksel pour les accédées.

Lorsqu'un PIC est réinitialisé l'exécution du code commence à l'adresse 0. le vecteur d'interruption lui est à l'adresse 4. On doit donc faire un saut au delà de la routine d'interruption pour aller à init:. J'ai commenté chaque étape de l'initialisation il ne devrait donc pas être facile de suivre le cheminement. Une des fonctions intéressante du PIC16F1703 est le PPS (Peripheral Pin Selsect). Qui permet de décider soit-même sur quelle broche on veut brancher les périphériques logiques, (ça ne s'applique pas aux analogiques, i.e. op amp, comparateurs, entrées ADC). Donc j'ai sélectionné la broche RA1 pour la sortie CCP1 (alarme) et la broche RA2 pour la sortie servomoteur CCP2.

Le TIMER0 est configuré pour générer une interruption à intervalle de 4 millisecondes et le TIMER1 est configuré pour générer une interruption à intervalle d'une seconde. J'explique la sous-routine d'interruption plus bas.

Auto test de mise sous tension

Lorsque la configuration du MCU est complétée une routine d'auto test à l'allumage est appelée. self_test: allume tous les segments de la barre de LEDs l'un après l'autre en commençant par le haut et fait entendre une tonalité différente à chaque segment. Ensuite self_test abaisse le bras et le relève. Ce test n'est effectué qu'une fois lors de la mise sous tension de l'appareil.

mise en sommeil

Après l'exécution de l'auto test le code tombe dans la routine main. Cette routine débute par une mise en sommeil du MCU pour réduire la dépense électrique lorsqu'il n'est pas utilisé. Le MCU utilise la fonction IOC (Iinterrupt On Change) pour sortir le MCU du sommeil. A chaque PORT est associé 2 registres de configuration IOCxP pour détecter les transitions positives (0 -> 5 volt) et IOCxN pour les transitions négatives (5 -> 0 volt). Cette fonction consiste à générer une interruption lorsqu'une broche configurée en entrée logique change d'état. Dans cet application on configure pour que le MCU s'éveille lorsque le bouton MARCHE/ARRET est enfoncé. Alors la broche START_PIN passe de 5 volt à 0 volt. L'interruption n'est cependant pas activée. Ce n'est pas nécessaire. Lorsque le MCU sort du mode sommeil il exécute l'instruction qui suis l'instruction sleep qui l'a fait entrer en mode sommeil.

La configuration IOCAN est désactivée et ensuite la mise en marche est annoncée par un BEEP. Après quoi la routine timeset est appelée.

Ajustement du chronomètre

L'utilisateur doit alors utiliser le bouton TEMPS pour ajuster la durée de l'infusion. Chaque pression du bouton incrémente le temps de 15 secondes. Cette routine fait donc la lecture des 2 boutons et met à jour l'affichage LED. Lorsque le bouton MARCHE/ARRET est enfoncé un branchement vers timeset_exit met fin à cette routine.

infusion

De retour dans la routine main le registre WREG est chargé avec la valeur SERVO_POS_BAS et la routine servo_pos est appelée. Cette routine est responsable du déplacement du bras. Il ne s'agit pas de simplement envoyer la nouvelle valeur de position au servomoteur car le bras descendrait trop rapidement et ça risquerait de produire des éclaboussures. Donc la valeur de la dernière position est conservée dans la variable last_pos et il s'agit de s'approcher de la nouvelle valeur étape par étape en partant de la valeur de last_pos en insérant un délais entre chaque commande. La valeur de ce délais est indiquée par la constante SERVO_DLY.

Lorsque le positionnement est terminé la routine quitte et de retour dans main la routine infusion est appelée. Cette routine est responsable de surveiller le chronomètre, d'afficher la valeur du temps restant et aussi faire la lecture du bouton MARCHE/ARRET a cas ou l'utilisateur annulerait la procédure. Lorsque le temps est expiré ou s'il y a annulation le routine retourne vers main

fin de l'infusion

Une nouvelle commande est envoyée au servomoteur cette fois avec la valeur SERVO_POS_HAUT comme argument dans WREG. De retour de la routine servo_pos, main appelle la routine alarm. La routine alarm: joue la séquence de notes enregistrées dans la table CE3K ensuite retourne vers main. La séquence étant terminée on boucle au début de main et le MCU est remis en sommeil.

sous-routine d'interruption

Les MCU PIC 8 bits n'ont pas de gestionnaire d'interruption. Toutes les interruptions commence à l'adresse 4. Pour savoir qu'elle interruption a été déclenchée il faut vérifier les indicateurs d'interruption. Dans cette application il y a 2 sources d'interruption possible, le TIMER0 et le TIMER1. On commence par vérifier si l'interruption TIMER0 est active en testant l'état du bit T0IE dans INTCON si cette interruption est active on test T0IF qui est aussi dans INTCON pour s'assurer que le TIMER0 est bien la source de l'interruption. Si c'est le cas on branche vers isr_timer0. TIMER0 est responsable de la gestion de la minuterie courte durée. Cette interruption lorsqu'elle est active se produit à intervalle de 4 msec et décrémente la variable msec4. Lorsque msec4 tombe à zéro c'est que le délais est expiré. Dans ce cas l'interruption TIMER0 est désactivée et l'indicateur booléen F_MSEC4 dans la variable flags est remis à zéro.

Si l'interruption a été générée par le TIMER1 le branchement se fait vers isr_timer1. Cette interruption lorsqu'elle est active est déclenchée à intervalle d'une seconde. La variable secondes est décrémentée à chaque interruption et lorsque sa valeur tombe à zéro l'interruption du TIMER1 est désactivée et l'indicateur booléen F_SEC dans la variable flags est mis à zéro.

Les routines qui utilisent l'une ou l'autre des minuteries, doivent les initialiser en appelant start_msec4_tmr pour la minuterie courte durée ou bien start_timer pour démarrer la minuterie longue durée. Ensuite la routine surveille l'état de l'indicateur booléen correspondant dans flags pour savoir quand le délais est expiré.

Pour les enhanced mid-range les registres suivants sont sauvegardés et restaurés automatiquement lors d'une interruption: WREG,STATUS,BSR,PCLATH,FSR0L,FSR0H,FSR1L,FSR1H. Ces registres sont sauvegarder dans les registres xxxx_SHAD situés dans la banque 31.

Utilisations des FSRx

le registre d'index FSR0 est utilisé pour accéder les tables de données en mémoire flash. Le programme utilise 3 tables de données.

table description
LED_CONN Cette table indique sur qu'elles broches est branchée la cathode et l'anode de chacun des 10 segments de la barre-LED. Cette table est utilisée par la routine light_segment.
SCALE Cette table contient les valeurs pour chaque note de la gamme tempérée utilisé par la routine tone. Les tonalités sont générées en utilisant CCP1 en mode PWM. Les valeurs de cette table vont dans PR2 et dans CCP1DCL:CCP1CON{DC1B} pour déterminer la fréquence et le rapport cyclique.
CE3K Cette table contient la liste des notes à jouer par la routine alarme. Le premier élément est le nombre de notes. Les autres sont par paire: durée, note.

Le registre d'index FSR1 est utilisé comme pointeur de pile pour créer une pile d'arguments pour les appels de sous-routine. Pour les routines qui n'utilisent qu'un seul argument de 8 bits cette valeur est passée dans le registre WREG. Mais pour les autres les arguments sont passés par la pile des arguments. la variable stack dans le segment udata réserve 16 locations pour cette pile. Cette pile utilise avantageusement les instructions moviw et movwi pour gérer les accès à cette pile. Une série de macro a été définie à cette effet.

macros gestion de la pile description
pushw empile le contenu du registre WREG
popw dépile dans WREG
dup cré une copie du sommet de la pile, de sorte que la pile grandie de 1 élément et que les 2 éléments au sommet sont identiques.
over copie le 2ième élément de la pile au sommet de celle-ci de sorte que la pile grandie de 1 élément et que la valeur au sommet est identique à la 3ième valeur.

Liste des sous-routines

sous-routine arguments Description
alarm aucun fait jouer la liste des notes de la table CE3K. Appel sous-routine tone.
beep aucun fait entendre un son court lorqu'un bouton est enfoncé. Appel sous-routine tone.
div15 WREG contient le dividende
Divise le contenu de WREG par 15. Le quotient est retourné dans la variable ACAH et le reste dans ACAL.
infusion aucun surveille la minuterie des secondes ainsi que les 2 boutons. Met à jour l'affichage LED selon le temps restant. Appel start_timer, div15, light_segment
init aucun Initialise les périphériques du MCU après la réinitialisation.
isr aucun sous-routine de service des interruptions. Gère les interruptions des TIMER0 et TIMER1.
main aucun boucle principale du programme.
light_segment WREG contient le numéro du segment à allumer Allume le segment indiqué par la valeur indiqué dans WREG. Accède la table LED_CONN.
pause_msec WREG contient la durée de la pause. Suspend l'exécution pour la durée indiquée dans WREG*4 millisecondes. Appel la sous-routine start_msec4_tmr.
self_test aucun auto test lors de la mise sous tension. Appel les sous-routines light_segment,tone,servo_pos.
servo_pos WREG contient la valeur de positionnement. Positionne le servomoteur à la valeur indiqué dans WREG.
start_msec4_tmr WREG contient la durée. Démarre la minuterie courte durée avec la valeur WREG*4 millisecondes
start_timer WREG contient la durée. démarre la minuterie longue durée avec la valeur WREG secondes.
tone arguments passés sur la pile. ( d n -- ) Fait entendre une tonalité. Le sommet de la pile contient l'indice de la note dans la table SCALE et le deuxième élément de la pile indique la durée en multiple de 4 msec. Appel sous-routine pause_msec.

mise à jour 2016-09-22

Le commentaire le plus fréquent de mon entourage était que c'est gros. Et pourtant ça prend moins de place qu'une petite cafetière à filtre. Quoi qu'il en soit j'ai tenu compte de ce commentaire et j'ai réalisé qu'il était inutile de prolongé la base en dehors de la tour. J'ai donc modifié la base en conséquence. La surface de comptoir occupée n'est que de 10x10cm. J'ai aussi ajouté deux chevilles sur le bras pour faciliter l'attachement de la ficelle du sachet. J'ai aussi modifié le logiciel pour que lors de l'allumage le temps initial soit à 15 secondes au lieu de zéro pour avoir un indicatif visuel que l'appareil est allumé. J'ai aussi ajouter un easter egg.

Aucun commentaire:

Publier un commentaire