program lavelinge;
{===============================================================================
par Silicium628 - voir la description de cette realisation sur mon site
(chercher silicium628 sur Google.fr, on ne peut pas le rater!)
versions: voir plus bas dans la partie "const" - derniere mise à jour 3 mars 2009
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.12';
//  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      = 60;  // vitesse la plus rapide
  OCRmin_purge   : byte      = 80;
  OCRmin_LAV     : byte      = 75;  // 95

  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      = 1500; // 2000
//  P_cons_defoul  : word      = 4000;
  P_cons_purge   : word      = 300;   // 300
  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;
  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:= 12;
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:= 8;
  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:= 20;     // secondes      //15
  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;
// lit la position d'un potentiometre à resistances CMS discretes  (63k au total)
begin
//  LCDclr;

  acqui_ADC:= GetADC;  // resolution 10bits

  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;
// allume le chauffage si la température est trop basse sinin l'éteint
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);
  if chauffage then tempo_s(19); endif;  // ARRET
    relais2_on;
  elsif relais2 and not(relais1) then
    relais2_off;
    tempo_s(1);
  if chauffage then tempo_s(19); endif;  // ARRET
    relais1_ON;
  elsif not(relais1) and not(relais2) then   // si aucun
    relais1_ON;
  elsif relais1 and relais2 then  // si les deux
    relais2_off;
  endif;

  tempo_s(tps_pause);
  vitesse_mini;    // pour repartir lentement
  
  TimeOUT_sens:= false;
  temps_ch_sens:= 0;
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
      udelay(200);

    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(20);

    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);

  tempo_s(10);   // 10s de vidange supplementaires à la fin
  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:= 2000;

  loop
    detection_bouton_ROUGE;
    acqui_analogique;
    P_consigne:= 1000 + 100 * word(pos_bt);

    Periode:= GetTimeCounter; // 50Hz -> P=2000
    if ((Periode > P_consigne + 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_consigne - 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;

    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(20);

    if affi_requis then
      AFFICHAGES;
    endif;

  until timeOUT_ess;
  vert:= false;
//------------------------------------------------------------------------------
  portA:= portA and %10111111;  // eteint LED essorage
  LCDclr;
  Write(LCDout, 'FIN ESSORAGE');

  stop_imps:= true;
  vitesse_mini;

  tempo_s(1);
  relais1_off;
  tempo_s(1);
  relais2_off;
  tempo_s(1);

  tempo_s(15);   // 15s de vidange supplementaires à la fin
  Pompe_off;

  vidange:= false;
  defoulage:= false;
  essorage:= false;

  LCDxy(0, 1);
  Write(LCDout, 'Lessive terminée');

  allume_LED(16);
  while true do
    detection_bouton_ROUGE;
  endwhile;
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;
// à 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 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 > 30*60 then tps_lavage_max:= 30*60; endif; // 30mn max (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;  // le 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 > (5*60) ) and (temps_lavage < (15*60) ) then
//on ne chaufffe pas avant 5 minutes (prelavage a froid)
//ni après 15mn de tournage (because on arrete le moteur pendant le chauffage et ça risquerait de trop
// augmenter la durée de la lesssive)
//(la temperature descendra alors lentement... je prefère ça à rechauffer à plusieurs reprises)
// pouquoi ne pas tourner en chauffant? because trop de consomation des 2 bobines de relais sur l'alim...
      chauffe_enable:= true;    // CHAUFFAGE
    else
      chauffe_enable:= false;
      Chauffage_OFF;
    endif;

    stop_imps:= false;
//    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.02;  //ralentit    //0.01
    endif;
    OCR2:= round(OCR2_real);
    udelay(25);    // 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' );
  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:= 20*60;
         tps_pause:= 1;
         nb_rincage_max:= 4;
         tps_essr_max:= 3*60;
         tps_deffou_max:= 15;
       |
    2  : prog1:= blanc;
         Nom_prg:= 'Blanc';
         Temperat_max:= 60;
         tps_lavage_max:= 25*60;
         tps_pause:= 1;
         nb_rincage_max:= 4;
         tps_essr_max:= 3*60;
         tps_deffou_max:= 15;
       |
    3  : prog1:= laine;
         Nom_prg:= 'Laine';
         Temperat_max:= 30;
         tps_lavage_max:= 15*60;
         tps_pause:= 2;
         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:= 20*60;
         tps_pause:= 1;
         nb_rincage_max:= 4;
         tps_essr_max:= 3*60;
         tps_deffou_max:= 15;
       |
    5  : prog1:= rapide;
         Nom_prg:= 'Rapide';
         Temperat_max:= 30;
         tps_lavage_max:= 10*60;
         tps_pause:= 1;
         nb_rincage_max:= 2;
         tps_essr_max:= 2*60;
         tps_deffou_max:= 15;
       |

    12 : STOP('8');
       |
  endcase;
  LCDclr;
  Write(LCDout, Nom_prg + ' ' + IntToStr(round(Temperat_max)) + 'deg ' + IntToStr(tps_lavage_max div 60) + 'mn');
  LCDxy(0, 1);
  Write(LCDout, '5 prem. mn = froid');
  LCDxy(0, 2);
  Write(LCDout, ByteToStr(nb_rincage_max)  + ' rincages ');
  tempo_s(5);
  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(45);
    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(45);
    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(47);
  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(45);
  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;

      if (temps_essorage > 10) then  // 10s de defoulage
        P_cons_ess:= 10000 div (temps_essorage);
// ce qui fait accelerer la vitesse linéairement / temps    (f=1/T)
        if  P_cons_ess < 50 then
          P_cons_ess := 50;
        endif;
      endif;

{
      if (temps_essorage >= 90) and (temps_essorage < 110) then
        P_cons_ess:= 2000; // redéfoulage avec le linge presque sec
      endif;
}

{
     if (temps_essorage >= 110 ) then
        P_cons_ess:= 50;  // grande vitesse, supérieure à la fréquence de résonnance du tambour
      endif;              // la resonnance du tambour se situe aux environs de 80
}


    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

    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 NC');
      while true do
        detection_bouton_ROUGE;
      endwhile;
    endcase;

  endloop;



//  RTCtimer_Stop(0);
end lavelinge.





