Silicium 628 - dernière mise à jour de cette page: 3 mars 2009
|
PROGRAMMATEUR POUR LAVE
LINGE |
Cette
réalisation comprend deux parties:
Je précise que je ne vends rien, je ne réalise rien pour les autres. Je me contente d'exposer ce que je fais pour moi, à titre d'exemple à ne PAS suivre, afin d'illustrer les extraordinaires possibilités des microcontrôleurs. En revanche je fournis tous mes codes sorces en Pascal, commentés. |
| Le microcontrôleur
ATmega32 contrôle l'ensemble des organes. 1) En sortie:
Le sens de rotation du moteur est choisi par permutation du branchement du stator par rapport au rotor, par deux relais. La pompe et les électrovannes sont commandées par des triacs. La résistance de chauffage est commandée par un relais 230V - 10A. Le microcontrôleur et les circuits basse tension (5V) sont isolés du secteur 230V:
|
![]() |
|
![]() | Une carte d'afichage à LED disposées en cercle donne un petit air rétro. C'est bien pratique pour apprécier du premier coup d'oeil où en est le cycle de lavage. |
![]() | L'ensemble des cartes reliées à la machine, le tout en fonctionnement. |


![]() |
Pour
la partie haute de la carte, de gauche à droite: -tout en haut en jaune les borniers de raccordement des (nombreux) fils du lave linge. -le triac BT137 -600 de commande de la vitesse du moteur - les deux relais de commande du sens de rotation - emplacement vide qui reçoit le relais de chauffage - les triacs pour la pompe et les électrovannes Viennent au dessous les optotriacs d'isolation secteur (petits boîtier blancs) Le transfo d'alim. pour le 5V et quelques composants basse tension |
![]() |
Le programme est en Pascal. |
![]() | L' implantation des composants de la carte à LED |
|
| Le
principe du
découpage de la tension alternative du secteur consiste à
déclencher la conduction d'un triac après un délai
très précis débutant au début de chaque
alternance. Il faut donc connaître l'instant du début de l'alternance: cette information est obtenue par l'optocoupleur CNX34 suivi d'une mise en forme par un inverseur à hystérésis CD40106. On applique ce signal sur l'entrée d'interruption externe INT0 de l' ATmega32 (pin16). Cette interruption va faire partir le compteur du Timer2: ![]() Ah ah ! déjà les choses se compliquent! Mais que fait donc cette interruption avec le registre TCCR2 ? Elle remet la sortie OC2 à zéro. Qu'ès aquo la sortie OC2? J'explique: Nous allons utiliser le compteur du Timer2 (8 bits) afin de créer le délai de conduction du triac. Ce timer est programmé en mode comparateur, il compare la valeur de son compteur interne (TCNT2) qui évolue avec le temps (l'horloge de l'ATmega) à la valeur que nous écrivons dans le registre OCR2. Lorsque la valeur du compteur TCNT2 atteint la valeur de OCR2, une "comparaison réussie" advient. Elle peut déclencher une interruption (interrupt Timer2COMP) dans laquelle on peut écrire un bout de code qui génère une impulsion vers le triac. C'est simple mais cela pose des problèmes de "télescopage d'interruptions" dans un programme assez lourd (timings résultants peu précis). Toutefois la documentation de l'ATmega fait apparaître une autre possibilité qui n'utilise pas d'interruption supplémentaire: lorsque la "comparaison réussie" se produit le niveau logique de la sortie OC2 peut être changé sans que cela n'affecte le déroulement du programme. J'ai donc configuré cette sortie OC2 pour qu'elle passe à 1 logique lors de la comparaison ok. L'électronique pourra recueillir ce front montant pour générer l'impulsion. Reste à faire reseter OC2 un peu plus tard, par exemple lors de l'INT0. Simple pensais-je, il suffit d'écrire un '1' "dans" OC2. Sauf que OC2 n'est pas le bit d'un registre, et que ce n'est pas accessible en écriture. (Le niveau logique sur OC2 et totalement distinct du portD7 qui dans cette configuration "n'existe plus"). Il a toutefois été prévu un mécanisme pour changer l'état logique d'OC2. Il faut configurer l'unité de comparaison du Timer2 afin que la sortie OC2 passe au niveau requis ('0' en ce qui nous concerne) puis provoquer une "comparaison virtuelle" en écrivant un '1' sur FOCE2 qui est le bit 7 du registre TCCR2. En réfléchissant à cette astuce en se plaçant dans l'état d'esprit d'un électronicien, on pourrait presque trouver ça logique! Ensuite il faut bien sûr penser à reconfigurer l'unité de comparaison du Timer2 afin que la sortie OC2 passe au niveau '0' lors de la comparaison réelle afin de générer le signal lors de la période suivante. Les quelques lignes de codes vues plus haut doivent désormais vous paraître limpides. Sachez que pour le déduire de la documentation de l'ATmega en anglais j'y ai passé un mauvais quart d'heure! Je viens d'écrire quelques lignes plus haut que les fronts montants que l'on génère de cette façon se produisent pour chaque PÉRIODE, un par période. C'est logique, l' INT0 se produit sur un front descendant du 40106, et il n'y a qu'un front descendant par période. Or il nous faut obtenir 2 tops de commande du triac par période ( un pour chaque alternance). On pourrait penser à configurer l'INT0 de façon à ce qu'elle se déclenche aussi bien sur les fronts montants que descendants (c'est possible avec les ATmega). Sauf que le signal issu de l'optocoupleur n'est pas forcément symétrique, d'ailleurs il N'EST pas symétrique. Ca conduirait donc à obtenir un angle de découpage différent pour chaque alternance avec une indétermination de pi (deux états possibles équiprobables). D'où l'intérêt de conserver un seul signal par période, pour une alternance toujours la même. Et pour l'autre alternance? un classique NE555 utilisé en monostable avec une tempo fixe de 10ms (pour 50Hz la période vaut 1/50Hz = 20ms) fait l'affaire. Ce NE555 associé à un CD40106 (voir schéma plus haut) génère ainsi les deux tops de déclenchements requis. L'ATmega se charge, lui, de les placer précisément dans le temps, en écrivant la valeur désirée du retard dans OCR2. L'ATmega mesure en permanence la vitesse réelle du tambour (par une fonction périodemètre programmé, sur le tachymètre en bout d'arbre moteur) et la compare à une consigne. Le résultat sert à régler l'angle de conduction du triac afin de maintenir la vitesse constante. (asservissement logiciel) La régulation de la température fait appel au convertisseur analogique/numérique de l'ATmega pour lire la valeur de la sonde (résistance CTN). L'ATmega interprète également les signaux de la télécommande IR. Je vous invite donc à étudier le source en Pascal, vous y trouverez un assez large éventail des possibilités des ATmega et des "drivers" de l'outil de programmation des AVR en Pascal qu'est AVRco. Le plus délicat à été la gestion des nombreux timings et d'éviter d'emboiter les procédures comme des poupées russes afin de ne pas faire déborder la pile logicielle du uC dans certaines configurations. Les procédures sont toujours appelées au niveau le plus bas possible, à des instants gérés par des flags. Pour la petite histoire, dans une première version j'avais une boucle principale qui appelait le choix du programme qui appelait le remplissage qui appelait le lavage qui appelait la vidange qui appelait le rinçage qui... dois-je préciser que les interruptions temps réel saupoudrées sur tout ça finissaient immanquablement la lessive avant son terme ? |
| program lavelinge;
{=============================================================================== par Silicium628 - voir la description de cette realisation sur mon site (chercher silicium628 sur Google, on ne peut pas le rater!) versions: voir plus bas dans la partie "const" - derniere mise à jour 8 juillet 2008 La version est visible sur l'afficheur par la touche 9 de la telecommande IR Quel plaisir de programmer un uControleur en PASCAL ! :-) ================================================================================ PRINCIPE: -------------------------------------------------------------------------------- Le moteur est un modele dit "universel" (à charbons) La vitesse du moteur en 220V est très grande La vitesse est limitée par un decoupage de la tension secteur par un triac BT137 - 800 drivé par un optocoupleur MOC3023 (sans circuit de detection de passage a zero) Le sens de rotation du moteur est changé par inversion du bobinage du stator par rapport au rotor cablé en série. Cette inversion est obtenue au moyen de deux relais inverseurs. Le moteur n'est pas alimenté si les relais sont tous deux au repos ou tous deux collés. Toutefois cette deuxième configuration (tous deux collés) est sans interet et consomme inutilement sur l'alim 5V. Elle n'est donc pas permise. Le moteur est également stoppé par absence d'impulsions de commande sur l'optocoupleur (et donc le triac) IMPORTANT: 1) Les relais ne doivent jamais êtres commutés alors que des impulsions de commandes du triac sont présentes, sous peine de destruction possible du triac. La procedure consiste donc à arrêter les impulsions et à attendre quelques secondes avant de commuter un relais. 2) Je déconseille d'envoyer des impulsions lorsque les deux relais sont au repos, et donc lorsque le triac est en roue libre. -------------------------------------------------------------------------------- REMARQUE: Triac sur charge inductive: Lors de la conduction du triac, le courant dans une inductance s'établit progressivement. Donc pas de di/dt important, et partant pas de surtension (e=-L.di/dt)). Mais un dv/dt important et gênant. Une coupure du courant dans une inductance provoque un di/dt qui genere mathematiquement une surtension E=-L.di/dt. Oui mais un triac n'est pas un transistor! Un triac se bloque naturellement de lui même lorsque le courant s'annule. DONC PAS DE SURTENSION au blocage contrairement à ce qu'on lit ici ou là à longueur de journée. Dans une self, la tension est en quadrature avec le courant. Donc le minimum de courant correspond à une tension non nulle (aux bornes de la self, en série avec le secteur et en opposition de phase avec celui-ci vu du triac, donc zéro volt vu du triac, me trompe-je ?) Un circuit dit "snubber" (100nF 250V alternatif + 100 ohm en série) est cablé en // entre A1 et A2 du triac. La fonction du condensateur est de maintenir un courant non nul dans le triac à la mise en conduction, le courant passant dans le bobinage ne s'établissant que trop lentement. Quant à la résistance en série avec le condensateur, elle sert à limiter le courant en question. Sans la résistance, c'est la destruction assurée du triac. De même une résistance de trop faible valeur fait veillir le triac prématurément et aboutit à une destruction plus ou moins rapide. Il existe maintenent des triacs qui se passent de snubber... -------------------------------------------------------------------------------- TEMPERATURES degres (Acquisition) (compte tenu de mon schéma) 20 (112) 30 (85) 40 (69) 50 (58) 60 (44) 80 (39) 90 (35) ================================================================================ La carte electronique répond aux signaux d'une télécommande TV infrarouge universelle Beaucoup de modèles conviennent, il faudra adapter les codes des touches dans ce programme le cas écheant (voir les 'case octet_IR of') dans la partie "main" Pour ma part, j'utilise une PHILIPS type 'UNIVERSAL SBC RU 252 II' ================================================================================ } { $W+ Warnings} {Warnings off} Device = mega32, VCC = 5; Import SysTick, LCDport, RTclock, {TickTimer,} RC5Rxport, FreqCount, ADCPort; From RTclock Import RTCtimer; From System Import Int64, float; Define ProcClock = 16000000; {Hertz} SysTick = 10, Timer0; {msec} // ATTENTION: necessaire pour l'horloge RTC et LCD RTclock = iData, DateTime; {Time, DateTime} RTCtimer = 4; // 4 cannaux RTCsource = SysTick {, adj}; { adj = +/-100} // TickTimer = Timer1; //le Timer1 de l'ATmega32 est un timer 16bits FreqTimer = Timer1; // (used 16bit Timer} StackSize = 512, iData; // (voir affi en runtime) FrameSize = 512, iData; LCDport = PortC; LCDtype = 44780; LCDrows = 4; {rows} LCDcolumns = 20; {columns per line} RC5Rxport = PinC, 7, negative; {Port, Pin#, polarity} RC5mode = rc_6bit; {command bit count} ADCchans = [8], iData; {use only 1 Channel, ADC7} ADCpresc = 128; Define_usr bouton_inc_h = %00100000; Implementation //============================================================================== {$IDATA} type Tprog = (couleur, blanc, laine, froid, rapide, test); const version : string = '3.3'; // icone_anime : string = '|/-\'; Labels_numeros : array [1..15] of string[8] = ('eau', 'lavage', 'vidange', 'eau', 'rincage1', 'vidange', 'eau', 'rincage2', 'vidange', 'eau', 'rincage3', 'vidange', 'eau', 'rincage4', 'essorage'); OCRmin_ESS : byte = 85; // vitesse la plus rapide OCRmin_purge : byte = 90; OCRmin_LAV : byte = 105; OCRmax_ESS : byte = 130; OCRmax_purge : byte = 130; OCRmax_LAV : byte = 130; OCRmax : byte = 130; // vitesse la plus lente // attention 162 est la limite au dela de laquelle on retablit les alternances à 100% // compte tenu de mon schéma P_cons_lav : word = 2000; // P_cons_defoul : word = 4000; P_cons_purge : word = 400; tps_rincage_max : word = 90; // secondes { Type Declarations } //============================================================================== {$IDATA} var // delay : SysTimer8; compteur1 : integer; ti : byte; ticked : boolean; timeout1 : boolean; // pour RTCtimer timeout2 : boolean; // pour RTCtimer timeout3 : boolean; // pour RTCtimer timeout4 : boolean; // pour RTCtimer stk1 : word; stk1_min : word; fram_free : word; fram_free_min : word; // FreqMode : tFreqCountMode; // Freq : word; Periode : word; OCR2_real : float; str12 : string[12]; rxAdr : byte; rxCmd : byte; adr1 : integer; cmd1 : integer; octet_IR : byte; //octet recu par le recepteur IR bitsix : byte; memobitsix : byte; nouveau : boolean; // pour l'anti rebond signaux IR acqui_ADC : word; pos_bt : byte; stop_imps : boolean; // force le blocage du triac relais1 : boolean; relais2 : boolean; EV1 : boolean; // électrovanne 1 (admision d'eau pour le prélavage) EV2 : boolean; // électrovanne 2 (admision d'eau pour le lavage et les rinçages) pompe : boolean; NIV1 : boolean; // pressostat NIV2 : boolean; // pressostat TXTniv : string[2]; Nom_prg : string[10]; affi_requis : boolean; temps_i : byte; // pour le generateur d'impulsions vers le triac moteur. compteur en secondes temps_ch_sens : byte; // pour le changement automatique de sens periode_ch_sens : byte; lavage : boolean; temps_lavage : word; // compteur en secondes tps_lavage_max : word; // secondes timeOUT_lavage : boolean; tps_pause : longword; // pause reglable de qq secondes ou plus lors du changement de sens (longue pour la laine) temps_pompe : word; // compteur en secondes temps_EV2 : word; // compteur en secondes rincage : boolean; temps_rincage : word; // compteur en secondes timeOUT_rincage : boolean; nb_rincage_max : byte; essorage : boolean; temps_essorage : word; // compteur en secondes tps_essr_max : word; timeOUT_ess : boolean; // F_purge : word; P_cons_ess : word; ajout_vt_esso : word; accelere : boolean; purge : boolean; temps_purge : word; // compteur en secondes tps_purge_max : word; timeOUT_purge : boolean; temps_defoul : word; tps_deffou_max : word; TimeOUT_defoul : boolean; temps_total : byte; // compteur en minutes num_requis : byte; remplissage : boolean; chauffage : boolean; chauffe_enable : boolean; brassage : boolean; defoulage : boolean; vidange : boolean; sens_enable : boolean; // permet ou bloque le changement de sens automatique du moteur toutes les 15s TimeOUT_sens : boolean; vert : boolean; Temperat_max : float; // temperature de lavage prog1 : Tprog; numero : byte; a_choisir : boolean; {--------------------------------------------------------------} {functions } procedure init_ports; // ports perso // 0 = entree, 1=sortie begin portA:= %00000000; DDRA:= %01111111; // portA[7] = entree Thermistance portB:= %00000000; DDRB:= %11111101; // portB[1] = T1 = entree (pour le frequencemetre) DDRC:= DDRC and %01111111; portD := %10100011; // les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC) DDRD:= DDRD or %10011000; // portD[6] en entree (ICP periodemetre) portD[7] en sortie (OC2) SFIOR:= SFIOR and %11111011; // p:50 (bit PUD du SFIOR) // (the Pull-up Disable – PUD bit in SFIOR disables the pull-up function for all pins in all ports when set) end; procedure interroge_IR; begin if RecvRC5(rxAdr, rxCmd) then // interroge IR adr1:= integer(rxAdr); cmd1:= {%00111111 and} integer(rxCmd); octet_IR:= byte(cmd1); // le bit6 (=64 decimal) est=1 un appui de touche de la zapette sur deux memobitsix:= bitsix; bitsix:= octet_IR and %01000000; if bitsix <> memobitsix then nouveau:= true; else nouveau:= false; endif; octet_IR:= octet_IR and %00111111; // on supprime l'info de repetition de l'octet // portx:= portx or ......; // allume LED else octet_IR:= $FF; // portx:= portx and %......; // eteint LED endif; if nouveau = false then octet_IR:= $FF; endif; // pas de repetition auto dans cette application end; procedure InitINTs; begin {Les 3 bits de poids faible de TCCR2 selectionnent l'horloge et le prescaler. voir page 119 et 125 du pdf Bit 6, 3 – WGM21:0 = 01 -> mode CTC voir p:125 du pdfCTC = "Clear Timer on Compare Match" (CTC) Mode In "Clear Timer on Compare mode" (CTC mode, WGM21:0 = 2), the counter is cleared to zero when the counter value (TCNT2) matches the OCR2. The OCR2 defines the top value for the counter, hence also its resolution. } TCCR2 := TCCR2 or %10001111; // p:125 TIMSK := TIMSK and %01111111; // INT Timer2 comp disable; Voir le datasheet ATmega32.pdf p:130} GICR := GICR or %01000000; // extINT0 enable; voir page 67 du pdf MCUCR := MCUCR or %00000010; // The falling edge of INT0 generates an interrupt request. p:67 du pdf end; (* interrupt Timer2COMP; { Commande de l'angle de fermeture du triac Cette interruption n'est plus utilisée (abandonnée parce que trop de télescopages avec les autres) On utilise directement le signal sur la sortie OCE la sortie OC2 Genere un signal "carré" qui bascule à chaque comparaison reussie. un petit circuit (monostable avec un NE555 et des inverseurs 40106) génere ensuite 2 impulsions espacées de 10ms pour commander les deux alternances en 50Hz, la periode vaut 20ms (1/50Hz) et la demi période vaut donc 10ms REMARQUE: The OCF2 Flag is automatically cleared when the interrupt is executed. (p:116 du pdf) } begin { portB:= portB or %00000001; // POUR TEST udelay(20); // POUR TEST portB:= portB and %11111110; // POUR TEST } end; *) interrupt Int0; // front descendant sur l'entree Int0 begin PushAllRegs; TCNT2:= 0; TCCR2:= TCCR2 or %00110000; // set OCE on compare match TCCR2:= TCCR2 or %10000000; // bit FOCE2 voir p:125 (force comparaison virtuelle pour RAZ OCE) // ce qui a pour effet de passer la sortie OCE à 1 if not stop_imps then TCCR2:= TCCR2 and %11101111; // clear OCE on compare match (generera le signal sur OCE) endif; PopAllRegs; end; procedure Affiche_STACK_free; // attention: ne pas appeller depuis une INT sinon fait planter begin LCDxy(16, 3); Write(LCDout, IntToStr(stk1 : 3 : '0') ); // LCDxy(16, 3); // Write(LCDout, IntToStr(fram_free : 3 : '0') ); fonction bugguée? chaque lecture consomme 1 octet! // end; procedure vitesse_mini; begin OCR2:= OCRmax; OCR2_real:= OCRmax; end; procedure relais1_on; begin portA:= portA or %00000100; relais1:= true; LCDxy(0, 3); Write(LCDout, '>'); end; procedure relais1_off; begin portA:= portA and %11111011; relais1:= false; LCDxy(0, 3); Write(LCDout, ' '); end; procedure relais2_on; begin portB:= portB or %00000100; relais2:= true; LCDxy(0, 3); Write(LCDout, '<'); end; procedure relais2_off; begin portB:= portB and %11111011; relais2:= false; LCDxy(0, 3); Write(LCDout, ' '); end; procedure EV2_on; // commande électrovanne2. //(on peut en rajouter... pour le prelavage par exemple...) begin portA:= portA or %00000010; EV2:= true; temps_EV2:= 0; LCDxy(5, 3); Write(LCDout, 'EV'); LCDxy(7, 3); Write(LCDout, '2'); end; procedure EV2_off; begin portA:= portA and %11111101; EV2:= false; if not(EV1) then // le dernier ferme la porte en sortant! LCDxy(5, 3); Write(LCDout, ' '); endif; LCDxy(7, 3); Write(LCDout, ' '); // chacun balaye devant sa porte end; procedure CHAUFFAGE_off; begin // stk1:= GetStackFree; if stk1 < stk1_min then stk1_min:= stk1; endif; // fram_free:= GetStackFree; if fram_free < fram_free_min then fram_free_min:= fram_free; endif; portB:= portB and %11101111; // relais chauffage chauffage:= false; periode_ch_sens:= 10; end; procedure CHAUFFAGE_on; begin //stk1:= GetStackFree; if stk1 < stk1_min then stk1_min:= stk1; endif; if chauffe_enable then stop_imps:= true; portB:= portB or %00010000; // relais chaufage chauffage:= true; periode_ch_sens:= 30; endif; end; procedure POMPE_on; begin Chauffage_OFF; portB:= portB or %00001000; // pompe pompe:= true; temps_pompe:= 0; end; procedure POMPE_off; begin portB:= portB and %11110111; // pompe pompe:= false; end; procedure detection_niveau_eau; // j'ai supprimé la detection du niveau2 (3/4 de cuve, c'est trop) begin //stk1:= GetStackFree; if stk1 < stk1_min then stk1_min:= stk1; endif; if (PinD and %00100000) = 0 then TXTniv:= 'Lo'; NIV1:= true; NIV2:= false; // elsif (PinD and %00100000) = 0 then // TXTniv:= 'Hi'; NIV1:= false; NIV2:= true; else NIV1:= false; NIV2:= false; TXTniv:= '--'; endif; end; procedure RAZ_tempos; begin // initialisation des compteurs avec des valeurs qui les bloquent temps_lavage:= 100*60; // en secondes temps_rincage:= 100*60; // en secondes temps_pompe:= 0; temps_EV2:= 0; // temps_fin_vidange:= 100; temps_essorage:= 100*60; // en secondes temps_defoul:= 100*60; temps_total:= 100; temps_ch_sens:= 5; tps_pause:= 1; prog1:= couleur; Temperat_max:= 40; tps_lavage_max:= 10*60; // secondes tps_pause:= 1; tps_essr_max:= 4*60; // secondes tps_purge_max:= 15; // secondes tps_deffou_max:= 30; // secondes periode_ch_sens:= 10; // secondes ti:= 0; end; procedure init_variables; begin stk1_min:= 1000; fram_free_min:= 1000; stop_imps:= true; remplissage:= false; lavage:= false; chauffe_enable:= false; brassage:= false; rincage:= false; nb_rincage_max:= 4; vidange:= false; essorage:= false; defoulage:= false; sens_enable:= false; a_choisir:= true; RAZ_tempos; affi_requis:= false; // F_purge:= 30; relais1_off; relais2_off; EV2_off; Pompe_off; OCR2:= 140; // [10..162] retard a la commutation du triac alimentant le moteur (162 = pas de tension) compteur1:= 0; Temperat_max:= 20; // en degres ºC end; procedure eteint_toutes_LED; begin portA:= portA or %00001000; // RAZ des deux CD4017 (éteint tout because les Q0 ne sont pas câblés) udelay(1); portA:= portA and %11110111; udelay(1); end; procedure allume_LED(num : byte); // n in [0..17] var n : byte; begin portA:= portA or %00001000; // RAZ des deux CD4017 (éteint tout because les Q0 ne sont pas câblés) udelay(1); portA:= portA and %11110111; udelay(1); inc(num); if num in [1..18] then if num < 10 then for n:= 1 to num do portA:= portA or %00010000; // CLK du premier CD4017 udelay(1); portA:= portA and %11101111; udelay(1); endfor; else for n:= 10 to num do portA:= portA or %00100000; // CLK du second CD4017 udelay(1); portA:= portA and %11011111; udelay(1); endfor; endif; endif; end; procedure STOP(str1 : string[20]); begin init_variables; // stop de tous les organes portA:= portA and %10111111; // eteint LED essorage allume_LED(16); LCDclr; LCDxy(0, 0); Write(LCDout, 'STOP logigiel '); LCDxy(0, 1); Write(LCDout, str1); LCDxy(0, 1); Write(LCDout, 'Appuyez bouton rouge'); mdelay(1000); repeat until (PinD and %00000010) = 0; // attend appui sur bouton rouge avant de reseter system_reset; end; procedure detection_bouton_ROUGE; // ARRET et RESET du programme begin //stk1:= GetStackFree; if stk1 < stk1_min then stk1_min:= stk1; endif; //fram_free:= GetFrameFree; if fram_free < fram_free_min then fram_free_min:= fram_free; endif; if (PinD and %00000010) = 0 then STOP('BT R'); endif; end; procedure detection_bouton_VERT; // ABREGE le lavage ou le rincage en cours (passe au numero suivant=vidange) begin if (PinD and %00000001) = 0 then vert:= true; LCDclr; LCDxy(10, 1); Write(LCDout, 'NEXT !'); mdelay(500); LCDclr; else vert:= false; endif; end; (* procedure ACQUI_vitessse; begin //Frequencemetre - acquisition de la vitesse du moteur if SemaStat(FreqCountSema) <> 0 then Freq:= GetFreqCounter; endif; end; *) procedure ACQUI_vitessse; begin //Periodemetre - acquisition de la vitesse du moteur Periode:= GetTimeCounter; end; procedure TEMPO_s(nb_secondes : longword); // boucle de tempo qui ne bloque pas l'arret d'urgence... begin disableInts; RTCTimer_Load(1, nb_secondes); // canal1, 3secondes enableInts; RTCTimer_Start(1); // start RTCTimer canal 1 timeout1:= false; // sera mis à true par l'INT RTCTimer repeat if (PinD and %00000010) = 0 then // detection bouton rouge STOP('Bt_R'); // reset le programme endif; detection_bouton_vert; until timeout1 or vert; vert:= false; end; procedure acqui_analogique; begin // LCDclr; acqui_ADC:= GetADC; // acqui_ADC:= acqui_ADC shr 2; // 8 bits au lieu de 10 de resolution case acqui_ADC of 859..865 : pos_bt:= 0; // 860 | 854..858 : pos_bt:= 1; // 856 | 848..853 : pos_bt:= 2; // 850 | 842..847 : pos_bt:= 3; // 844 | 836..841 : pos_bt:= 4; // 838 | 827..835 : pos_bt:= 5; // 830 | 816..826 : pos_bt:= 6; // 822 | 806..815 : pos_bt:= 7; // 811 | 791..805 : pos_bt:= 8; // 799 | 776..790 : pos_bt:= 9; // 785 | 756..775 : pos_bt:= 10; // 768 | 736..755 : pos_bt:= 11; // 749 | 711..735 : pos_bt:= 12; // 724 | 671..710 : pos_bt:= 13; // 693 | 631..670 : pos_bt:= 14; // 653 | 551..630 : pos_bt:= 15; // 603 | 501..550 : pos_bt:= 16; // 537 | 400..500 : pos_bt:= 17; // 445 | 250..350 : pos_bt:= 18; // 310 | 80..100 : pos_bt:= 19; // 92 | endcase; // LCDxy(0, 1); // Write(LCDout, ByteToStr(n : 3)); end; procedure regulation_TEMPERATURE; var T : float; t1 : integer; begin //mesure de la temperature acqui_ADC:= GetADC; acqui_ADC:= acqui_ADC shr 2; // 8 bits au lieu de 10 de resolution T:= (3500 / float(acqui_ADC)) - 11; // voir feuille de calcul Ooo LCDxy(8, 3); if (T > 0) and (T < 100) then t1:= round(T); Write(LCDout, 'T=' + IntToStr(t1 : 2) + ' '); if (T < (Temperat_max - 5) ) and not(chauffage) then relais1_off; relais2_off; chauffage_ON; elsif (T > Temperat_max) and chauffage then Chauffage_OFF; endif; endif; end; procedure Affiche_temps(nb_secondes : word); var h, mn, sec : byte; R1 : word; signe : char; begin h : = byte(nb_secondes div 3600); R1 : = nb_secondes mod 3600; mn : = byte(R1 div 60); sec : = byte(R1 mod 60); LCDxy(13, 0); LCDclrEOL; Write(LCDout, ByteToStr(mn : 2 : '0') + ':' + ByteToStr(sec : 2 : '0')); end; procedure change_SENS; begin //stk1:= GetStackFree; if stk1 < stk1_min then stk1_min:= stk1; endif; //fram_free:= GetFrameFree; if fram_free < fram_free_min then fram_free_min:= fram_free; endif; vitesse_mini; // pour ralentir stop_imps:= true; tempo_s(1); if relais1 and not relais2 then relais1_off; tempo_s(1); relais2_on; elsif relais2 and not(relais1) then relais2_off; tempo_s(1); relais1_ON; elsif not(relais1) and not(relais2) then // si aucun relais1_ON; elsif relais1 and relais2 then // si les deux relais2_off; endif; if chauffage = true then // brassage_pendant_chauffage; debraye pour TEST else tempo_s(tps_pause); vitesse_mini; // pour repartir lentement // stop_imps:= false; endif; end; procedure AFFICHAGES; // permsise toutes les secondes par le flag affi_requis. begin affi_requis:= false; if lavage then Affiche_temps(tps_lavage_max - temps_lavage); if chauffe_enable then regulation_temperature; // comporte un affichage donc ne pas deplacer dans 1 boucle rapide endif; endif; if chauffage then LCDxy(13, 3); Write(LCDout, 'CH'); else LCDxy(13, 3); Write(LCDout, ' '); endif; LCDxy(2, 3); Write(LCDout, TXTniv); //------------------------------------------------------------------------------ if rincage then Affiche_temps(tps_rincage_max - temps_rincage); endif; // stk1:= GetStackFree; //fram_free:= GetFrameFree; // Affiche_STACK_free; //------------------------------------------------------------------------------ { if temps_pause < 100 then inc(temps_pause); endif; // ne reboucle pas if temps_pause = 2 then vitesse_mini; pause1:= false; endif; endif; } //------------------------------------------------------------------------------ if defoulage then Affiche_temps(tps_deffou_max - temps_defoul); endif; //------------------------------------------------------------------------------ if essorage then Affiche_temps(tps_essr_max - temps_essorage); endif; if TimeOUT_sens then TimeOUT_sens:= false; change_sens; endif; end; procedure remplissage_NIV1(tps_plus : longword); // par EV2 begin remplissage:= true; LCDxy(3, 0); LCDclrEOL; Write(LCDout, 'EAU'); vitesse_mini; stop_imps:= true; sens_enable:= false; relais1_off; relais2_off; EV2_on; repeat detection_bouton_ROUGE; detection_bouton_vert; detection_niveau_eau; if affi_requis then // 1 fois par seconde AFFICHAGES; endif; LCDxy(2, 3); Write(LCDout, TXTniv); until NIV1 or NIV2 or vert; // oui, NIV2 convient bien entendu. vert:= false; if octet_IR = 12 then STOP('3'); endif; octet_IR:= 255; LCDxy(0, 2); LCDclrEOL; Write(LCDout, 'Remplissage +'); // un peu plus, pour eviter d'en reprendre un verre 10 fois de suite tempo_s(tps_plus); EV2_off; LCDclrLine(2); remplissage:= false; end; procedure RINCER(num_rincage : byte); begin rincage:= true; LCDxy(3, 0); LCDclrEOL; Write(LCDout, 'RINCAGE' + ByteToStr(num_rincage : 1)); stop_imps:= true; vitesse_mini; chauffe_enable:= false; Chauffage_OFF; periode_ch_sens:= 10; sens_enable:= true; stop_imps:= true; vitesse_mini; // pour partir lentement tempo_s(1); relais2_off; tempo_s(1); relais1_ON; tempo_s(1); stop_imps:= false; temps_rincage:= 0; //---------------------------- boucle rincage ---------------------------------- LCDclrLine(2); LCDxy(0, 2); Write(LCDout, 'boucle rincage'); timeOUT_rincage:= false; repeat detection_bouton_ROUGE; detection_bouton_vert; detection_niveau_eau; if not NIV1 then remplissage_NIV1(30); endif; // si le niveau baisse, on complete; stop_imps:= false; Periode:= GetTimeCounter; // 50Hz -> P=2000 if ((Periode > P_cons_lav + 10) or (Periode = 0)) and (OCR2_real > OCRmin_LAV) then // asservissement de la vitesse OCR2_real := OCR2_real - 0.01; //accelere endif; if (Periode < P_cons_lav - 10 ) and (Periode <> 0) and (OCR2_real < OCRmax_LAV) then OCR2_real := OCR2_real + 0.01; //ralentit endif; OCR2:= round(OCR2_real); udelay(50); if affi_requis and not vert then AFFICHAGES; endif; until timeOUT_rincage or vert; vert:= false; //------------------------------------------------------------------------------ vitesse_mini; stop_imps:= true; sens_enable:= false; stop_imps:= true; vitesse_mini; LCDxy(3, 0); LCDclrEOL; Write(LCDout, 'fin rinçage'); tempo_s(1); relais1_off; relais2_off; tempo_s(1); rincage:= false; LCDclr; end; procedure PURGER; // mini essorage à la fin de la vidange afin de purger l'eau du linge begin // offset_OCR2:= 0; Chauffage_OFF; EV2_off; LCDxy(3, 0); LCDclrEOL; Write(LCDout, 'PURGE'); stop_imps:= false; pompe_on; vitesse_mini; stop_imps:= true; tempo_s(1); relais2_off; tempo_s(1); relais1_ON; tempo_s(1); sens_enable:= false; stop_imps:= false; // demarrre le moteur temps_purge:= 0; purge:= true; //------------------------- boucle "esso-purge" -------------------------------- LCDclrLine(2); LCDxy(0, 2); Write(LCDout, 'boucle purge'); timeOUT_purge:= false; repeat detection_bouton_ROUGE; detection_bouton_vert; stop_imps:= false; Periode:= GetTimeCounter; // 50Hz -> P=2000 if ((Periode > P_cons_purge + 10) or (Periode = 0)) and (OCR2_real > OCRmin_LAV) then // asservissement de la vitesse OCR2_real := OCR2_real - 0.01; //accelere endif; if (Periode < P_cons_purge - 10 ) and (Periode <> 0) and (OCR2_real < OCRmax_LAV) then OCR2_real := OCR2_real + 0.01; //ralentit endif; OCR2:= round(OCR2_real); udelay(50); if affi_requis then AFFICHAGES; endif; until timeOUT_purge or vert; //------------------------------------------------------------------------------ stop_imps:= true; vitesse_mini; tempo_s(1); relais1_off; relais2_off; tempo_s(1); Pompe_off; vidange:= false; defoulage:= false; stop_imps:= true; purge:= false; LCDclr; end; procedure TEST_asservissement; var P_consigne : word; begin LCDclr; LCDxy(5, 1); LCDclrEOL; Write(LCDout, ' TEST vitesse'); stop_imps:= true; tempo_s(1); relais2_off; relais1_ON; tempo_s(1); sens_enable:= false; stop_imps:= false; P_consigne:= 1000; // + 100 * word(pos_bt); loop detection_bouton_ROUGE; { Periode:= GetTimeCounter; // 50Hz -> P=2000 if ((Periode > P_consigne + 10) or (Periode = 0)) and (OCR2 > OCRmin_LAV) then // asservissement de la vitesse OCR2:= OCR2 - 1; //accelere endif; if (Periode < P_consigne - 10 ) and (Periode <> 0) and (OCR2 < OCRmax_LAV) then OCR2:= OCR2 + 1; //ralentit endif; mdelay(10); } Periode:= GetTimeCounter; // 50Hz -> P=2000 if ((Periode > P_cons_lav + 10) or (Periode = 0)) and (OCR2_real > OCRmin_LAV) then // asservissement de la vitesse OCR2_real := OCR2_real - 0.01; //accelere endif; if (Periode < P_cons_lav - 10 ) and (Periode <> 0) and (OCR2_real < OCRmax_LAV) then OCR2_real := OCR2_real + 0.01; //ralentit endif; OCR2:= round(OCR2_real); udelay(50); endloop; end; procedure ESSORER; begin portA:= portA or %01000000; // allume LED essorage LCDclr; accelere:= false; // offset_OCR2:= 0; Chauffage_OFF; EV2_off; LCDxy(3, 0); LCDclrEOL; Write(LCDout, 'ESSORAGE'); stop_imps:= false; pompe_on; stop_imps:= true; tempo_s(1); relais2_off; tempo_s(1); relais1_ON; tempo_s(1); sens_enable:= false; stop_imps:= false; if tps_essr_max > 5*60 then tps_essr_max:= 5*60; endif; // securite temps_essorage:= 0; essorage:= true; LCDclrLine(2); LCDxy(0, 2); Write(LCDout, 'boucle essorage'); // mdelay(1000); timeOUT_ess:= false; accelere:= true; // add_F_ess sera incremente chaque seconde par RTCTickSecond // baseOCR2:= 130; // offset_OCR2:= 0; vitesse_mini; P_cons_ess:= 2000; // vitesse tres lente au depart pour defouler //------------------------- boucle essorage ------------------------------------ repeat detection_bouton_ROUGE; detection_bouton_vert; if vert then vert:= false; stop_imps:= true; tempo_s(12); // pour laisser le temps au tambour de ralentir OCR2_real:= 130; P_cons_ess:= 2000; stop_imps:= false; endif; stop_imps:= false; if temps_essorage > 10 then // 10s de defoulage P_cons_ess:= 20000 div (temps_essorage); if P_cons_ess < 50 then P_cons_ess :=50; endif; // ce qui fait accelerer la vitesse linéairement / temps (f=1/T) endif; Periode:= GetTimeCounter; // 50Hz -> P=2000 if ((Periode > P_cons_ess + 10) or (Periode = 0)) and (OCR2_real > OCRmin_ESS) then // asservissement de la vitesse OCR2_real := OCR2_real - 0.01; //accelere endif; if (Periode < P_cons_ess - 10 ) and (Periode <> 0) and (OCR2_real < OCRmax_ESS) then OCR2_real := OCR2_real + 0.01; //ralentit endif; OCR2:= round(OCR2_real); udelay(100); if affi_requis then AFFICHAGES; endif; until timeOUT_ess; vert:= false; //------------------------------------------------------------------------------ portA:= portA and %10111111; // eteint LED essorage LCDclr; Write(LCDout, 'FIN ESSORAGE'); LCDxy(0, 1); Write(LCDout, 'Lessive terminée'); stop_imps:= true; vitesse_mini; Pompe_off; vidange:= false; defoulage:= false; tempo_s(1); relais1_off; relais2_off; tempo_s(1); essorage:= false; allume_LED(16); while true do detection_bouton_ROUGE; endwhile; end; { procedure DEFOULER; var eau_restante : boolean; begin if tps_deffou_max > 5*60 then tps_deffou_max:= 5*60; endif; // securite chauffe_enable:= false; Chauffage_OFF; EV2_off; stop_imps:= true; LCDxy(3, 0); LCDclrEOL; Write(LCDout, 'DEFOULAGE'); LCDclrLine(2); LCDxy(0, 2); Write(LCDout, 'vidange'); // par sécurité eau_restante:= false; // a priori pompe_on; repeat detection_bouton_ROUGE; detection_bouton_vert; detection_niveau_eau; if NIV1 then eau_restante:= true; endif; until not(NIV1) or vert; // normalement on sort dès la première passe if eau_restante then LCDxy(0, 2); // normalement on ne passe pas ici Write(LCDout, 'VIDANGE ++'); tempo_s(30); endif; LCDclrLine(2); LCDxy(3, 0); LCDclrEOL; Write(LCDout, 'DEFOULAGE' ); vitesse_mini; stop_imps:= true; tempo_s(1); relais2_off; tempo_s(1); relais1_ON; tempo_s(1); sens_enable:= false; stop_imps:= false; // demarrre le moteur temps_defoul:= 0; defoulage:= true; // le moteur tournera a la vitesse de rincage //------------------------- boucle defoulage ----------------------------------- LCDclrLine(2); LCDxy(0, 2); Write(LCDout, 'boucle defoulage'); TimeOUT_defoul:= false; repeat detection_bouton_ROUGE; detection_bouton_vert; // asservissement de la vitessse du moteur Periode:= GetTimeCounter; // 50Hz -> P=2000 if ((Periode > P_cons_defoul + 10) or (Periode = 0)) and (OCR2_real > OCRmin_LAV) then // asservissement de la vitesse OCR2_real := OCR2_real - 0.01; //accelere endif; if (Periode < P_cons_defoul - 10 ) and (Periode <> 0) and (OCR2_real < OCRmax_LAV) then OCR2_real := OCR2_real + 0.01; //ralentit endif; OCR2:= round(OCR2_real); udelay(50); if affi_requis then AFFICHAGES; endif; until TimeOUT_defoul or vert; //------------------------------------------------------------------------------ stop_imps:= true; vitesse_mini; defoulage:= false; LCDclr; end; } procedure VIDANGER; begin LCDxy(3, 0); LCDclrEOL; Write(LCDout, 'VIDANGE'); chauffe_enable:= false; Chauffage_OFF; vidange:= true; LCDclrLine(2); LCDxy(0, 2); Write(LCDout, 'Pompe en marche'); pompe_on; affi_requis:= false; repeat detection_bouton_ROUGE; detection_bouton_vert; detection_niveau_eau; if affi_requis then // 1 fois par seconde affi_requis:= false; LCDxy(2, 3); Write(LCDout, TXTniv); endif; until not(NIV1 or NIV2) or vert; vert:= false; //octet_IR:= 255; // a partir de cet instant il faut encore au minimum 16s pour vider la cuve // On fait donc tourner la pompe 20s supplémentaires LCDxy(3, 0); LCDclrEOL; Write(LCDout, 'VIDANGE +'); LCDxy(2, 3); Write(LCDout, TXTniv); tempo_s(20); Pompe_off; LCDclrLine(2); vidange:= false; end; procedure Affiche_1octet(octet_i : byte); begin LCDxy(8, 3); Write(LCDout, 'x=' + ByteToStr(octet_i : 3 : '0') ); end; procedure test_IR; var chr1 : char; begin LCDclr; LCDxy(0, 0); Write(LCDout, 'Lave Linge v: ' + version ); LCDxy(0, 1); Write(LCDout, 'Test IR' ); LCDxy(0, 2); Write(LCDout, 'ok ou 0 pour sortir' ); repeat detection_bouton_ROUGE; interroge_IR; if nouveau = true then chr1:= '*'; else chr1:= ' '; endif; if octet_IR <> 255 then LCDxy(0, 3); Write(LCDout, ByteToStr(octet_IR : 3 : ' ') + ' ' + chr1); mdelay(200); endif; until (octet_IR = 0) or (octet_IR = 23); STOP('4'); // reset end; procedure LAVER; begin lavage:= true; if tps_lavage_max > 15*60 then tps_lavage_max:= 15; endif; // securite if Temperat_max > 80 then Temperat_max:= 80; endif; // securite LCDxy(3, 0); LCDclrEOL; Write(LCDout, 'LAVAGE'); LCDclrLine(1); LCDxy(7, 1); Write(LCDout, Nom_prg); sens_enable:= true; stop_imps:= true; tempo_s(1); relais2_off; tempo_s(1); relais1_ON; tempo_s(1); vitesse_mini; // pour partir lentement temps_ch_sens:= 0; temps_lavage:= 0; // de temps est incrementé par la procedure RTCtickMinute //----------------------------- BOUCLE LAVAGE ---------------------------------- LCDclrLine(2); LCDxy(0, 2); Write(LCDout, 'boucle lavage'); timeOUT_lavage:= false; repeat detection_bouton_ROUGE; detection_bouton_vert; detection_niveau_eau; if not NIV1 then remplissage_NIV1(30); endif; // si le niveau baisse, on complete; if (NIV1 or NIV2) and (prog1 <> froid) and (temps_lavage > 30 ) and (temps_lavage < 5*60 ) then //on ne chaufffe pas avant 30s (brassage pour melanger le detergent) ni après 5mn de tournage //(la temperature descendra alors lentement... je prefère ça à rechauffer à plusieurs reprises) chauffe_enable:= true; else chauffe_enable:= false; Chauffage_OFF; endif; stop_imps:= chauffage; // le moteur ne peut tourner qu'en l'absence de chauffage; sens_enable:= not(chauffage); Periode:= GetTimeCounter; // 50Hz -> P=2000 if ((Periode > P_cons_lav + 10) or (Periode = 0)) and (OCR2_real > OCRmin_LAV) then // asservissement de la vitesse OCR2_real := OCR2_real - 0.01; //accelere endif; if (Periode < P_cons_lav - 10 ) and (Periode <> 0) and (OCR2_real < OCRmax_LAV) then OCR2_real := OCR2_real + 0.01; //ralentit endif; OCR2:= round(OCR2_real); udelay(50); if affi_requis then AFFICHAGES; endif; until timeOUT_lavage or vert; vert:= false; //-------------------------------- FIN LAVAGE ---------------------------------- Chauffage_OFF; chauffe_enable:= false; LCDxy(3, 0); LCDclrEOL; Write(LCDout, 'fin lavage'); vitesse_mini; sens_enable:= false; stop_imps:= true; vitesse_mini; tempo_s(1); relais1_off; relais2_off; lavage:= false; LCDclr; end; procedure choix_programme; begin disableInts; LCDclr; LCDxy(0, 0); Write(LCDout, '==== PROGRAMME: ====' ); LCDxy(0, 1); Write(LCDout, '1:COULEUR 2:BLANC ' ); LCDxy(0, 2); Write(LCDout, '3:LAINE 4:FROID ' ); LCDxy(0, 3); Write(LCDout, '5:RAPIDE 6:TEST ' ); repeat detection_bouton_ROUGE; interroge_IR; until octet_IR in [1..6, 12]; case octet_IR of 1 : prog1:= couleur; Nom_prg:= 'Couleur'; Temperat_max:= 40; tps_lavage_max:= 10*60; tps_pause:= 1; nb_rincage_max:= 4; tps_essr_max:= 4*60; tps_deffou_max:= 15; | 2 : prog1:= blanc; Nom_prg:= 'Blanc'; Temperat_max:= 60; tps_lavage_max:= 15*60; tps_pause:= 1; nb_rincage_max:= 4; tps_essr_max:= 4*60; tps_deffou_max:= 15; | 3 : prog1:= laine; Nom_prg:= 'Laine'; Temperat_max:= 30; tps_lavage_max:= 10*60; tps_pause:= 15; nb_rincage_max:= 4; tps_essr_max:= 2*60; tps_deffou_max:= 20; | 4 : prog1:= froid; Nom_prg:= 'A froid'; Temperat_max:= 0; tps_lavage_max:= 10*60; tps_pause:= 1; nb_rincage_max:= 3; tps_essr_max:= 4*60; tps_deffou_max:= 15; | 5 : prog1:= rapide; Nom_prg:= 'Rapide'; Temperat_max:= 40; tps_lavage_max:= 5*60; tps_pause:= 1; nb_rincage_max:= 2; tps_essr_max:= 2*60; tps_deffou_max:= 15; | 6 : prog1:= test; Nom_prg:= 'Test'; Temperat_max:= 30; tps_lavage_max:= 1*60; tps_pause:= 1; nb_rincage_max:= 1; tps_essr_max:= 1*60; tps_deffou_max:= 10; | 12 : STOP('8'); | endcase; LCDclr; case prog1 of couleur : Write(LCDout, 'COULEUR 40 deg 10mn'); | blanc : Write(LCDout, 'BLANC 60 deg 15mn'); | laine : Write(LCDout, 'LAINE 30 deg 10mn'); | froid : Write(LCDout, 'FROID -- 15mn'); | rapide : Write(LCDout, 'RAPIDE 40 deg 5mn'); | test : Write(LCDout, 'TEST 30 deg 1mn'); | endcase; tempo_s(3); LCDclr; temps_total:= 0; enableInts; end; procedure numero_15; // ESSORAGE begin numero:= 15; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); vidanger; purger; LCDclr; LCDxy(0, 0); Write(LCDout, '5 pour essorer' ); LCDxy(0, 1); Write(LCDout, 'bouton rouge = stop' ); repeat detection_bouton_ROUGE; interroge_IR; until (octet_IR = 5); essorer; LCDclr; STOP('5'); end; procedure numero_14; // RINCAGE 4 facultatif begin numero:= 14; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); rincer(4); num_requis:= 15; end; procedure numero_13; begin numero:= 13; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); if nb_rincage_max > 3 then remplissage_NIV1(30); num_requis:= 14; else num_requis:= 15; // vers essorage endif; end; procedure numero_12; begin numero:= 12; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); vidanger; purger; num_requis:= 13; end; procedure numero_11; // RINCAGE 3 facultatif begin numero:= 11; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); rincer(3); num_requis:= 12; end; procedure numero_10; begin numero:= 10; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); if nb_rincage_max > 2 then remplissage_NIV1(30); num_requis:= 11; else num_requis:= 15; // vers essorage endif; end; procedure numero_9; begin numero:= 9; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); vidanger; purger; num_requis:= 10; end; procedure numero_8; // RINCAGE 2 begin numero:= 8; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); rincer(2); num_requis:= 9; end; procedure numero_7; begin numero:= 7; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); remplissage_NIV1(30); num_requis:= 8; end; procedure numero_6; //bonjour chez vous begin numero:= 6; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); vidanger; purger; num_requis:= 7; end; procedure numero_5; // RINCAGE 1 begin numero:= 5; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); rincer(1); num_requis:= 6; end; procedure numero_4; begin numero:= 4; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); remplissage_NIV1(30); num_requis:= 5; end; procedure numero_3; begin numero:= 3; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); vidanger; purger; num_requis:= 4; end; procedure numero_2; begin if a_choisir then choix_programme; endif; numero:= 2; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); laver; num_requis:= 3; end; procedure numero_1; begin choix_programme; a_choisir:= false; // pour ne pas devoir le choisir une 2eme fois au numero_2 numero:= 1; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); vidanger; remplissage_NIV1(30); // niveau haut mais inferieur à NIV2 (qui demande 3mn de remplissage!) num_requis:= 2; end; procedure numero_0; begin numero:= 0; allume_LED(numero); LCDxy(0, 0); Write(LCDout, ByteToStr(numero : 2)); STOP('6'); end; procedure RTCtimer(chan : byte); // CallBack from RTCtimer begin if chan = 1 then timeout1:= true; endif; end; {$NOSAVE} procedure RTCtickSecond; // CallBack from RTClock //var // char1 : char; begin PushAllRegs; // sauvegarde sur la pile et non dans iData en statique // portA:= portA xor %00100000; // fait clignoter une LED pour TEST if lavage then if (temps_lavage < 120*60) and not(chauffage) then // ne boucle pas 120mn= 2h inc(temps_lavage); if temps_lavage = tps_lavage_max then timeOUT_lavage:= true; endif; endif; endif; //------------------------------------------------------------------------------ if rincage then if temps_rincage < 120*60 then inc(temps_rincage); // ne boucle pas if temps_rincage = tps_rincage_max then timeOUT_rincage:= true; endif; endif; endif; //------------------------------------------------------------------------------ if essorage then if temps_essorage < 120*60 then inc(temps_essorage); if temps_essorage = tps_essr_max then timeOUT_ess:= true; endif; endif; // ne boucle pas endif; //------------------------------------------------------------------------------ if purge then if temps_purge < 120*60 then inc(temps_purge); if temps_purge = tps_purge_max then timeOUT_purge:= true; endif; endif; // ne boucle pas // inc(offset_OCR2); //pour accelerer petit à petit endif; //------------------------------------------------------------------------------ if defoulage then if temps_defoul < 120*60 then inc(temps_defoul); if temps_defoul = tps_deffou_max then TimeOUT_defoul:= true; endif; endif; endif; //------------------------------------------------------------------------------ if pompe then inc(temps_pompe); endif; if temps_pompe > 5*60 then STOP('PB vidange > 5mn'); endif; if EV2 then inc(temps_EV2); endif; if temps_EV2 > 5*60 then STOP('PB niveau eau'); endif; //------------------------------------------------------------------------------ inc(temps_ch_sens); if temps_ch_sens >= periode_ch_sens then temps_ch_sens:= 0; if sens_enable then TimeOUT_sens:= true; // changement de sens requis endif; endif; //------------------------------------------------------------------------------ affi_requis:= true; // pas d'affichage directement dans une interruption PopAllRegs; end; procedure RTCtickMinute; // CallBack from RTClock begin if temps_total < 255 then inc(temps_total); endif; end; procedure RTCtickHour; // CallBack from RTClock begin end; procedure choix_numero; var num : byte; begin disableInts; LCDclr; LCDxy(0, 0); Write(LCDout, '== CHOIX NUMERO ====' ); LCDxy(0, 1); Write(LCDout, 'Touches < > ok' ); num:= 1; allume_LED(num); LCDxy(0, 3); Write(LCDout, ByteToStr(num) + ' ' + Labels_numeros[num]); repeat detection_bouton_ROUGE; octet_IR:= 255; repeat detection_bouton_ROUGE; interroge_IR; until octet_IR in [16, 17, 23]; case octet_IR of 16 : if num < 15 then inc(num); mdelay(20); endif; | 17 : if num > 1 then dec(num); mdelay(20); endif; | endcase; allume_LED(num); LCDclrLine(3); LCDxy(0, 3); Write(LCDout, ByteToStr(num) + ' ' + Labels_numeros[num]); until (octet_IR = 23); LCDclr; enableInts; num_requis:= num; if num_requis = 2 then a_choisir:= true; endif; end; //============================================================================== { Main Program } {$IDATA} begin init_variables; init_ports; allume_LED(0); InitINTs; ticked:= true; Chauffage_OFF; LCDclr; { clear display } LCDcursor(false, false); { display on, cursor off & no blink } Write(LCDout, 'RESET'); if (MCUCSR and %00001000) <> 0 then Write(LCDout, ' WT-Dog'); while true do detection_bouton_ROUGE; endwhile; endif; mdelay(300); LCDclr; Write(LCDout, 'Lave Linge v: ' + version ); tempo_s(2); // SetFreqCountMode(TFreqBase100kHz); // mode frequencemetre SetFreqCountMode(TTimeBase100ms); // mode periodemetre num_requis:= 0; LCDclr; LCDxy(0, 0); Write(LCDout, '1:LAVAGE'); LCDxy(0, 1); Write(LCDout, '2:Choix Numero'); LCDxy(0, 2); Write(LCDout, '3:vitesse'); LCDxy(0, 3); Write(LCDout, '9:Test IR'); octet_IR:= 255; repeat detection_bouton_ROUGE; interroge_IR; until octet_IR in [1, 2, 3, 9, 32, 33]; // attention cette LISTE doit etre exacte case octet_IR of 1 : numero_1; | 2 : choix_numero; | 3 : TEST_asservissement; | 9 : Test_IR; | 12 : STOP('9'); | 32 : inc(OCR2); //F_essorage:= F_essorage + 5; | 33 : dec(OCR2); //F_essorage:= F_essorage - 5; | else LCDclr; LCDxy(0, 0); Write(LCDout, 'err1 - choix inconnu'); while true do detection_bouton_ROUGE; endwhile; endcase; enableInts; // ne pas remonter cette ligne. loop case num_requis of 1 : numero_1; | 2 : numero_2; | 3 : numero_3; | 4 : numero_4; | 5 : numero_5; | 6 : numero_6; | 7 : numero_7; | 8 : numero_8; | 9 : numero_9; | 10 : numero_10; | 11 : numero_11; | 12 : numero_12; | 13 : numero_13; | 14 : numero_14; | 15 : numero_15; | else LCDxy(0, 0); Write(LCDout, 'err2 - n requis HS'); while true do detection_bouton_ROUGE; endwhile; endcase; endloop; // RTCtimer_Stop(0); end lavelinge. |