Go Down

Topic: Interface à base de pic pour gestion d'impulsions (Read 7131 times) previous topic - next topic

Super_Cinci

A partir de mes deux signaux, dans le MEGA2560 :

- le carré représentant 352 dent / tour moteur incrémentera le timer5 (via T5 par exemple).
- le second (impulsion PMH) viendra déclencher l'ICP5.

Dans le MEGA, un timer déclenche une interruption toutes les 256ms, dans laquelle je relève TCNT5 puis fais TCNT5 = 0, et je multiplie ma valeur par 1.5 pour trouver la vitesse du moteur en tr/min.

Lors de l'iCP5, je relève ICR5 tout en laissant tourner le compteur bien sûr, puis à l'aide une seconde interruption déclenchée par l'étincelle de l'allumage, je calcule l'avance à l'allumage. (c'est surtout là que la multiplication d'impulsions aide dans la précision...)

C'est tout... Tu vas me dire que le 328P peut certainement s'occuper de tout ça de envoyer les valeur au MEGA via USART, mais non, je veux des signaux bien précis, c'est comme ça...

Les pins mentionnées ici ne sont données qu'à titre indicatif, car le MEGA va mesurer tout un tas d'autres données, et au final, les faire afficher sur deux écrans graphiques LCD de 160 x 128 qui remplaceront le tableau de bord actuel. Les autres mesures sont beaucoup plus simples, ça se fera tout seul.

De plus, le MEGA aura la lourde (mais essentielle) tâche de remplacer le câble d'accélérateur : un portar sur la pédale et un servo sur le carbu, plus de câble. Il y aura un mode "régulateur de vitesse" et un autre "limiteur de vitesse". Et certainement la gestion du starter avec un autre servomoteur, puis il me restera une troisième voie pour un troisième servo, mais pour quoi faire? je ne sais pas encore...

mon coéf de 1.5 risque de tomber à 1 si je fais une fenêtre de 170.45ms au lieu de 256ms, mais ma "fenêtre" doit être assez large pour y éxécuter les différentes fonctions, donc notamment le régulateur qui a besoin d'intervalles de calcul réguliers pour être précis.

