/***************************************************************************
* http://www.silicium628.fr/ *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; *
* See the GNU General Public License for more details. *
***************************************************************************/
/*=========================================================================================
Lave_Linge
par Silicium628 - voir la description de cette réalisation sur mon site
versions: voir plus bas dans la partie "const" - dernière mise à jour 2012-11-02
La version est visible sur l'afficheur au reset du programme
Quel plaisir de programmer un uControleur en PASCAL ! disais-je:-)
Quel plaisir donc, de programmer un uControleur en C ! et sous Linux :-))
===========================================================================================
PRINCIPE:
PRINCIPE:
--------------------------------------------------------------------------------
Le moteur est un modèle 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.
--------------------------------------------------------------------------------
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 ISR (INT0_vect) va faire partir le compteur du Timer2
voir la suite de l'explication sur la page html associee a ce montage
--------------------------------------------------------------------------------
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 maintenant 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'
================================================================================
======================== Rappel ATmega32 =======================================
Device Flash EEPROM SRAM Speed Volts
ATmega32 32kB 1024B 2048B 0 - 16MHz 4.5 - 5.5V
================================================================================
*/
#include "lave_linge.h"
#define F_CPU 16000000
#include <math.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <avr/wdt.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "dm_lcd.c" // ATTENTION: la configuration des ports dédiés a l'afficheur LCD est effectuée dans le fichier "dm_lcd.h"
#include <avr/pgmspace.h>
extern uint8_t asmfunction(uint8_t); // all assembly subroutines must be declared external to the C source
#define false 0
#define true 1
#define OCRmin 60 // -> rapide
#define OCRdemar 105 // -> lent
#define OCRmax 140 // attention: 162 est la limite au dela de laquelle on rétablit les alternances à 100% (compte tenu de mon schéma)
// #define P_cons_purge 188
#define tps_rincage_max 120
char * version = "5.1";
char Labels_numeros[16][8+1] = {"STOP", "eau", "lavage", "vidange", "eau",
"rincage1", "vidange", "eau", "rincage2", "vidange", "eau",
"rincage3", "vidange", "eau", "rincage4", "essorage"};
/*
RAPPEL variables avr-gcc (vérifiable avec le .map)
char 1 -128 .. 127 ou caractères
unsigned char 1 0 .. 255 (equiv à byte du pascal)
uint8_t 1 (c'est la même chose que l'affreux 'unsigned char')
char toto[n] n
int 2 -32768 .. 32767
int16_t 2 idem 'int'
short int 2 pareil que int (?)
unsigned int 2 0 .. 65535
uint16_t 2 idem 'unsigned int'
long int 4 -2 147 483 648 Ã 2 147 483 647
int32_t 4 32 bits ; idem long int
long long int 8
unsigned long int 4 32 bits ; 0 .. 4 294 967 295 (4,2 x 10^9)
uint32_t 4 32 bits ; idem 'unsigned long int'
float 4
double 4 // (oui, 32 bits, et pas 8octets (64bits) comme en C standard)
La déclaration char JOUR[7][9];
réserve l'espace en mémoire pour 7 mots contenant 9 caractères (dont 8 caractères significatifs).
*/
//types
enum Prg { couleur = 0, blanc, laine, froid, rapide, test };
// variables en RAM
int Prg = froid;
uint8_t rxAdr;
uint8_t rxCmd;
uint8_t adr1;
uint8_t cmd1;
uint8_t rxAdr_IR;
uint8_t rxCmd_IR;
uint8_t bit_bascul_IR; // BOOLEAN
uint8_t octet_IR; //octet recu par le recepteur IR
uint8_t bitsix;
uint8_t memo_bitsix;
uint8_t nouveau; // BOOLEAN // pour l'anti rebond signaux IR
uint8_t stop; // BOOLEAN
uint8_t stop_imps; // boolean // force le blocage du triac
uint8_t memo_stop_imps; // boolean
uint16_t nb_ms2;
int compteur1;
uint8_t ti;
uint16_t periode;
float OCR2_real;
char str12[12+1];
uint16_t acqui_ADC;
float T; // temperature
uint8_t pos_bt;
uint8_t pause; //boolean
uint8_t relais1; // boolean
uint8_t relais2; // boolean
uint8_t EV1; // boolean // électrovanne 1 (admision d'eau pour le prélavage)
uint8_t EV2; // boolean // électrovanne 2 (admision d'eau pour le lavage et les rinçages)
uint8_t pompe; // boolean
uint8_t memo_pompe; // boolean
uint8_t NIV1; // boolean // pressostat
uint8_t NIV2; // boolean // pressostat
char TXTniv[2+1];
char Nom_prg1[10+1];
char Nom_prg2[10+1];
uint8_t affi_requis; // boolean
uint8_t temps_i; // pour le generateur d'impulsions vers le triac moteur. compteur en secondes
uint8_t temps_ch_sens; // pour le changement automatique de sens
uint8_t periode_ch_sens; // en secondes
uint8_t prelavage; // boolean
uint8_t lavage; // boolean
uint8_t memo_lavage; // boolean
uint16_t temps_lavage; // compteur en secondes
uint16_t duree_lavage; // duree totale (consigne, en minutes)
uint8_t mn_froides; // nombre de minutes de lavage à froid au bébut du cycle
uint16_t P_cons_lav;
uint16_t tps_lavage_max; // secondes
uint8_t timeOUT_lavage; // boolean
uint16_t temps_chauffage; // compteur en secondes
uint8_t temps_chauffage_max; // en secondes
uint8_t timeOUT_chauffage; //boolean
uint16_t temps_brassage; // compteur en secondes
uint8_t temps_brassage_max; // en secondes
uint8_t timeOUT_brassage; //boolean
uint16_t temps_pompe; // compteur en secondes
uint16_t temps_EV2; // compteur en secondes
uint8_t rincage; // boolean
uint8_t memo_rincage; // boolean
uint16_t temps_rincage; // compteur en secondes
uint8_t timeOUT_rincage; // boolean
uint8_t nb_rincage_max;
uint8_t essorage; // boolean
int16_t temps_essorage; // compteur en secondes
uint16_t tps_essr_max;
uint8_t timeOUT_ess; // boolean
uint16_t P_cons_ess;
uint16_t ajout_vt_esso;
uint8_t accelere; // boolean
uint8_t purge; // boolean
uint16_t temps_purge; // compteur en secondes
uint16_t tps_purge_max;
uint8_t timeOUT_purge; // boolean
uint16_t P_cons_purge;
uint16_t temps_defoul;
uint16_t tps_defoul_max;
uint8_t timeOUT_defoul; // boolean;
uint8_t temps_total; // compteur en minutes
uint8_t num_requis;
uint8_t remplissage; // boolean
uint8_t memo_remplissage; // boolean
uint8_t chauffage; // boolean
uint8_t memo_chauffage; // boolean
uint8_t chauffe_enable; // boolean
uint8_t memo_chauffe_enable; // boolean;
uint8_t brassage; // boolean
uint8_t defoulage; // boolean
uint8_t vidange; // boolean
uint8_t memo_vidange; // boolean
uint8_t sens_enable; // boolean permet ou bloque le changement de sens automatique du moteur toutes les 15s
uint8_t TimeOUT_sens; // boolean
uint8_t vert; // boolean
float Temperat_max; // temperature de lavage
uint8_t numero;
uint8_t a_choisir; // boolean
//--------------------------------------------------------------
void init_ports(void) // ports perso DDRx 0=entree, 1=sortie
{
PORTA = 0b00000000;
DDRA = 0b01111111; // portA[7] = entree Thermistance
PORTB = 0b00000000;
DDRB = 0b11111101; // portB[1] = T1 = entree (pour le frequencemetre)
DDRC = 0b01111111; // PC7 en entree (IR)
PORTD = 0b10100011; // les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC)
DDRD |= 0b10011000; // portD[6] en entree (ICP periodemetre) portD[7] en sortie (OC2)
SFIOR &= 0b11111011; // 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)
}
void InitINTs(void)
{
/*
TIMER0 ------------------------------------------------------------------------------------------------------
utilise comme base de temps 1ms (et 1s)
*/
TCCR0 |= 0b01001000; // Timer0 en mode CTC (Clear Timer on Compare Match): WGM00=1 (bit6) et WGM01=0 (bit3
//voir p:74 et 80
TCCR0 |= 0b00000011; // Timer0 prescaller = 1/64. 0CS0[2,1,0] = 011 voir p:82 et p:80
TIMSK |= 0b00000010; // Timer/Counter0 Output Compare Match Interrupt Enable.( Bit 1 – OCIE0) p:82
OCR0 = 250; // Timer0 freq = 16MHz/64/250= 1kHz (T=1ms)
/*
TIMER1 16 bits -------------------------------------------------------------------------------------------------
utilise comme PERIODEMETRE pour determiner la vitesse de rotation du moteur
Le Timer1 s'incremente avec clk/256 (From prescaler)
16MHz / 256 = 62500Hz (=16us)
Le Timer1 sera utilise avec la fonction 'capture' sur pin ICP1 (PD6)
Le front sur ICP appelle l'interruption ISR (TIMER1_CAPT_vect) (voir plus bas)
Cette interruption recopie la valeur du compteur du timer1 (TCNT1H,L) dans les registres de capture ICR1H,L
la valeur obtenue représente donc la durée de la période du signal sur le pin ICP1
*/
// TCCR1A
TCCR1B |= 0b00000100; // clk/256 (From prescaler)
TIMSK |= 0b00100000; //TICIE1 = bit5 (TICIE1) Timer/Counter1, Input Capture Interrupt Enable
/*
TIMER2 - 8bits ------------------------------------------------------------------------------------------------
Utilise pour generer le signal de decoupage de l'alimentation secteur du moteur
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 |= 0b10001111; // p:125
TIMSK &= 0b01111111; // INT Timer2 comp disable; Voir le datasheet ATmega32.pdf p:130}
GICR |= 0b01000000; // extINT0 enable; voir page 67 du pdf
MCUCR |= 0b00000010; // The falling edge of INT0 generates an interrupt request. p:67 du pdf
}
void init_variables(void)
{
stop_imps = true;
remplissage = false;
prelavage = false;
lavage = false;
chauffe_enable = false;
brassage = false;
rincage = false;
nb_rincage_max = 4;
mn_froides = 5;
vidange = false;
essorage = false;
defoulage = false;
sens_enable = false;
a_choisir = true;
RAZ_tempos();
affi_requis = false;
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
pause = false;
}
void RAZ_tempos(void)
{
// initialisation des compteurs avec des valeurs qui les bloquent
temps_lavage = 100*60; // en desondes
P_cons_lav = 875;
temps_rincage = 100*60; // en secondes
temps_pompe = 0;
temps_EV2 = 0;
temps_essorage = 100*60; // en secondes
temps_defoul = 100*60;
temps_total = 100;
temps_ch_sens = 5;
Prg = couleur;
Temperat_max = 40;
tps_lavage_max = 10 *60; //secondes
temps_chauffage_max = 60; // secondes, puis on fait tourner quelques tours
tps_essr_max = 4*60; // secondes
tps_purge_max = 50; // secondes
tps_defoul_max = 5; // secondes
periode_ch_sens = 10; // secondes
ti = 0;
}
void lcd_gotoxy_clrEOL (int x, int y)
// place le curseur en x,y et efface jusqu'a la fin de la ligne
{
lcd_gotoxy(x, y);
int i;
for (i=x; i<(20-x); i++) lcd_puts(" ");
lcd_gotoxy(x, y);
}
void relais1_off(void)
{
PORTA &= 0b11111011;
relais1 = false;
lcd_gotoxy(0,3);
lcd_puts(" ");
}
void relais2_off(void)
{
PORTB &= 0b11111011;
relais2 = false;
lcd_gotoxy(0,3);
lcd_puts(" ");
}
void relais1_on(void)
{
PORTA |= 0b00000100;
relais1 = true;
lcd_gotoxy(0,3);
lcd_puts(">");
}
void relais2_on(void)
{
PORTB |= 0b00000100;
relais2 = true;
lcd_gotoxy(0,3);
lcd_puts("<");
}
void EV2_on(void) // commande électrovanne2. //(on peut en rajouter)
{
PORTA |= 0b00000010;
EV2 = true;
temps_EV2 = 0;
lcd_gotoxy(5,3);
lcd_puts("EV");
lcd_gotoxy(7,3);
lcd_puts("2");
}
void EV2_off(void)
{
PORTA &= 0b11111101;
EV2 = false;
if (!EV1)
{ // le dernier ferme la porte en sortant!
lcd_gotoxy(5,3);
lcd_puts(" ");
}
lcd_gotoxy(7,3);
lcd_puts(" ");
}
void POMPE_on(void)
{
CHAUFFAGE_off();
PORTB |= 0b00001000; // pompe
pompe = true;
temps_pompe = 0;
}
void POMPE_off(void)
{
PORTB &= 0b11110111; // pompe
pompe = false;
}
void RESET(void)
{
lcd_clrscr();
lcd_gotoxy(1,1);
lcd_puts("SOFT RESET dans 1s");
_delay_ms(300);
wdt_enable( 10 ); // active le chien de garde
while(1); // boucle infinie --> le chien de garde fait reseter
}
void initADC(void)
{
// voir p:201
// RAPPEL: ADCH,L sont les deux registres contennat le resultat de la conversion (voir p:217)
ADMUX = 0b00000111; // ADC7 en mode "Single Ended Input" voir p:215
ADCSRA = 0b11000111; // ADC prescaler = 1/128 voir p:217 ADEN = 1 (ADC Enable) Bit 6 start conversion
}
/*
RAPPEL: pour la liste complete des interruptions disponibles:
voir le fichier iom32.h situé (sous Linux) dans /usr/lib/avr/include/avr
*/
ISR(BADISR_vect)
{
// évite de planter si une int est enable et pas de procedure associée écrite (ce qui fait reseter l'ATmega)
}
ISR (INT0_vect)
//interruption sur front descendant sur l'entree Int0
// declenchee par le signal secteur 50Hz
{
// genere le signal de decoupage de la tension du moteur sur la sortie OC2
// PushAllRegs;
TCNT2 = 0;
TCCR2 |= 0b00110000; // set OCE on compare match
TCCR2 |= 0b10000000; // bit FOCE2 voir p:125 (force comparaison virtuelle pour RAZ OCE)
// ce qui a pour effet de passer la sortie OC2 Ã 1
if (!stop_imps) TCCR2 = TCCR2 &= 0b11101111; // clear OCE on compare match (generera le signal sur OCE)
// PopAllRegs;
}
ISR (TIMER1_CAPT_vect)
/* TIMER1 = PERIODEMETRE
voir p:110, 111, 112 du datasheet
The Input Capture (ICR1H - ICR1L) is updated with the counter (TCNT1) value each time an event occurs on the ICP1 pin
16MHz/256(prescaler)=62500Hz
62500/50=1250
*/
{
cli();
periode = ICR1L + 256 * ICR1H; //lit le registre de capture ( 50Hz -> periode = 1250 )
TCNT1H = 0; //raz TCNT1, registre de comptage
TCNT1L = 0;
sei();
}
/*
ISR (ANA_COMP_vect)
{
//interruption du convertisseur analogique/numerique
======= NON UTILISEE DANS CE PROGRAMME ==========
}
*/
void GetADC(void)
{
ADCSRA |= 0b01000000; // Bit 6 start conversion
_delay_us(10);
acqui_ADC = ADCL + 256 * ADCH;
}
int32_t abs_int32(int32_t x)
{
if (x >= 0) return x ; else return -x;
}
void strcpy_a_n(char *CH1, char *CH2, uint8_t a, uint8_t n )
/*
recopie n caractères depuis i0 de CH2 dans CH1
attention: CH2 peut ne pas être un string (pas de \0 à la fin) ce qui peut être pratique pour lire un flux
mais a+n ne doit pas déborder de CH1 et CH2 !
ajoute \0 à la fin (le prévoir dans la longueur de la chaine CH1)
*/
{
uint8_t i;
i=0;
while ( i < n )
{
CH1[i] = CH2[a+i];
i++;
}
CH1[i]='\0';
}
uint8_t isNumeric(char *CH1)
{
uint8_t i;
i=0;
while (CH1[i] != '\0')
{
if ((CH1[i]>='0' && CH1[i]<='9') == 0) return 0;
i++;
}
return 1;
}
void vitesse_mini(uint8_t ocr2_i)
{
OCR2 = ocr2_i;
OCR2_real = ocr2_i;
}
void CHAUFFAGE_on(void)
{
if (chauffe_enable)
{
stop_imps = true;
PORTB |= 0b00010000; // colle le relais de chaufage
chauffage = true;
}
}
void CHAUFFAGE_off(void)
{
PORTB &= 0b11101111; // décolle le relais de chauffage
chauffage = false;
}
void interroge_IR(void)
{
int r1;
r1 = RecvRC5();
if (r1==0)
{
octet_IR = rxCmd_IR;
memo_bitsix= bitsix;
bitsix= bit_bascul_IR;
if (bitsix != memo_bitsix) nouveau=1; else nouveau=0;
PORTD |= 0b01000000; /* allume LED */
}
else { octet_IR = 0xFF; }
PORTD &= 0b10111111; /* eteint LED */
if (!nouveau) { octet_IR = 0xFF; } /* pas de répétition auto dans cette application */
}
void PAUSER(char str1[20+1])
{
vitesse_mini(OCRmax); // pour ralentir
memo_stop_imps = stop_imps;
stop_imps = true; // STOP MOTEUR
memo_chauffage = chauffage;
memo_chauffe_enable = chauffe_enable ;
chauffe_enable = false;
CHAUFFAGE_off();
EV2_off();
memo_pompe = pompe;
POMPE_off();
pause = true;
// ligne 0 non touchée
lcd_gotoxy_clrEOL(0,1);
lcd_puts("== PAUSE == ");
lcd_puts(str1);
lcd_gotoxy_clrEOL(0,2);
lcd_puts("Bouton rouge = RESET");
lcd_gotoxy_clrEOL(0,3);
lcd_puts("Touche 5 = MARCHE");
_delay_ms(1000);
octet_IR = 255; // securité
do
{
interroge_IR();
} // attend appui sur bouton rouge ou touche 5 avant de reseter ou de remettre en marche
while (( ((PIND & 0b00000010) != 0) && (octet_IR != 5)));
if ((PIND & 0b00000010) == 0) RESET(); // (bouton rouge)
// (touche 5)suite, remise en marche normale avec restauration des parametres courants
if (essorage)
{
temps_essorage = 0;
P_cons_ess = 2000; // vitesse tres lente au depart pour defouler
}
chauffe_enable = memo_chauffe_enable;
if (memo_chauffage == 1) { CHAUFFAGE_on(); }
pompe = memo_pompe;
if (pompe == 1) { POMPE_on(); }
vitesse_mini(OCRmax);
stop_imps = memo_stop_imps;
// ligne 0 non touchée
lcd_gotoxy_clrEOL(0,1);
lcd_gotoxy_clrEOL(0,2);
lcd_gotoxy_clrEOL(0,3);
pause = false;
}
void detection_bouton_ROUGE(void)
{
if ((PIND & 0b00000010) == 0 ) PAUSER("BT R");
}
void detection_bouton_VERT(void) // ABREGE le lavage ou le rincage en cours (passe au numero suivant=vidange)
{
if ((PIND & 0b00000001) == 0)
{
vert = true;
lcd_clrscr();
lcd_gotoxy(10,1);
lcd_puts("NEXT !");
_delay_ms(500);
lcd_clrscr();
} else vert = false;
}
void _delay_s(int8_t nb_s)
{
while ((nb_s > 0) && !vert)
{
lcd_gotoxy_clrEOL(6,2);
lcd_aff_nb (nb_s, 2, 0, 1 );
detection_bouton_ROUGE();
detection_bouton_VERT();
_delay_ms(500);
detection_bouton_ROUGE();
detection_bouton_VERT();
_delay_ms(500);
nb_s--;
}
vert = false;
}
void Timer1_setTime(int t) // en microsecondes
// quartz = 16MHz et prescaller (du Timer1) = 1/8 ce qui fait 2MHz
//voir p:87
{
OCR1A = 2 * t;
}
int RecvRC5 (void)
/* recepteur RC5 bas niveau qui scrute le port relié au capteur et décode le signal
2 bits de start
1 bit de basculement
5 bits d'adressage (type d'appareil, TV, VCR etc...)
6 bits de commande (touches)
*/
{
uint32_t t;
uint8_t niv_entree, niv1, niv2; /* booleens - niveaux des deux demi plateaux constituant un bit */
uint8_t n;
uint16_t data;
t=0;
niv1=0;
niv2=0;
data = 0;
niv_entree=1;
/* attente pendant la durée d'une trame d'un front descendant (le signal issu du capteur est à 1 au repos) */
const unsigned long int tmax = 14*1778; //24892us = durée de 1 trame (environ 25ms donc)
while ( (niv_entree == 1) && (t<= tmax) )
{
if ( (PINC & 0b10000000) > 0) { niv_entree=1; } else { niv_entree=0; }
_delay_us(1);
t++;
}
/* si le signal parvient trop vite (< 2 bits) on en déduit qu'on a pris le train en marche et on saute,
puisque lecture impossible de la trame en cours
*/
if (t < (889 * 2)) return t;
if (t > tmax) return 2; /* temps maxi écoulé, pas de signal reçu */
/* ici on se trouve juste un epsilon après le basculement à 0 du premier bit de start (milieu de bit) */
_delay_us(444); /* durée de 1/4 bit */
for (n=1; n<=13; n++)
{
_delay_us(889); // durée de 1/2 bit
/* ici on se trouve au milieu du niveau du plateau du demi bit */
if ( (PINC & 0b10000000) == 0) { niv1 = 0; } else { niv1 = 1; }
_delay_us(889); // durée de 1/2 bit
/* ici on se trouve au milieu du second plateau du demi bit */
if ( (PINC & 0b10000000) == 0) { niv2 = 0; } else { niv2 = 1; }
/* ici on connait les valeurs des deux niveaux, on en déduit le sens de la transition (front montant ou descendant)*/
if (niv1 == niv2) { return 3; } // erreur de lecture, bit non valide
if ((niv1 == 1 ) && (niv2 == 0)) { data |= 0b00000001; }
data = data << 1;
}
data = data >> 1; // on rejette le dernier décallage (sans perte de bit puisque largeur data = 16 bits > )
rxCmd_IR = data & 0b0000000000111111;
rxAdr_IR = (data & 0b0000011111000000) >> 6;
bit_bascul_IR = (data & 0b0000100000000000) >> 11;
return 0;
}
void lcd_aff_nb (int32_t valeur1, uint8_t nb_chiffres, uint8_t position_pt_decimal, char affi_zero )
//affiche un nombre positif en representation decimale
// affi_zero = 1 affiche les zéros non significatifs
{
int32_t N;
uint8_t r ;
char tbl[7];
uint8_t i;
char affz;
affz = affi_zero;
if ((valeur1 == 0) && (affz == 0)) // le zéro tout seul est significatif
{
for (i=1; i<nb_chiffres; i++) {lcd_putc(' '); }
lcd_putc('0');
}
N = valeur1;
for (i=1; i<=nb_chiffres; i++)
{
r=48 + N % 10; // modulo (reste de la division)
N /= 10; // quotient
tbl[i]=r;
}
for (i=1; i<=nb_chiffres; i++)
{
if (i == (nb_chiffres - position_pt_decimal +1) ) { lcd_puts("."); affz=1;} // après le . les 0 seront affichés
if (tbl[nb_chiffres+1-i] != '0') affz=1; // si 1 digit est >0 alors les 0 ultérieurs deviennent significatifs
if ((!affz) && (tbl[nb_chiffres+1-i] == '0')) lcd_putc(' ');
else lcd_putc(tbl[nb_chiffres+1-i]);
}
}
void lcd_aff_bin (unsigned long int valeur, int nb_digits)
//affiche un nombre en representation binaire
// 16 bits max
{
unsigned char r ;
char tbl[17];
unsigned i;
for (i=1; i<=nb_digits; i++)
{
r= 48 + valeur % 2; // modulo (reste de la division)
valeur /= 2; // quotient
tbl[i]=r;
};
for (i=1; i<=nb_digits; i++) lcd_putc(tbl[nb_digits +1 -i]);
}
void test_IR(void)
{
int r1;
lcd_clrscr();
lcd_gotoxy(0, 0);
lcd_puts("Test IR STOP=ok ou 0");
interroge_IR();
while (rxCmd_IR !=0)
{
r1 = RecvRC5();
if (r1==0)
{
lcd_gotoxy(2,1);
lcd_puts("CMD= ");
lcd_aff_nb (rxCmd_IR, 5, 0, 0 );
lcd_gotoxy(2,2);
lcd_puts("Adr= ");
lcd_aff_nb (rxAdr_IR, 5, 0, 0 );
lcd_gotoxy(2,3);
lcd_puts("Bsc= ");
lcd_aff_nb (bit_bascul_IR, 5, 0, 0 );
_delay_ms(10);
}
}
lcd_clrscr();
lcd_gotoxy(1,0);
lcd_puts("fin de test");
_delay_ms(500);
lcd_clrscr();
PAUSER("1");
}
uint32_t StrTo_uint32 (char *string1)
// nombres positifs (non signés) seulement 0...4 294 967 295 (4,2 x 10^9)
// atoi m'a occasionné des problèmes... pour les grands nombres > 2 octets
{
uint8_t i;
uint32_t x;
i=0;
x=0;
while (string1[i] != '\0')
{
x=(10 * x) + (string1[i] -48);
i++;
}
return x;
}
void Event_1s(void)
{
if (!pause)
{
// PushAllRegs; // sauvegarde sur la pile et non dans iData en statique
// portA:= portA xor %00100000; // fait clignoter une LED pour TEST
if (lavage)
{
if (temps_lavage < 120 *60) // ne boucle pas 120mn= 2h
{
if (!remplissage) {temps_lavage++;}
if (temps_lavage >= tps_lavage_max) { timeOUT_lavage = true; }
}
}
//------------------------------------------------------------------------------
if (chauffage)
{
if (temps_chauffage < 120 * 60) // ne boucle pas 120mn= 2h
{
temps_chauffage++;
if (temps_chauffage >= temps_chauffage_max) { timeOUT_chauffage = true; }
}
}
//------------------------------------------------------------------------------
if (brassage)
{
if (temps_brassage < 120 * 60) // ne boucle pas 120mn= 2h
{
temps_brassage++;
if (temps_brassage >= temps_brassage_max) { timeOUT_brassage = true; }
}
}
//------------------------------------------------------------------------------
if (rincage)
{
if (temps_rincage < 120*60 )
{
temps_rincage++; // ne boucle pas
if (temps_rincage >= tps_rincage_max ) { timeOUT_rincage = true; }
}
}
//------------------------------------------------------------------------------
if (essorage)
{
if (temps_essorage < 120*60) // ne boucle pas
{
temps_essorage++;
if (temps_essorage >= tps_essr_max) { timeOUT_ess = true;}
if (temps_essorage > 10) // 10s de defoulage
{
P_cons_ess = 6250 / temps_essorage;
// ce qui fait accelerer la vitesse linéairement / temps, puisque f=1/T
if (P_cons_ess < 31 ) { P_cons_ess = 31; }
}
}
}
//------------------------------------------------------------------------------
if (purge)
{
if (temps_purge < 120*60) // ne boucle pas
{
temps_purge++;
if (temps_purge >= tps_purge_max ) { timeOUT_purge = true; } //35s max
P_cons_purge = 1000 / temps_purge;
// ce qui fait accelerer la vitesse pour un départ en douceur
if (P_cons_purge < 190 ) { P_cons_purge = 190; } // atteint au bout de 5s
}
// inc(offset_OCR2); //pour accelerer petit à petit
}
//------------------------------------------------------------------------------
if (defoulage)
{
if (temps_defoul < 120*60)
{
temps_defoul++;
if (temps_defoul >= tps_defoul_max) { timeOUT_defoul = true; }
}
}
//------------------------------------------------------------------------------
if (pompe) { temps_pompe++; }
if (temps_pompe > 5*60)
{
PAUSER("PB vidange > 5mn");
}
if (EV2) { temps_EV2++; }
if (temps_EV2 > 5*60) { PAUSER("PB niveau eau"); }
//------------------------------------------------------------------------------
temps_ch_sens++;
if (temps_ch_sens >= periode_ch_sens)
{
if (sens_enable) {TimeOUT_sens= true;} // changement de sens requis
}
//------------------------------------------------------------------------------
affi_requis = true; // pas d'affichage directement dans une interruption
// PopAllRegs;
}
}
ISR (TIMER0_COMP_vect)
// timer 1ms
{
nb_ms2++;
if (nb_ms2 >= 1000)
{
nb_ms2=0;
Event_1s();
}
}
void detection_niveau_eau(void) // j'ai supprimé la detection du niveau2 (3/4 de cuve, c'est trop)
{
if ((PIND & 0b00100000) == 0)
{
strcpy(TXTniv, "Lo");
NIV1 = true;
NIV2 = false;
}
else
{
NIV1 = false;
NIV2 = false;
strcpy(TXTniv, "--");
}
}
void eteint_toutes_LED(void)
{
PORTA |= 0b00001000; // RAZ des deux CD4017 (éteint tout because les Q0 ne sont pas câblés)
_delay_us(1);
PORTA &= 0b11110111;
_delay_us(1);
}
void allume_LED(uint8_t num) // num in [0..17]
{
uint8_t n;
PORTA |= 0b00001000; // RAZ des deux CD4017 (éteint tout because les Q0 ne sont pas câblés)
_delay_us(1);
PORTA &= 0b11110111;
_delay_us(1);
num++;
if ((num > 0) && (num<19))
{
if (num < 10)
{
for (n=1; n<=num; n++)
//for n:= 1 to num do
{
PORTA |= 0b00010000; // clk du premier cd4017
_delay_us(1);
PORTA &= 0b11101111;
_delay_us(1);
}
}
else
{
for (n=10; n<=num; n++)
{
PORTA |= 0b00100000; // clk du second cd4017
_delay_us(1);
PORTA &= 0b11011111;
_delay_us(1);
}
}
}
}
void affiche_temperature(void)
{
uint8_t t1;
GetADC();
acqui_ADC = acqui_ADC >> 2; //décallage à droite de 2 bits; 8 bits au lieu de 10 de resolution
T = (3500 / acqui_ADC) - 11; // voir feuille de calcul Ooo
lcd_gotoxy(8, 3);
if ((T > 0) && (T < 100))
{
t1 = T; // conversion float -> entier
lcd_puts("T=");
lcd_aff_nb (t1, 2, 0, 0 );
lcd_puts(" ");
}
}
void change_SENS(uint8_t ocr2_i)
{
memo_stop_imps = stop_imps;
stop_imps = true;
_delay_ms(1000);
if (relais1 && !relais2)
{
relais1_off();
_delay_ms(300);
relais2_on();
}
else if (relais2 && !relais1)
{
relais2_off();
_delay_ms(300);
relais1_on();
}
else if (!relais1 && !relais2) { relais1_on(); } // si aucun
else if (relais1 && relais2) { relais2_off(); } // si les deux
_delay_ms(400);
vitesse_mini(ocr2_i); // pour repartir lentement avec le couple souhaité
stop_imps = memo_stop_imps; //on ne remet en marche que si le moteur tournait déjà lors de l'appel de la fonction
TimeOUT_sens = false;
temps_ch_sens = 0;
}
void Affiche_temps(uint16_t nb_secondes)
{
uint8_t h, mn, sec;
uint16_t R1;
// char signe;
h = nb_secondes / 3600; // transcodage float -> integer A VERIFIER
R1 = nb_secondes % 3600;
mn = R1 / 60; // transcodage float -> integer A VERIFIER
sec = R1 % 60;
lcd_gotoxy_clrEOL(13,0);
lcd_aff_nb (mn, 2, 0, 1 );
lcd_puts(":");
lcd_aff_nb (sec, 2, 0, 1 );
}
void AFFICHAGES(void) // permsise toutes les secondes par le flag affi_requis.
// le flag affi_requis est positionné par la fonction "Event_1s()"
{
affi_requis = false;
if (lavage) { Affiche_temps(tps_lavage_max - temps_lavage); }
if (chauffage) { Affiche_temps(temps_chauffage_max - temps_chauffage); }
if (brassage) { Affiche_temps(temps_brassage_max - temps_brassage); }
lcd_gotoxy(13, 3); if (chauffage) { lcd_puts("CH"); } else {lcd_puts(" "); }
lcd_gotoxy(2, 3); lcd_puts(TXTniv);
//------------------------------------------------------------------------------
if (rincage) {Affiche_temps(tps_rincage_max - temps_rincage);}
//------------------------------------------------------------------------------
if (purge) {Affiche_temps(tps_purge_max - temps_purge);}
//------------------------------------------------------------------------------
if (defoulage) { Affiche_temps(tps_defoul_max - temps_defoul);}
//------------------------------------------------------------------------------
if (essorage) { Affiche_temps(tps_essr_max - temps_essorage);}
//------------------------------------------------------------------------------
if (TimeOUT_sens)
{
TimeOUT_sens = false;
if ((!chauffage) && (!brassage)) change_SENS(OCRdemar);
}
}
void demarrage(uint8_t sens, uint8_t ocr2_i)
{
stop_imps = true; // pas de conduction du triac pendant la commutation des relais
_delay_ms(300);
if (sens == 0)
{
relais2_off();
_delay_ms(300);
relais1_on();
_delay_ms(300);
}
else if (sens == 1)
{
relais1_off();
_delay_ms(300);
relais2_on();
_delay_ms(300);
}
temps_ch_sens = 0;
vitesse_mini(ocr2_i); // pour partir lentement
stop_imps = false; // conduction du triac: mise en route du moteur
_delay_ms(1000); // demarrage franc forcé sans asservissement de vitesse
}
void asservissement_vitesse(uint16_t P_cons_i)
{
// float distance;
if (((periode > P_cons_i + 10) || (periode ==0)) && (OCR2 > OCRmin) )
{
OCR2_real -= 0.003; //accelere
}
if (((periode < P_cons_i - 10) && (periode !=0)) && (OCR2 < OCRmax) )
{
OCR2_real += 0.003; //ralentit
}
OCR2 = OCR2_real;
}
void LAVER(uint16_t tps)
//tps = duree de lavage en secondes
{
lavage = true;
if (tps > 30*60) { tps = 30*60; } // 30mn max (securite)
tps_lavage_max = tps; // en secondes
lcd_gotoxy_clrEOL(0,1);
if(prelavage==true)
{
lcd_puts("PRE");
}
lcd_puts("LAVAGE ");
lcd_puts(Nom_prg1);
lcd_gotoxy_clrEOL(0,2); lcd_puts(Nom_prg2);
affiche_temperature(); //mesure ET affichage de la temperature (en position 8,3)
lcd_gotoxy_clrEOL(10,2); lcd_puts("bcl lav");
//----------------------------- BOUCLE LAVAGE ----------------------------------
timeOUT_lavage = false;
temps_lavage = 0; // le temps est incrementé par la procedure RTCtickMinute
sens_enable = true;
demarrage(0, OCRdemar);
do
{
detection_bouton_ROUGE();
detection_bouton_VERT();
detection_niveau_eau();
if (!NIV1)
{// si le niveau baisse, on complete;
remplissage_NIV1(30);
demarrage(0, OCRdemar);
sens_enable = true;
lcd_gotoxy_clrEOL(0,1); lcd_puts("LAVAGE "); lcd_puts(Nom_prg1);
}
asservissement_vitesse(P_cons_lav);
_delay_us(25);
if (affi_requis) { AFFICHAGES(); }
} while (!timeOUT_lavage && !vert);
vert = false;
//-------------------------------- FIN LAVAGE ----------------------------------
CHAUFFAGE_off();
chauffe_enable = false;
// lcd_gotoxy_clrEOL(0,0);
// lcd_gotoxy(3, 0);
// lcd_puts("fin lavage");
vitesse_mini(OCRmax);
sens_enable = false;
stop_imps = true;
_delay_ms(1000);
relais1_off();
relais2_off();
lavage = false;
lcd_clrscr();
}
void DEFOULER(void)
{
uint8_t n;
lcd_clrscr();
CHAUFFAGE_off();
EV2_off();
lcd_gotoxy_clrEOL(0,1);
lcd_puts("DEFOULAGE");
defoulage = true;
sens_enable = false;
demarrage(0, OCRdemar);
for (n=1; n<5; n++)
{
timeOUT_defoul = false;
temps_defoul = 0;
do
{
asservissement_vitesse(1200);
_delay_us(20);
} while (!timeOUT_defoul);
if (n < 4) { change_SENS(OCRdemar); }
}
vitesse_mini(OCRmax);
stop_imps = true;
_delay_ms(300);
relais1_off();
relais2_off();
defoulage = false;
lcd_gotoxy_clrEOL(0,1);
}
void ESSORER(void)
{
PORTA |= 0b01000000; // allume LED essorage
lcd_clrscr();
accelere = false;
// offset_OCR2 = 0;
CHAUFFAGE_off();
EV2_off();
lcd_gotoxy_clrEOL(0,1);
lcd_puts("ESSORAGE");
POMPE_on();
sens_enable = false;
demarrage(0, OCRdemar);
if (tps_essr_max > 5*60) { tps_essr_max = 5*60;} // securite
temps_essorage = 0;
essorage = true;
lcd_gotoxy_clrEOL(11,3);
lcd_puts("bcl ess");
// mdelay(1000);
timeOUT_ess = false;
accelere = true; // add_F_ess sera incremente chaque seconde par RTCTickSecond
// baseOCR2 = 130;
// offset_OCR2 = 0;
vitesse_mini(OCRdemar);
P_cons_ess = 1500; // vitesse tres lente au depart pour defouler
stop_imps = false;
//------------------------- boucle essorage ------------------------------------
do
{
detection_bouton_ROUGE();
detection_bouton_VERT();
if (vert)
{ // coup d'accélération pour sauter manuellement la résonance
vert = false;
temps_essorage += 10;
}
asservissement_vitesse(P_cons_ess);
_delay_us(20);
if (affi_requis) { AFFICHAGES();}
} while (!timeOUT_ess);
vert = false;
//------------------------------------------------------------------------------
PORTA &= 0b10111111; // eteint LED essorage
lcd_clrscr();
lcd_puts("FIN ESSORAGE");
stop_imps = true;
vitesse_mini(OCRmax);
_delay_s(1);
relais1_off();
_delay_s(1);
relais2_off();
_delay_s(1);
_delay_s(15); // 15s de vidange supplementaires à la fin
POMPE_off();
vidange = false;
defoulage = false;
essorage = false;
lcd_gotoxy(0,1);
lcd_puts("Lessive terminee");
allume_LED(16);
while (1)
{
detection_bouton_ROUGE();
}
}
void VIDANGER(void)
{
lcd_gotoxy_clrEOL(0,1);
lcd_puts("VIDANGE");
chauffe_enable = false;
CHAUFFAGE_off();
vidange = true;
lcd_gotoxy_clrEOL(0,2);
lcd_puts("Pompe en marche");
POMPE_on();
affi_requis = false;
//sens_enable = true;
//demarrage(0, OCRdemar); // on continue à tourner pendant la vidange, (évite de se retrouver aver le linge en boule compacte)
do
{
detection_bouton_ROUGE();
detection_niveau_eau();
asservissement_vitesse(P_cons_lav);
_delay_us(25);
if (affi_requis) // 1 fois par seconde
{
affi_requis = false;
lcd_gotoxy(2, 3);
lcd_puts( TXTniv);
}
} while (NIV1 || NIV2);
//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
lcd_gotoxy_clrEOL(0,1);
lcd_puts("VIDANGE +");
lcd_gotoxy(2, 3);
lcd_puts(TXTniv);
_delay_s(20);
vitesse_mini(OCRmax);
sens_enable = false;
stop_imps = true;
_delay_ms(1000);
relais1_off();
relais2_off();
POMPE_off();
lcd_gotoxy_clrEOL(0,1);
vidange = false;
}
void remplissage_NIV1(uint16_t tps_plus) // par EV2
{
remplissage = true;
lcd_gotoxy_clrEOL(0,1);
lcd_puts("EAU");
vitesse_mini(OCRmax);
stop_imps = true;
sens_enable = false;
relais1_off();
relais2_off();
EV2_on();
do
{
detection_bouton_ROUGE();
detection_bouton_VERT();
detection_niveau_eau();
if (affi_requis) {AFFICHAGES();} // 1 fois par seconde
lcd_gotoxy(2, 3);
lcd_puts(TXTniv);
} while (!NIV1 && !NIV2 && !vert); // oui, NIV2 convient bien entendu.
vert = false;
if (octet_IR == 12) { PAUSER("3"); }
octet_IR = 255;
lcd_gotoxy_clrEOL(0,2);
lcd_puts("Eau +"); // un peu plus, pour eviter d'en reprendre un verre 10 fois de suite
_delay_s(tps_plus);
EV2_off();
lcd_clrscr();
remplissage = false;
}
void RINCER(uint8_t num_rincage)
{
rincage = true;
lcd_gotoxy_clrEOL(0,1);
lcd_puts("RINCAGE ");
lcd_aff_nb (num_rincage, 1, 0, 0 );
affiche_temperature(); //mesure ET affichage de la temperature (en position 8,3)
periode_ch_sens = 10;
chauffe_enable = false;
CHAUFFAGE_off();
temps_rincage = 0;
sens_enable = true;
demarrage(0, OCRdemar);
//---------------------------- boucle rincage ----------------------------------
lcd_gotoxy_clrEOL(11,3);
lcd_puts("bcl rin");
timeOUT_rincage = false;
do
{
detection_bouton_ROUGE();
detection_bouton_VERT();
detection_niveau_eau();
if (!NIV1)
{
remplissage_NIV1(30);
sens_enable = true;
demarrage(0, OCRdemar);
} // si le niveau baisse, on complete;
stop_imps = false;
asservissement_vitesse(P_cons_lav);
_delay_us(20);
if (affi_requis && !vert) { AFFICHAGES();}
} while (!timeOUT_rincage && !vert);
vert = false;
//------------------------------------------------------------------------------
vitesse_mini(OCRmax);
stop_imps = true;
sens_enable = false;
lcd_gotoxy_clrEOL(0,2);
lcd_puts("fin rincage");
_delay_s(1);
relais1_off();
relais2_off();
_delay_s(1);
rincage = false;
lcd_clrscr();
}
void PURGER(void) // mini essorage à la fin de la vidange afin de purger l'eau (et la lessive) du linge
{
// offset_OCR2:= 0;
CHAUFFAGE_off();
EV2_off();
lcd_gotoxy_clrEOL(0,1);
lcd_puts("PURGE");
POMPE_on();
sens_enable = false;
demarrage(0, OCRdemar);
temps_purge = 0;
P_cons_purge = 1000; // sera recalculé chaque seconde pour accélérer (in fonction Event_1s)
purge = true;
//------------------------- boucle "esso-purge" --------------------------------
lcd_gotoxy_clrEOL(11,3);
lcd_puts("bcl purg");
timeOUT_purge = false;
stop_imps = false;
do
{
detection_bouton_ROUGE();
detection_bouton_VERT();
asservissement_vitesse(P_cons_purge);
_delay_us(20);
if (affi_requis) { AFFICHAGES();}
} while (!timeOUT_purge && !vert);
vert = false;
//------------------------------------------------------------------------------
stop_imps = true;
vitesse_mini(OCRdemar);
_delay_ms(1000);
relais1_off();
relais2_off();
_delay_ms(1000);
_delay_ms(10000); // 10s de vidange supplementaires à la fin
POMPE_off();
stop_imps = true;
vidange = false;
defoulage = false;
purge = false;
lcd_clrscr();
}
uint8_t regulation_TEMPERATURE(void)
// cette fonction est appellée uniquement par la fonction "affichages" chaque seconde (ligne 1232)
// si chauffe_enable == true
// allume le chauffage si la température est trop basse sinon l'éteint
{
if (Temperat_max > 80) { Temperat_max = 80;} // securite
affiche_temperature(); //mesure ET affichage de la temperature
if (T < (Temperat_max) ) // alors il faut chauffer!
{
CHAUFFAGE_on();
return 1;
}
else
{
CHAUFFAGE_off();
return 0;
}
return 2;
}
void CHAUFFER(void)
{
uint8_t chauffe;
uint8_t sens = 0;
if (Prg == froid) return;
lcd_gotoxy_clrEOL(0,1); lcd_puts("CHAUFFAGE "); lcd_puts(Nom_prg1);
affiche_temperature(); //mesure ET affichage de la temperature (en position 8,3)
stop_imps = true;
_delay_ms(1000);
relais1_off();
relais2_off();
do
{
// -------------------------------- CHAUFFAGE 60s ------------------------------
if ((NIV1 || NIV2) ) { chauffe_enable = true; } // CHAUFFAGE
else
{
chauffe_enable = false;
CHAUFFAGE_off();
return;
}
lcd_gotoxy_clrEOL(0,2); lcd_puts("boucle de chauffage");
timeOUT_chauffage = false;
temps_chauffage = 0;
do
{ // on chauffe pendant 60s
detection_bouton_ROUGE();
detection_bouton_VERT();
chauffe = regulation_TEMPERATURE();
_delay_ms(50);
if (affi_requis) { AFFICHAGES(); }
} while ((!timeOUT_chauffage) && (chauffe == 1) && !vert);
vert = false;
CHAUFFAGE_off();
// -------------------------------- BRASSAGE ---------------------------------
if (chauffe == 1)
{
brassage = true;
lcd_gotoxy_clrEOL(0,2); lcd_puts("boucle de brassage");
temps_brassage_max = 8; // en secondes
timeOUT_brassage = false;
temps_brassage = 0; // le temps est incrementé par la procedure RTCtickMinute
sens_enable = false;
sens = 1-sens;
demarrage(sens, OCRdemar);
do
{
detection_bouton_ROUGE();
detection_bouton_VERT();
asservissement_vitesse(P_cons_lav);
_delay_us(25);
if (affi_requis) { AFFICHAGES(); }
} while (!timeOUT_brassage && !vert);
vert = false;
vitesse_mini(OCRmax);
stop_imps = true;
_delay_ms(1000);
relais1_off();
relais2_off();
brassage = false;
}
// ----------------------------------------------------------------------------
} while (chauffe == 1);
lcd_clrscr();
}
void choix_programme(void)
{
cli();
lcd_clrscr();
lcd_gotoxy(0, 0); lcd_puts("==== PROGRAMME: ====");
lcd_gotoxy(0, 1); lcd_puts("1:COUL-40 2:Blanc60");
lcd_gotoxy(0, 2); lcd_puts("3:LAINE-30 4:FROID ");
lcd_gotoxy(0, 3); lcd_puts("5:RAPIDE ");
do
{
detection_bouton_ROUGE();
interroge_IR();
} while ( (octet_IR!=1)&&(octet_IR!=2)&&(octet_IR!=3)&&(octet_IR!=4)&&(octet_IR!=5)&&(octet_IR!=12) );
switch (octet_IR)
{
case 1:
{
Prg = couleur;
strcpy(Nom_prg1, "Couleur40");
Temperat_max = 40;
duree_lavage = 15*60;
nb_rincage_max = 4;
tps_essr_max = 4*60;
}
break;
case 2:
{
Prg = blanc;
strcpy(Nom_prg1, "Blanc60");
Temperat_max = 60;
duree_lavage = 15*60;
nb_rincage_max = 4;
tps_essr_max = 4*60;
}
break;
case 3:
{
Prg = laine;
strcpy(Nom_prg1, "Laine");
Temperat_max = 30;
duree_lavage = 10*60;
nb_rincage_max = 4;
tps_essr_max = 1*60;
}
break;
case 4:
{
Prg = froid;
strcpy(Nom_prg1, "A froid");
Temperat_max = 0;
duree_lavage = 15*60;
nb_rincage_max = 4;
tps_essr_max = 4*60;
}
break;
case 5:
{
Prg = rapide;
strcpy(Nom_prg1, "Rapide");
Temperat_max = 0;
duree_lavage = 2*60;
nb_rincage_max = 2;
tps_essr_max = 1*60;
}
break;
case 12:
{
PAUSER("8");
}
break;
}
lcd_clrscr();
lcd_puts(Nom_prg1);
lcd_puts(" ");
lcd_aff_nb(Temperat_max, 2,0,0);
lcd_puts("deg ");
lcd_gotoxy(0, 1);
lcd_puts("5 prem. mn froid +");
lcd_gotoxy(0, 2);
lcd_puts("lavage ");
lcd_aff_nb(duree_lavage / 60, 2,0,0);
lcd_puts(" mn");
lcd_gotoxy(0, 3);
lcd_aff_nb (nb_rincage_max, 1, 0, 0 );
lcd_puts(" rincages ");
_delay_s(6);
lcd_clrscr();
temps_total = 0;
sei();
}
void numero_15(void) // vidange (du rinçage 4) puis pause et ESSORAGE
{
numero = 15;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
VIDANGER();
PURGER();
lcd_clrscr();
lcd_gotoxy(0, 0); lcd_puts("5 pour essorer");
lcd_gotoxy(0, 1); lcd_puts("bouton rouge = PAUSE");
while (octet_IR != 5)
{
detection_bouton_ROUGE();
interroge_IR();
}
DEFOULER();
ESSORER();
lcd_clrscr();
PAUSER("5");
}
void numero_14(void) // RINCAGE 4
{
numero = 14;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
RINCER(4);
num_requis = 15;
}
void numero_13(void) // remplissage (du rinçage 4)
{
numero = 13;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
if (nb_rincage_max > 3)
{
remplissage_NIV1(45);
num_requis = 14;
} else {num_requis = 15;} // vers essorage
}
void numero_12(void) // vidange (du rinçage 3)
{
numero = 12;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
VIDANGER();
PURGER();
num_requis = 13;
}
void numero_11(void) // RINCAGE 3
{
numero = 11;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
RINCER(3);
num_requis = 12;
}
void numero_10(void) // remplissage (du rinçage 3) puis pause pour ajout assouplissant
{
numero = 10;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
if (nb_rincage_max > 2)
{
remplissage_NIV1(45);
/*
lcd_clrscr();
lcd_gotoxy(0, 0); lcd_puts("ajout ASSOUPLISSANT");
lcd_gotoxy(0, 1); lcd_puts("5 pour continuer");
while (octet_IR != 5)
{
detection_bouton_ROUGE();
interroge_IR();
}
*/
num_requis = 11;
} else {num_requis = 15;} // vers essorage
}
void numero_9(void) // vidange (du rinçage 2)
{
numero = 9;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
VIDANGER();
PURGER();
num_requis = 10;
}
void numero_8(void) // RINCAGE 2
{
numero = 8;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
RINCER(2);
num_requis = 9;
}
void numero_7(void) // remplissage (du rinçage 2)
{
numero = 7;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
remplissage_NIV1(45);
num_requis = 8;
}
void numero_6(void) // vidange (du rinçage 1)
//bonjour chez vous
{
numero = 6;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
VIDANGER();
PURGER();
num_requis = 7;
}
void numero_5(void) // RINCAGE 1
{
numero = 5;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
RINCER(1);
num_requis = 6;
}
void numero_4(void) // remplissage (du rinçage 1)
{
numero = 4;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
remplissage_NIV1(45);
num_requis = 5;
}
void numero_3(void) // vidange (du lavage) et purge
{
numero = 3;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
VIDANGER();
PURGER();
num_requis = 4;
}
void numero_2(void) // LAVAGE
{
if (a_choisir) { choix_programme(); }
numero = 2;
allume_LED(numero);
lcd_gotoxy(0, 0); lcd_puts("numero "); lcd_aff_nb (numero, 2, 0, 0 );
strcpy(Nom_prg2, "froid(5mn)");
LAVER(mn_froides * 60); // 5 premieres minutes à froid
lcd_gotoxy(0, 0); lcd_puts("numero "); lcd_aff_nb (numero, 2, 0, 0 );
CHAUFFER();
lcd_gotoxy(0, 0); lcd_puts("numero "); lcd_aff_nb (numero, 2, 0, 0 );
if(Prg != froid) {strcpy(Nom_prg2, "chaud ");} else {strcpy(Nom_prg2, " ");}
LAVER(duree_lavage); // durée déterminée dans la fonction "choix_programme"
num_requis = 3;
}
void numero_1(void) // remplissage (pour lavage)
{
choix_programme();
a_choisir = false; // pour ne pas devoir le choisir une 2eme fois au numero_2
numero = 1;
allume_LED(numero);
lcd_gotoxy(0, 0);
lcd_puts("numero ");
lcd_aff_nb (numero, 2, 0, 0 );
remplissage_NIV1(30); // niveau haut mais inferieur à NIV2 (qui demande 3mn de remplissage!)
num_requis = 2;
}
void prelaver(void)
{
numero = 0;
allume_LED(numero);
lcd_clrscr();
lcd_gotoxy(0, 0);
lcd_puts("Prelavage");
lcd_aff_nb (numero, 2, 0, 0 );
affiche_temperature(); //mesure ET affichage de la temperature
prelavage = true;
remplissage_NIV1(30);
LAVER(2*60);
VIDANGER();
PURGER();
stop_imps = true;
lcd_clrscr();
lcd_gotoxy(0, 0); lcd_puts("FIN PRELAVAGE");
lcd_gotoxy(0, 1); lcd_puts("AJOUTER POUDRE");
lcd_gotoxy(0, 2); lcd_puts("Puis 5 = continuer");
do
{
detection_bouton_ROUGE();
interroge_IR();
} while (octet_IR != 5);
prelavage = false;
num_requis = 1;
lcd_clrscr();
}
void choix_numero(void)
{
uint8_t num;
cli();
lcd_clrscr();
lcd_gotoxy(0, 0); lcd_puts("== CHOIX NUMERO ====");
lcd_gotoxy(0, 1); lcd_puts("Touches < >");
lcd_gotoxy(0, 2); lcd_puts("Touche 5 = ok");
num = 1;
allume_LED(num);
lcd_gotoxy(0, 3); lcd_aff_nb (num, 2, 0, 0 ); lcd_puts(" "); lcd_puts(Labels_numeros[num]);
while ((octet_IR != 23) && (octet_IR != 5))
{
detection_bouton_ROUGE();
octet_IR = 255;
while ((octet_IR != 16) && (octet_IR != 17) && (octet_IR != 5) && (octet_IR != 23))
{
detection_bouton_ROUGE();
interroge_IR();
}
switch (octet_IR)
{
case 16 : {if (num < 15) { num++; _delay_ms(20); }}
break;
case 17 : {if (num > 1 ) { num--; _delay_ms(20); }}
break;
}
allume_LED(num);
lcd_gotoxy_clrEOL(0,3);
lcd_aff_nb (num, 2, 0, 0 ); lcd_puts(" "); lcd_puts(Labels_numeros[num]);
}
lcd_clrscr();
sei();
num_requis = num;
if (num_requis == 2) { a_choisir = true;}
}
void TEST(void)
{
/*
// test de la conversion implicite d'un reel en entier (sans emploi de "round", qui n'existe d'ailleurs pas)
float nb_reel1;
uint8_t octet1;
nb_reel1 = 3.141592653;
octet1 = 100000 * (nb_reel1-3.14);
lcd_clrscr();
lcd_aff_nb (octet1, 3, 0, 0 ); // affiche bien 159 sur le LCD
while(1);
*/
/*
// test periodemetre
lcd_clrscr();
while (1)
{
lcd_gotoxy(0,0);
lcd_aff_nb (periode, 5, 0, 1 );
_delay_ms(350);
}
*/
// test ADC
lcd_clrscr();
while (1)
{
GetADC();
lcd_gotoxy(0,0);
lcd_aff_nb (acqui_ADC, 3, 0, 1 );
_delay_ms(300);
}
}
int main (void)
{
wdt_disable();
nb_ms2=0;
init_ports();
init_variables();
InitINTs();
initADC();
allume_LED(0);
CHAUFFAGE_off();
lcd_init(LCD_DISP_ON);
lcd_clrscr();
lcd_home();
lcd_clrscr();
_delay_ms(300);
lcd_gotoxy(0,0);
lcd_puts("Lave Linge v: ");
lcd_puts(version);
_delay_s(4);
num_requis= 0;
lcd_clrscr();
lcd_gotoxy(0,0); lcd_puts("1:LAVAGE");
lcd_gotoxy(0,1); lcd_puts("2:Choix Numero");
lcd_gotoxy(0,2); lcd_puts("3:PRELAVAGE");
lcd_gotoxy(0,3); lcd_puts("9:Test IR");
octet_IR = 255;
while ((octet_IR !=1) && (octet_IR !=2) && (octet_IR !=3) && (octet_IR !=9) && (octet_IR !=32) && (octet_IR !=33) )
// attention cette LISTE doit etre exacte
{
detection_bouton_ROUGE();
interroge_IR();
}
if (octet_IR != 255)
{
sei(); // enable interruptions
switch (octet_IR)
{
case 1 : { numero_1();}
break;
case 2 : { choix_numero();}
break;
case 3 : { prelaver();}
break;
case 9 : { test_IR();}
break;
}
}
while(1)
{
switch (num_requis)
{
case 1 : numero_1();
break;
case 2 : numero_2();
break;
case 3 : numero_3();
break;
case 4 : numero_4();
break;
case 5 : numero_5();
break;
case 6 : numero_6();
break;
case 7 : numero_7();
break;
case 8 : numero_8();
break;
case 9 : numero_9();
break;
case 10 : numero_10();
break;
case 11 : numero_11();
break;
case 12 : numero_12();
break;
case 13 : numero_13();
break;
case 14 : numero_14();
break;
case 15 : numero_15();
break;
default :
{
lcd_gotoxy(0, 0);
lcd_puts("err2 - n requis NC");
}
while (1)
{
detection_bouton_ROUGE();
}
}
}
return 0;
}