From c3b974dfa483e5a38696d92aab918937d6db16de Mon Sep 17 00:00:00 2001 From: root Date: Tue, 11 Apr 2017 22:35:10 +0200 Subject: [PATCH] Initial commit --- ae_grouch.ino | 404 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100755 ae_grouch.ino diff --git a/ae_grouch.ino b/ae_grouch.ino new file mode 100755 index 0000000..2b7587c --- /dev/null +++ b/ae_grouch.ino @@ -0,0 +1,404 @@ +//Aepl-Duino Allumage electronique programmable - Arduino Nano et compatibles +char ver[] = "Version du 3_5_2016";//Choix entre 3 types de Dwell. +//En option, connexion d'un potard de 100kohm enter la patte A0 et la masse +//pour decaler "au vol" la courbe de quelques degrés, voir delAv plus bas +//T>14ms, correction Christophe.Avance 0°jusqu'a 500t/mn, anti retour de kick +//Detection et traitement special au demarrage (première etincelle) +//Multi courbes , 2 courbes supplementaires, b et c, selectionables par D8 ou D +//La Led L sur tout Arduino suit le courant dans la bobine + +#include "TimerOne.h" //Génère une interruption toutes les trech ou Dsecu µs +//#include +//LiquidCrystal lcd(10, 11, 12, 14, 15, 16); + +//------------------------------------------------------------------------------------------------------------------------ +// COURBES D'ALLUMAGE ET PARAMETRES +//------------------------------------------------------------------------------------------------------------------------ +// A renseigner obligatoirement : Na[],Anga[], Ncyl, AngleCapteur, CaptOn, Dwell +//Les tableau Na[] et Anga[] doivent obligatoirement débuter puis se terminer par 0 et contenir des valeurs entières >=1 +//Le nombre de points est libre. L'avance est imposée à 0° entre 0 et Nplancher t/mn +//Le dernier N fixe la ligne rouge, c'est à dire la coupure de l'allumage. +//Anga [] degrés d'avance vilebrequin correspondant ( peut croitre ou decroitre) + +int Na[] = {0, 500, 700, 800, 1200, 3000, 5000, 0}; +int Anga[] = {0, 10 , 10, 10, 10, 30, 30, 0}; +int Ncyl = 2; //Nb de cylindres, moteur 4 temps. Multiplier par 2 pour moteur 2 temps +const int AngleCapteur = 45; //le capteur(Hall ou autre ) est a 45° avant le PMH habituellement +const int CaptOn = 0; //1 déclenchement sur front montant (par ex. capteur Hall "saturé") + //0 déclenchement sur front descendant (par ex. capteur Hall "non saturé"). +const int Dwell = 3; //1 pour alimenter la bobine en permanencetre sauf 1ms/cycle.Elle doit pouvoir chauffer sans crainte + //2 pour alimenter la bobine seulement trech ms par cycle, 3ms par exemple + //3 pour simuler un allumage à vis platinées: bobine alimentée x% du tps + +//A la place de la courbe a, on peut selectionner la courbe b (D8 à la masse)ou la c (D9 à la masse) +//Courbe b +int Nb[] = {0, 500, 1200, 3000, 6000, 0}; //Connecter D8 à la masse +int Angb[] = {0, 10 , 10, 30, 30, 0}; +//Courbe c +int Nc[] = {0, 9900, 0}; //Connecter D9 à la masse +int Angc[] = {0, 10, 0}; + +const int Nplancher = 450; // vitesse en t/mn jusqu'a laquelle l'avance = 0° +const int trech = 5300; //temps de recharge bobine, 3ms= 3000µs typique, 7ms certaines motos +const int unsigned long Dsecu = 600000; //Securite: bobine coupee à l'arret apres Dsecu µs +int delAv = -3; //delta avance,par ex 2°. Quand Pot avance d'une position, l'avance croit de delAv +//Uniquement si l'on veut jouer à translater les courbes d'avance en connectant un potard +//Ceci est une option. Avec un potard de 100kohms entre A0 et la masse, de 0-1V environ, courbe originale +//de 1 a 2V environ avance augmentée de delAv, au dela de 2V, augmentée de 2*delAv +//Attention.....Pas de pleine charge avec trop d'avance, danger pour les pistons.. +const int tflash = 150; // Durée flash en µsec + +//------------------------------------------------------------------------------------------------------------------------ +// CONSTANTES - PINS +//------------------------------------------------------------------------------------------------------------------------ +const int Bob = 3; //Sortie D4 vers bobine. +const int Cible = 2; //Entrée sur D2 du capteur, R PullUp +const int Pot = A3; //Entrée analogique sur A0 pour potard de changement de courbes. R PullUp +const int Led13 = 13; //Temoin sur tout Arduino, suit le courant de bobine +const int Courbe_b = 8; //Entrée D8 R PullUp. Connecter à la masse pour courbe b +const int Courbe_c = 9; //Entrée D9 R PullUp. Connecter à la masse pour courbe c +const int Flash = 10; + +//------------------------------------------------------------------------------------------------------------------------ +// VARIABLES +//------------------------------------------------------------------------------------------------------------------------ +//Ici 3 positions:Decalage 0° si <1V, delAV° si 1 à 2 V, 2*delAv° pour > 2V +int valPot = 0; //0 à 1023 selon la position du potentiomètre en entree +float modC1 = 0; //Correctif pour C1[], deplace la courbe si potard connecté +int unsigned long D = 0; //Delai en µs à attendre après la cible pour l'étincelle +int milli_delay = 0; +int micro_delay = 0; +float RDzero = 0; //pour calcul delai avance 0° < Nplancher t/mn +float Tplancher = 0; //idem//idem +const int tcor = 140; //correction en µs du temps de calcul pour D +int unsigned long Davant_rech = 0; //Delai en µs avant la recharge de la bobine. +int unsigned long prec_H = 0; //Heure du front precedent en µs +int unsigned long T = 0; //Periode en cours +int unsigned long Tprec = 0; //Periode precedant la T en cours, pour calcul de Drech +int N1 = 0; //Couple N,Ang de debut d'un segment +int Ang1 = 0; //Angle d'avance vilo en degrès +int N2 = 0; //Couple N,Ang de fin de segment +int Ang2 = 0; +int* pN = &Na[0]; //pointeur au tableau des régimes. Na sera la courbe par defaut +int* pA = &Anga[0]; //pointeur au tableau des avances. Anga sera la courbe par defaut +float k = 0; //Constante pour calcul de C1 et C2 +float C1[20]; //Tableaux des constantes de calcul de l'avance courante +float C2[20]; //Tableaux des constantes de calcul de l'avance courante +float Tc[20]; //Tableau des Ti correspondants au Ni + //Si necessaire, augmenter ces 3 valeurs:Ex C1[30],C2[30],Tc[30] +float Tdebug[10]; +int Tlim = 0; //Période minimale, limite, pour la ligne rouge +int j_lim = 0; //index maxi des N , donc aussi Ang +int unsigned long NT = 0; //Facteur de conversion entre N et T à Ncyl donné +int AngleCibles = 0; //Angle entre 2 cibles, 180° pour 4 cyl, 120° pour 6 cyl, par exemple +int UneEtin = 1; //=1 pour chaque étincelle, testé et remis à zero par isr_GestionIbob() +int Ndem = 180; //Vitesse estimée du vilo entrainé par le demarreur en t/mn +int unsigned long Tdem = 0; //Periode correspondante à Ndem,forcée pour le premier tour +int Mot_OFF = 0; //Sera 1 si moteur detecté arrété par l'isr_GestionIbob() +//Permet d'icentifier le premier front et forcer T=Tdem, ainsi que Ibob=1, pour demarrer au premier front +// initialize the library with the numbers of the interface pins +unsigned long pm = 0; +boolean flag_lcd = true; +int i_debug = 0; + +//------------------------------------------------------------------------------------------------------------------------ +// DEBUG +//------------------------------------------------------------------------------------------------------------------------ +//Macro ps(v) de debug pour imprimer le numero de ligne, le nom d'une variable, sa valeur puis s'arreter definitivement +#define ps(v) Serial.print("LT2>T3... + for (int j = 1; j <= j_lim; j++) //On commence par T la plus longue et on remonte + { + if (T >= Tc[j]) + { //on a trouvé le bon segment de la courbe d'avance + D = float(T * ( C1[j] - modC1 ) + C2[j]); //D en µs, C2 incorpore le temps de calcul tcor + if ( T > Tplancher)D = T * RDzero; //Imposer 0° d'avance de 0 à 500t/mn + break; //Sortir, on a D + } + } +} + +//------------------------------------------------------------------------------------------------------------------------ +// ETINCELLE +//------------------------------------------------------------------------------------------------------------------------ +void Etincelle () +{ + D = D - tflash; + if (D < 14000) + { // Currently, the largest value that will produce an accurate delay is 16383 µs + delayMicroseconds(D); //Attendre D + } + else + { + milli_delay = ((D / 1000) - 2); //Pour ces D longs, delayMicroseconds(D)ne va plus. + micro_delay = (D - (milli_delay * 1000)); + delay(milli_delay); + delayMicroseconds(micro_delay); + } + digitalWrite (Flash, 1); // Flash de calage + delayMicroseconds(tflash); + digitalWrite (Flash, 0); + digitalWrite(Bob, 0); //Couper le courant, donc étincelle + digitalWrite(Led13, 0); //Temoin + //Maintenant que l'étincelle est émise, il faut rétablir Ibob au bon moment + + switch (Dwell) //Attente courant coupé selon le type de Dwell + { + case 1: //Ibob coupe 1ms par cycle seulement, la bobine doit supporter de chauffer + Davant_rech = 1000; //1ms off par cycle + break; + case 2: //Type bobine faible resistance, dite "electronique" + Davant_rech = 2 * T - Tprec - trech; //On doit enir compte des variations de régime moteur + Tprec = T; //Maj de la future periode precedente + break; + case 3: //Type "vis platinées", Off 1/3, On 2/3 + Davant_rech = T / 2; + break; + } + Timer1.initialize(Davant_rech); //Attendre Drech µs avant de retablire le courant dans la bobine + Tst_Pot(); //Voir si un potard connecté pour deplacer la courbe ou selectionner une autre courbe + UneEtin = 1; //Pour signaler que le moteur tourne à l'isr_GestionIbob(). +} + +//------------------------------------------------------------------------------------------------------------------------ +// INIT +//------------------------------------------------------------------------------------------------------------------------ +void Init () +//Calcul de 3 tableaux,C1,C2 et Tc qui serviront à calculer D, temps d'attente +//entre la detection d'une cible par le capteur et la generation de l'etincelle. +//Le couple C1,C2 est determiné par la periode T entre deux cibles, correspondant au +//bon segment de la courbe d'avance entrée par l'utilisateur: T est comparée à Tc +{ + AngleCibles = 720 / Ncyl; //Ex pour 4 cylindres 180°, et 120° pour 6 cylindres + NT = 120000000 / Ncyl; //Facteur de conversion Nt/mn en Tµs + //T temps entre 2 étincelle soit 720° mono cylindre 4temps.360°=60/Ns 1°=1/6N + //720° = 720/6N=120/N Pour un moteur à Ncyl, T=120/Ncyl.N en secondes + Tdem = NT / Ndem; //Periode imposée à la première étincelle qui n'a pas de valeur prec_H + Tplancher = 120000000 / Nplancher / Ncyl; //T à vitesse plancher en t/mn: en dessous, avance centrifuge = 0 + RDzero = float(AngleCapteur) / float(AngleCibles); + Select_Courbe(); //Ajuster éventuellement les pointeurs pN et pA pour la courbe b ou c + N1 = 0; Ang1 = 0; //Toute courbe part de 0 + int i = 0; //locale mais valable hors du FOR + pN++; pA++; //sauter le premier element de tableau, toujours =0 + for (i = 1; *pN != 0; i++) //i pour les C1,C2 et Tc.Arret quand regime=0. + { + //pN est une adresse (pointeur) qui pointe au tableau N.Le contenu pointé est *pN + N2 = *pN; Ang2 = *pA; //recopier les valeurs pointées dans N2 et Ang2 + k = float(Ang2 - Ang1) / float(N2 - N1); //pente du segment (1,2) + C1[i] = float(AngleCapteur - Ang1 + k * N1) / float(AngleCibles); + C2[i] = - float(NT * k) / float(AngleCibles) - tcor; //Compense la durée de calcul de D + Tc[i] = float(NT / N2); + N1 = N2; Ang1 = Ang2; //fin de ce segment, début du suivant + pN++; pA++; //Pointer à l'element suivant de chaque tableau + } + j_lim = i - 1; //Revenir au dernier couple entré + Tlim = Tc[j_lim]; //Ligne rouge + + Serial.print("Ligne_"); Serial.println(__LINE__); + Serial.print("Tc = "); for (i = 1 ; i < 15; i++)Serial.println(Tc[i]); + Serial.print("Tlim = "); Serial.println(Tlim); + //Serial.print("C1 = "); for (i = 1 ; i < 15; i++)Serial.println(C1[i]); + //Serial.print("C2 = "); for (i = 1 ; i < 15; i++)Serial.println(C2[i]); + + //Timer1 a deux roles: + //1)couper le courant dans la bobine en l'absence d'etincelle pendant plus de Dsecu µs + //2)après une étincelle, attendre le delais Drech avant de retablir le courant dans la bobine + //Ce courant n'est retabli que trech ms avant la prochaine étincelle, condition indispensable + //pour une bobine à faible resistance, disons inférieure à 3 ohms.Typiquement trech = 3ms à 7ms + Timer1.attachInterrupt(isr_GestionIbob); //IT d'overflow de Timer1 (16 bits) + Timer1.initialize(Dsecu); //Le courant dans la bobine sera coupé si aucune etincelle durant Dsecu µs + Mot_OFF = 1; // Signalera à loop() le premier front + //lcd.setCursor(5,1); + //lcd.print(Mot_OFF); + + digitalWrite(Bob, 0); //par principe, couper la bobine + digitalWrite(Led13, 0); //Temoin +} + +//------------------------------------------------------------------------------------------------------------------------ +// isr GESTION BOBINE +//------------------------------------------------------------------------------------------------------------------------ +void isr_GestionIbob() +{ + Timer1.stop(); //Arreter le decompte du timer + if (UneEtin == 1) + { + digitalWrite(Bob, 1); //Le moteur tourne,retablire le courant dans bobine + digitalWrite(Led13, 1); //Temoin + } + else + { + digitalWrite(Bob, 0); + digitalWrite(Led13, 0); //Moteur arrete, preserver la bobine, couper le courant + Mot_OFF = 1; //Permettra à loop() de detecter le premier front de capteur + //lcd.setCursor(5,1); + //lcd.print(Mot_OFF); + } + UneEtin = 0; //Remet le detecteur d'étincelle à 0 + Timer1.initialize(Dsecu); //Au cas où le moteur s'arrete, couper la bobine apres Dsecu µs +} + +//------------------------------------------------------------------------------------------------------------------------ +// SELECTION COURBE +//------------------------------------------------------------------------------------------------------------------------ +void Select_Courbe() +{ + //lcd.setCursor(7,1); + //lcd.print("A"); + //Par défaut, la courbe a est déja selectionnée + if (digitalRead(Courbe_b) == 0) //D8 à la masse + { + pN = &Nb[0]; // pointer à la courbe b + pA = &Angb[0]; + //lcd.setCursor(7,1); + //lcd.print("B"); + } + if (digitalRead(Courbe_c) == 0) //D9 à la masse + { + pN = &Nc[0]; // pointer à la courbe c + pA = &Angc[0]; + //lcd.setCursor(7,1); + //lcd.print("C"); + } +} + +//------------------------------------------------------------------------------------------------------------------------ +// GESTION POTAR +//------------------------------------------------------------------------------------------------------------------------ +void Tst_Pot() +{ + valPot = analogRead(Pot); + if (valPot < 240 || valPot > 900) modC1 = 0; //0° ou pas de potard connecté (valpot =1023 en théorie) + else + { + if (valPot < 500) modC1 = float (delAv) / float(AngleCibles); //Position 1 + else modC1 = 2 * float (delAv) / float(AngleCibles); //Position 2 + } +} + +//------------------------------------------------------------------------------------------------------------------------ +// SETUP +//------------------------------------------------------------------------------------------------------------------------ +void setup() +{ + Serial.begin(9600); //Ligne suivante, 3 Macros du langage C + Serial.println(__FILE__); Serial.println(__DATE__); Serial.println(__TIME__); + Serial.println(ver); + + if (Ncyl < 2)Ncyl = 2; //On assimile le mono cylindre au bi, avec une étincelle perdue + + pinMode(Cible, INPUT_PULLUP); //Entrée front du capteur sur D2 + pinMode(Bob, OUTPUT); //Sortie sur D4 controle du courant dans la bobine + pinMode(Pot, INPUT_PULLUP); //Entrée pour potard 100kohms, optionnel + pinMode(Courbe_b, INPUT_PULLUP); //Entrée à la masse pour selectionner la courbe b + pinMode(Courbe_c, INPUT_PULLUP); //Entrée à la masse pour selectionner la courbe c + pinMode(Led13, OUTPUT); //Led d'origine sur tout Arduino, temoin du courant dans la bobine + pinMode(Flash, OUTPUT); + + //lcd.begin(8, 2); + + Init(); //Executée une fois au demarrage et à chaque changement de courbe + + //lcd.setCursor(4,0); + //lcd.print(" tpm"); + + //lcd.setCursor(2,0); + //lcd.print("max. "); + //lcd.print(NT/Tlim); + //lcd.print(" tr/min"); +} + +//------------------------------------------------------------------------------------------------------------------------ +// LOOP +//------------------------------------------------------------------------------------------------------------------------ +void loop() +{ + while (digitalRead(Cible) == !CaptOn); //Attendre front actif de la cible + T = micros() - prec_H; //front actif arrivé, calculer T + prec_H = micros(); //heure du front actuel qui deviendra le front precedent + + if ( Mot_OFF == 1 ) + { //Demarrage:premier front de capteur + T = Tdem; //Fournir T = Tdem car prec_H n'existe par pour la première étincelle + digitalWrite(Bob, 1); //Alimenter la bobine + digitalWrite(Led13, 1); //Temoin + Mot_OFF = 0; //Le moteur tourne + //lcd.setCursor(5,1); + //lcd.print(Mot_OFF); + } + + if (T > Tlim) //Sous la ligne rouge? + { + CalcD(); // Top(); //Oui, generer une etincelle + Etincelle(); + } + /* + if (millis() - pm > 500) + { + lcd.setCursor(6,1); + lcd.print(flag_lcd); + if (flag_lcd) + { + lcd.setCursor(0,0); + if (NT/T<1000) lcd.print(" "); + lcd.print(NT/T); //régime mot. + lcd.print(" "); + lcd.setCursor(0,1); + } + if (!flag_lcd) + { + float avance = float(AngleCapteur)-(360.0*float(D)/float(T)); + lcd.setCursor(0,1); + if (avance<10) lcd.print(" "); //angle avance + lcd.print(avance,1); + lcd.print(" "); + } + pm=millis(); + flag_lcd=!flag_lcd; + } + + if (i_debug < 10){ + Tdebug[i_debug] = T; + i_debug++; + } else { + i_debug = 0; + Serial.print("T = "); for (int i = 1 ; i < 10; i++)Serial.println(Tdebug[i]); + } + */ + while (digitalRead(Cible) == CaptOn); //Attendre si la cible encore active + +} + + +/////////////////Exemples de CAPTEURS///////////////// +//Capteur Honeywell cylindrique 1GT101DC,contient un aimant sur le coté,type non saturé, sortie haute à vide, +//et basse avec une cible en acier. Il faut CapteurOn = 0, declenchement sur front descendant. +//Le capteur à fourche SR 17-J6 contient un aimant en face,type saturé, sortie basse à vide, +//et haute avec une cible en acier. Il faut CapteurOn = 1, declenchement sur front montant. +////////////////DEBUGGING//////////////////////// +//Voir les macros pc et ps ci dessus +//float gf = 0;//pour boucle d'attente,gf GLOBALE et FLOAT indispensable +// gf = 1; while (gf < 2000)gf++;//10= 100µs,100=1.1ms,2000=21.8ms +//void Top()////////////////////while (1); delay(1000);///////////////////////////// +//{ digitalWrite(Bob, 1); //Crée un top sur l'oscillo +// gf = 1; while (gf < 10)gf++;//gf DOIT être Globale et Float 10=100µs,2000=21.8ms, retard/Cible=50µs +// digitalWrite(Bob, 0); // +//} +//void software_Reset() //jamais testé +// Redémarre le programme depuis le début mais ne +// réinitialiser pas les périphériques et les registresivre... +//{ +// asm volatile (" jmp 0"); +//}