A partir de ces deux signaux, dans un autre projet, je compte refaire un AEI (calculateur d'allumage) que l'on peut reprogrammer à souhaits. Mais je pense passer à une multiplication plus élevée, et peut-être passer sur une plateforme 32bits/168MHz comme le STM32F4 par exemple.

Mais tout ça, c'est un autre projet. Ce qui compte ici, c'est de faire de l'extraction de synchro et multiplication, rien d'autre (un peu comme les circuits vidéo)...

skywodd


La comme ça, je ne vois pas du tout. Qu'est le "générateur de signaux A"? Il me parrait difficile d'utiliser le timer 2 en sortie

Pour mieux comprendre -> datasheet page 146, figure 18-1.

Tu peut voir que OCRnA (le registre qui est comparé avec TCNTn et qui peut générer une INT sur overflow et/ou "match compare") et associé à un module "Waveform Generation" OCnA.

Comme tu as OCRnA et OCRnB tu peut utiliser OCRnA en INT "match compare" pour générer le signal n°2 (avec le morceau de code qui va bien) et OCRnB en "waveform génération" (mode FastPWM avec TOP = OCRnB) pour générer le signal n°1.

En gros le canal A génère une INT (sans signal PWM) sur lors du "match compare" OCRnA/TCNTn et le canal B génère un signal PWM (sans INT) lors du "match compare" OCRnB/TCNTn .

Tant que tu peut caser tes deux valeurs de "match compare" dans OCRnA et OCRnB avec un même prescaler pour les deux tout va bien.
Des news, des tutos et plein de bonnes choses sur http://skyduino.wordpress.com !

Super_Cinci


Pour mieux comprendre -> datasheet page 146, figure 18-1.
heu... pas trouvé, page 146 : on est dans le 17.1...

Ce que je ne vois pas, c'est comment un timer peut générer un un signal carré B et une impulsion A toutes les 352 impulsions de B... Sachant que l'on décide de générer l'impulsion A en fonction du signal de référence externe...

UniseV

@Super_Cinci :
Je comprends que tu ne veuilles pas utiliser de liaison Asynchrone type USART puisque tout ton système est synchrone, tu veux du temps réel côté MEGA pour la partie calcul du retard à l'allumage. (donc dent PMH ?)

En revanche pour la partie trs/min, pour une mesure précies, il suffit de prendre un tour complet (entre 2 passsages de la dent PMH) et on obtient un calcul précis... (même haut dans les tours avec un prescaler à clk/8 on garde une précision de moins d'un tour par minutes).

Ne peut-on pas imaginer de séparer ces 2 fonctions ?
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Super_Cinci

#34
Aug 13, 2012, 11:07 pm Last Edit: Aug 13, 2012, 11:13 pm by Super_Cinci Reason: 1

Ne peut-on pas imaginer de séparer ces 2 fonctions ?
non.

ça fait un an que je traine sur ce projet en arduino à cause de cette interface, 7 ans qu'il est dans un coin de ma tête. maintenant que tu m'as soufflé le bonne idée (et merci!) du 368P en stand-alone, il ne me reste plus qu'à le valider et passer à la prog des LCD (qui sont équipés chacun d'un 168 que je reprogramme différemment avec bootloader arduino pour faire de l'"accélération matérielle" grave) et du méga "centraliseur". En gros, ça va me faire tourner 4 µP arduino, ça me fait déjà pas mal de prog à prévoir... L'ensemble du projet prochainement dans un topic dédié.

UniseV

J'ai peur que le 328P t'ajoute un délais de traitement qui rende le truc moins "temps réel" côté Mega...
De plus la multiplication de la fréquence risque aussi de multiplier l'imprécision...
...Mais t'as l'air motivé et ton projet a l'air bien établi dans ta tête alors je te souhaite bon courage en attendant de voir le FAMEUX topic  ;)
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Super_Cinci


J'ai peur que le 328P t'ajoute un délais de traitement qui rende le truc moins "temps réel" côté Mega...
De plus la multiplication de la fréquence risque aussi de multiplier l'imprécision...
...Mais t'as l'air motivé et ton projet a l'air bien établi dans ta tête alors je te souhaite bon courage en attendant de voir le FAMEUX topic  ;)
Je sais que plus y'a de traitement numérique, moins y'a de précision.

J'ai écrit les codes pour les deux arduino (l'un simulateur de capteur PMH, l'autre multiplicateur). Il ne me reste plus qu'à trouver le courage de poser l'oscillo à côté et tester l'ensemble. En toute théorie, le retard induit par le 328 devrait être très faible, si j'ai le temps, je confirme cela demain.

Oui, je suis assez butté, mais je suis aussi breton... :smiley-mr-green:

Super_Cinci

#37
Aug 15, 2012, 04:52 pm Last Edit: Aug 16, 2012, 03:03 am by Super_Cinci Reason: 1
Bon, ça commence bien, j'ai uploadé le simulateur de capteur dans le mega :

Code: [Select]

/*********************************************
**          Générateur signaux PMH          **
**********************************************
**      Circuit de mise au point pour       **
**        ODB / REGULATEUR de vitesse       **
**                                          **
**              DEV : MEGA2560              **
**********************************************
** Pin / por - Signal                       **
**  A0 - PC0 - Motor speed (analog)         **
**  04 - PG5 - PMH OUT                      **
**  05 - PE3 - allumage OUT                 **
*********************************************/

volatile byte Cmpt_dent;  // comptage des dents
volatile boolean moteur;  // ON/OFF
volatile byte allumage;   // postion allumage

void setup() {
  TCCR1A = 0x00;        // (WGM1 = Fast PWM CTC)
  TCCR1B = 0x08;   // (WGM1 = Fast PWM CTC), clk = 0
  TCCR1C = 0x00;
  TIMSK1 = 0x02;   // INT sur OCR1A
  OCR1A = 65530;   // valeur de démarrage moteur
  TCCR1B += 0x01;  // Prescaler = 1/1, Fclk = 16MHz
  DDRG |= 0x20;
  DDRE &= 0xDF;    // PE5 en entrée
  DDRE |= 0x08;     // PE3 en sortie
  PORTE |= 0x20;    // pullup sur PE5
  DDRF &= 0xFE;    // PF0 en entrée
  DDRB |= 0x80;    // led en sortie
  allumage = 36;
}

ISR(TIMER1_COMPA_vect){  // appelée par T1 sur COMPA
  if (Cmpt_dent > 38) {  // secteur PMH
    if (Cmpt_dent == 41) {
      PORTG |= 0x20;
    }
  } else {
    if ((Cmpt_dent & 0x01) == 1) {  // dent impaire
      PORTG |= 0x20;
    } else {                  // dent paire
      PORTG &= 0xDF;
    }
  }
  if (Cmpt_dent == allumage) {
    PORTE |= 0x08;
  } else {
    PORTE &= 0xF7;
  }
  Cmpt_dent++;
  if (Cmpt_dent == 44) Cmpt_dent = 0;  // 1 demi-tour fini
}

void Moteur_set_speed(word val){
  long IC_Val;

      IC_Val = 272727 / val;
      OCR1A = IC_Val;
}

volatile word accelerateur, acc_old;

void loop() {
  accelerateur = analogRead(A0) >> 2;
  if (accelerateur != acc_old) {
    PORTB &= 0x7F;
    Moteur_set_speed(accelerateur);
    acc_old = accelerateur;
    PORTB |= 0x080;
  }
}


Bon, ça marche, mais ça ne marche pas : par moment, le timer saute une int, et mon signal en ressort tout perturbé, les tempos nagent. Est-ce que par zazard, analogRead() ne désactiverait pas les int le temps de la conversion? et dans ce cas, le timer sauterait un pas de temps en temps... si ce n'est que ça, ça va, sinon, mon banc de test ne marchera pas, et pour la suite, pas gagné...

EDIT :

J'ai réécrit analogRead() en analogGet(), plus simple (trop de #if pour moi), et je pense avoir trouvé pourquoi ça cafouille et ça ne vient pas de analogRead() qui ne contient pas de cli() ou sei(). En effet, la config timer en CTC que j'ai prise n'est pas bonne, car OCR1A est mis à jour immédiatement, je suis donc passé en mode WGM1=15, comme ça, plus de surprise.

J'ai aussi pensé à utiliser une seconde entrée analogique pour simuler la charge du moteur (et donc obtenir une accélération lente avec avance à l'allumage augmentée lorsque la commande dépasse la vitesse finale voulue (quand on fait forcer le moteur, l'avance augmente).



A0 donne la vitesse du moteur, A1, la charge : lorsqu'on veut accélérer, le condensateur doit se charger, donc fait monter le potentiel sur A0 doucement. Sur A1, on a une image du courant dans le condensateur, avec un rapide calcul, on en déduit une avance à l'allumage... L'idée est de générer les signaux que je compte utiliser par la suite (PMH et allumage), On retrouve le même comportement de l'allumage dans le vrai moteur via la mesure de dépression dans le carburateur.

Super_Cinci

Bon, il va falloir tout reprendre à zéro...

J'ai donc un mega qui me simule à perfection le signal de mon capteur :


en haut : le signal du capteur avec les deux dents "manquantes"
en bas : une impulsion correspondant à l'allumage (elle est synchronisée sur le pmh à la dent 19, purement arbitraire dans un premier temps, mais je m'en sers pour synchroniser l'oscillo...)

Puis j'envoie mon signal PMH dans un UNO :

en haut, le signal PMH du méga
en bas, la sortie "multiplicateur" du UNO, 8 impulsions par période, pari gagné.


en haut, le signal PMH du méga
en bas, la sortie "extracteur de PMH" du UNO décalée de 500ns par rapport au front descendant du signal d'entrée.

C'est beau, mais... Soit je fais un TCNT1=0 pour synchroniser le signal de sortie sur celui d'entrée, mais de temps en temps une impulsion saute, soit je fais pas, et là, le signal de sortie est propre et complet, mais nage complètement par rapport au signal PMH.

Puis ce n'est pas très stable, ça déraille facilement. Bref, ce n'est pas du tout précis. Il va falloir que je repense tout ça...

les codes finaux :

le simulateur PMH sur MEGA :
Code: [Select]

/*********************************************
**          Générateur signaux PMH          **
**********************************************
**      Circuit de mise au point pour       **
**        ODB / REGULATEUR de vitesse       **
**                                          **
**              DEV : MEGA2560              **
**********************************************
** Pin / por - Signal                       **
**  A0 - PC0 - Motor speed IN (analog)      **
**  04 - PG5 - PMH OUT                      **
**  05 - PE3 - allumage OUT                 **
*********************************************/

volatile byte Cmpt_dent;  // comptage des dents
volatile boolean moteur;  // ON/OFF
volatile byte allumage;   // postion allumage

void setup() {
 TCCR1A = 0x03;        // (WGM1 = Fast PWM)
 TCCR1B = 0x18;   // (WGM1 = Fast PWM CTC), clk = 0
 TIMSK1 = 0x02;   // INT sur OCR1A
 OCR1A = 65530;   // valeur de démarrage moteur
 TCCR1B += 0x01;  // Prescaler = 1/1, Fclk = 16MHz
 DDRG |= 0x20;
 DDRE &= 0xDF;    // PE5 en entrée
 DDRE |= 0x08;     // PE3 en sortie
 PORTE |= 0x20;    // pullup sur PE5
 DDRF &= 0xFE;    // PF0 en entrée
 DDRB |= 0x80;    // led en sortie
 allumage = 36;
}

ISR(TIMER1_COMPA_vect){  // appelée par T1 sur COMPA
 if (Cmpt_dent > 38) {  // secteur PMH
   if (Cmpt_dent == 41) {
     PORTG |= 0x20;
   }
 } else {
   if ((Cmpt_dent & 0x01) == 1) {  // dent impaire
     PORTG |= 0x20;
   } else {                  // dent paire
     PORTG &= 0xDF;
   }
 }
 if (Cmpt_dent == allumage) {
   PORTE |= 0x08;
 } else {
   PORTE &= 0xF7;
 }
 Cmpt_dent++;
 if (Cmpt_dent == 44) Cmpt_dent = 0;  // 1 demi-tour fini
}


volatile word accelerateur, charge;

void loop() {
 accelerateur = analogRead(A0);
//  charge = analogGet(1);  
   Moteur_set_speed(accelerateur, charge);
}
void Moteur_set_speed(word accel, word depression){
 long IC_Val;

     IC_Val = 1090909 / accel;
//      IC_Val = 1090909 / (accel - depression);
//      allumage = depression;
     OCR1A = IC_Val;
     
}


et le code du "multiplicateur" pour UNO :
Code: [Select]

/*********************************************
**        Multiplicateur d'impulsions       **
**********************************************
**        Circuit d'accessoire pour         **
**        ODB / REGULATEUR de vitesse       **
**********************************************
** Pin / por - Signal                       **
**  02 - PD2 - PMH IN                       **
**  09 - PB1 - Imp moteur                   **
**  10 - PB2 - Imp PMH                      **
*********************************************/

volatile byte T2_MSB;  // extention logicielle de timer2
volatile word TCNT2_New, TCNT2_Old; // ancienne mesure de timer2

void Timers_Setup(){  // configure les timers 1 et 2
 TCCR1A = 0x82;   // WGM1 = fast PWM TOP = ICR1
 TCCR1B = 0x18;   // (WGM1 = Fast PWM)
 OCR1A = 100;  // durée d'impulsion sur OC1A = 6.25µs
 
 TCCR2A = 0x00; // normal port operation
 TCCR2B = 0x00; // Clock = 0Hz
 TIMSK2 = 0x01; // int sur OverFlow
 TCNT2_Old = 65535;  // sinon ne démarre pas...
 EICRA = 0x02;  // INT0 sur front descendant
}

void Timers_start(){
 TCCR1B |= 0x01;   //  Prescaler = 1 (16MHz)
 TCCR2B = 0x02; // Clock = 2MHz
 EIMSK = 0x01;  // autoriser INT0
}

void Timers_stop(){
 TCCR1B &= 0xFE;   //  Prescaler = 0 (0MHz)
 TCCR2B = 0x00; // Clock = 0MHz
 EIMSK &= 0xFE;  // autoriser INT0
}

void setup() {
 pinMode(13, OUTPUT);
 Timers_Setup();
 DDRB |= 0x06; // pins PHM_OUT en sortie
 DDRD &= 0xFB; // pin PMH en entrée
 PORTD |= 0x04; // activation pullup pin PMH
 Timers_start();
}

ISR(TIMER2_OVF_vect){   // extention logicielle de T2 en 16 bits
 T2_MSB++;
 if (T2_MSB == 0) { // débordement, le moteur ne tourne pas assez vite ou pas du tout)!
   // a définir
 }
}

ISR(INT0_vect){   // réception impulsion capteur PMH
 TCNT2_New = TCNT2 + (T2_MSB << 8);    // lecture Timer2
 TCNT2 = 0;
 T2_MSB = 0;  // mise à zéro timer2
//  TCNT1 = 0;
 if (TCNT2_New < TCNT2_Old){  // impultion "normale"
   ICR1 = TCNT2_New;   // update Timer1
   TCNT2_Old = TCNT2_New << 1;  // préparation de la prochaine comparaison (TCNT2_Old = TCNT2_New * 2)
   PORTB &= 0xFB; // impulsion PMH OFF
 } else {                     // Impulsion "PMH"
   PORTB |= 0x04; // impulsion PMH ON
 }
}

void loop() {
}


A suivre!

Super_Cinci

#39
Sep 13, 2012, 10:17 pm Last Edit: Sep 13, 2012, 10:24 pm by Super_Cinci Reason: 1
Histoire de, je suis passé au lycée où j'a passé mon bac, y'a pas loin de 18 ans, et en discutant de choses et d'autres, les profs m'ont proposé de présenter ce projet aux jeunes élèves... demain. Il faut donc que je prépare un petit topo sur le bidule, que je leur laisse le générateur PMH en référence, et si certains sont intéressés, ils tenteront de me pondre un PIC.


le générateur en mode maquette...

Moi, ça me botte bien de faire participer des jeunes à un projet concret, surtout que je n'ai jamais vu autant de boîtes arduino sur le bureau d'un prof, ils sont à fond dedans... bilan demain soir, j'ai quelque transparents à préparer (enfin sur une clé USB, c'est fini le temps des rétroprojecteurs...). je vais tenter de leur amener mon tableau de bord avec quelques potars pour leur montrer l'objectif final.

Un futur projet de bac l'année prochaine? (ça me ferait marrer...)

Super_Cinci

#40
Sep 21, 2012, 11:13 pm Last Edit: Sep 22, 2012, 11:27 am by Super_Cinci Reason: 1
Ayant bien endormi les élèves, j'ai continué à lézarder sur mon multiplicateur, et je suis tombé sur le leonardo. Pour 10€ de moins qu'un arduino uno, j'ai une carte qui possède deux timers 16 bits, que demander de plus? Il y a aussi ce timer4, qui peut tourner à 64MHz, c'est alléchant, mais il est limité à 10 bits, et ne possède pas d'ICP4, dommage. Et je ne comprends pas les gens chez ATMEL : ils y collent un timer 16 bits avec trois sorties PWM qui pourrait être très utile, mais ce timer 3 n'est pas câblé sur les sorties, donc ne peut servir que de timer à interruptions soft [OUPS] tout raté, on a accès quand même à ICP3 et OC3A (mais pas OC3B ni OC3C). Donc ça m'arrange super en fait![/OUPS]

J'ai tourné le pb dans tous les sens avec toutes les configs possibles, ben rien de bien bandant, ces foutus 10 bits me cassent toute ma précision.

Mais je pense avoir trouvé un compromis sur ATMEGA32U4 :

T3 à 2MHz sert à la lecture des temps T et 3T via une INT externe déclenchée par le signal, et le timer T1 sert de sortie. sur le papier, ça marche bien, j'arrive à une erreur de +/-0.03% sur la fréquence de sortie pour 6000tr/min (+/- 2tr/min). le % d'erreur diminue grandement avec la vitesse du moteur, dont la vitesse ne dépasse rarement les 3500 / 4000 tr/min (soit 130km/h en 5°, et la plus proche autoroute est à 200km de chez moi...).

Mon extension logicielle du timer2 d'un UNO était pas mal, mais deux interruptions qui peuvent survenir en même temps, ça me créait des sauts pas très élégants dans mes signaux. Là, une seule INT (enfin une autre, pour le débordement de T3 si le moteur ne tourne pas assez vite, mais là, à 125tr/min, la précision n'est pas primordiale, elles peuvent se chevaucher).

J'ai aussi pensé à mesurer la vitesse du moteur et en sortir la valeur en tr/min, puis générer un signal PWM dont la longueur d'impulsion OCR1B par exemple = v_moteur (soit entre 0 et 10000). il me suffirait de mesurer la durée d'impulsion avec un timer à la même fréquence, et plus besoin de calcul... pourquoi pas...

Il faudra que je mesure le temps entre le déclenchement de l'INT et la lecture du timer T3, pour en retrancher ce qu'il faut et récupérer ce retard [OUPS encore] Oui, mais ce côté-là est résolu. Par contre, reste à remettre TCNT3 à 0 le plus vite possible après l'INT ICP3...[/OUPS encore], et ne pas forcément remettre le timer à 0, mais certainement à 1 pour le retard (1 @ 2MHz représente 8 coups d'horloge, ce qu'on dépasse vite avec des variables volatile 16 bits et des sauts d'INT...) l'idéal serait de coder ça en asm, pour savoir exactement ce qu'on fait (puis le code est simple, ça peut valoir le coup). Remettre le timer à 1 au lieu de 0 permet aussi de s'affranchir d'une éventuelle int OVF lors du RAZ... A méditer...

A suivre...

Go Up