ae_grouch/ae_grouch.ino

405 lines
22 KiB
C++
Executable File

//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.h>
//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("L<igne_") ; Serial.print(__LINE__) ; Serial.print(#v) ; Serial.print(" = ") ;Serial.println((v)) ; Serial.println(" Sketch stop"); while (1);
//Exemple, à la ligne 140, l'instruction ps(var1);
//inprimera "Ligne_140var1 = 18 Sketch stop"
//Macro pc(v)de debug pour imprimer le numero de ligne, le nom d'une variable, sa valeur, puis s'arreter et
//attendre un clic de souris sur le bouton 'Envoyer'en haut de l'ecran seriel pour continuer.
#define pc(v) Serial.print("Ligne_") ; Serial.print(__LINE__) ; Serial.print(#v) ;Serial.print(" = ") ; Serial.println((v)) ; Serial.println(" Clic bouton 'Envoyer' pour continuer") ;while (Serial.available()==0);{ int k_ = Serial.parseInt() ;}
//Exemple, à la ligne 145, l'instruction pc(var2);
//imprimera "Ligne_145var2 = 25.3 Clic bouton 'Envoyer' pour continuer"
//------------------------------------------------------------------------------------------------------------------------
// CALCUL
//------------------------------------------------------------------------------------------------------------------------
void CalcD ()
{ // Noter que T1>T2>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");
//}