Arduino nano rate des interruptions PCINT

Bonjour,

Je suis sur un projet de boitier bioethanol dont le principe est de lire la valeur d'ethanol contenue dans un réservoir d'essence, convertir cette valeur en pourcentage d'augmentation de durée d'injection et prolonger la durée de commande des injecteurs.

Ayant besoin de 4 interruptions pour gérer les 4 cylindres et étant sur arduino nano, j'utilise les PCINT via la librairie PinChangeInterrupt.h.

En parallèle je voulais gérer un écran sur controleur st7735 pour monitorer certaines variables.

Or la partie affichage fait rater des interruptions, là j'affiche que le pourcentage d'ethanol, la fonction prend quand même 27 ms à exécuter et plus je rajoutes de variables à afficher plus il rate des injections (le moteur broute) alors qu'on est à un petite centaine de Hz.

Quelqu'un saurait expliquer pourquoi ? Le principe des interruptions est qu'elles ont la priorité. Or, ici ça ne semble pas être le cas. Je n'ai pas trouvé de cas d'interférence entre la gestion du SPI et les interruptions/timer1.

Si quelqu'un a des idées, je suis preneur...

#include "PinChangeInterrupt.h"
#include <Arduino.h>

//PARTIE AFFICHAGE
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <SPI.h>
#define TFT_CS        5
#define TFT_RST        A5 // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC         3
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

unsigned int VRT;

volatile unsigned int frontMontantFlex, precedentfrontMontantFlex,periodeFlex, periodeVitesse, tempsCycleMoteur, frontMontantVitesse, precedentfrontMontantVitesse, timer, timer2 ;
volatile unsigned long tempsInjectionDeBase, momentOuvertureInj, prededentDeclenchementInj,tempsSupplementaireTick;
unsigned long periodeFlex2, frequence, injDutyCycle, tempsDepuisDerniereInj, dernierAffichage, derniereTemperature, dernierEthanol, dernierRPM;
unsigned int pourcentageEthanol, pourcentageProlongation, regimeMoteur;

float rapportReduction;
byte rapportEngage;
bool capteurDebranche;
bool initialisation = 1;

void setup() {
  // init entrées inj
  pinMode(14, INPUT);//les pullups sont en hardware
  pinMode(15, INPUT);
  pinMode(16, INPUT);
  pinMode(17, INPUT);
  
  attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(14), interruptinj1, CHANGE);//revérifier si front montant ou descendant attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(14), testvide, RISING); 14,10
  attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(15), interruptinj2, CHANGE);//15,9
  attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(16), interruptinj3, CHANGE);//16,6
  attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(17), interruptinj4, CHANGE);//17,7

  // init sorties inj
  pinMode(10, OUTPUT);//INJ1
  pinMode(9, OUTPUT);//INJ2
  pinMode(6, OUTPUT);//INJ3
  pinMode(7, OUTPUT);//INJ4
  digitalWrite(10, LOW);
  digitalWrite(9, LOW);
  digitalWrite(6, LOW);
  digitalWrite(7, LOW);

  //Capteur Ethanol
  pinMode(8, INPUT);
  attachPinChangeInterrupt(digitalPinToPCINT(8), interruptFlex, FALLING);
 
  //Sonde température
  pinMode(A6, INPUT);  

  //Capteur Vitesse
  pinMode(2, INPUT);
  attachInterrupt(digitalPinToInterrupt(0), ISRvitesse, RISING);

  //capteur point mort
  pinMode(4, INPUT);//pullup probablement déjà gérée par l'ECU

  inittimer();
 
  tft.initR(INITR_MINI160x80);  // Init ST7735S mini display  
  tft.setRotation(3);
  tft.fillScreen(ST77XX_BLACK);
}

void interruptinj1(){
  if(digitalRead(14)==LOW){//on vérifie si c'était l'ouverture de l'injecteur
    digitalWrite(10, HIGH);//on commande l'injecteur dès que l'ecu envoie l'info    
    momentOuvertureInj = micros();
    //calcul tours minute:
    tempsCycleMoteur = micros() - prededentDeclenchementInj;
    prededentDeclenchementInj = micros();
    tempsDepuisDerniereInj=0;
  }
  else if(digitalRead(14)==HIGH){//L'ECU stoppe l'injection
    TCNT1 = 0;//on initialise le timer 2 (en 8 bits) à 0
    tempsInjectionDeBase = micros() - momentOuvertureInj;
    tempsSupplementaireTick = tempsInjectionDeBase*2;//x2 car l tick c'est 0.5µs
    tempsSupplementaireTick = tempsSupplementaireTick*pourcentageProlongation;//pourcentage déduit de la lecture du capteur ethanol
    tempsSupplementaireTick = tempsSupplementaireTick/100;//divise par 100 pour un pourcentage
    OCR1A = tempsSupplementaireTick;
		TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt*/  	  
  }
}

void interruptinj2() {
    if (digitalRead(15) == LOW) {//on vérifie si c'était l'ouverture de l'injecteur
        digitalWrite(9, HIGH);//on commande l'injecteur dès que l'ecu envoie l'info        
        momentOuvertureInj = micros();
    }
    else if(digitalRead(15)==HIGH){//L'ECU stoppe l'injection
      TCNT1 = 0;//on initialise le timer 2 (en 8 bits) à 0
      tempsInjectionDeBase = micros() - momentOuvertureInj;
      tempsSupplementaireTick = tempsInjectionDeBase*2;//x2 car l tick c'est 0.5µs
      tempsSupplementaireTick = tempsSupplementaireTick*pourcentageProlongation;//pourcentage déduit de la lecture du capteur ethanol
      tempsSupplementaireTick = tempsSupplementaireTick/100;//divise par 100 pour un pourcentage
      OCR1A = tempsSupplementaireTick;
      TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt*/     
    }
}

void interruptinj3() {
    if (digitalRead(16) == LOW) {//on vérifie si c'était l'ouverture de l'injecteur
        digitalWrite(6, HIGH);//on commande l'injecteur dès que l'ecu envoie l'info        
        momentOuvertureInj = micros();
    }
    else if(digitalRead(16)==HIGH){//L'ECU stoppe l'injection
      TCNT1 = 0;//on initialise le timer 2 (en 8 bits) à 0
      tempsInjectionDeBase = micros() - momentOuvertureInj;
      tempsSupplementaireTick = tempsInjectionDeBase*2;//x2 car l tick c'est 0.5µs
      tempsSupplementaireTick = tempsSupplementaireTick*pourcentageProlongation;//pourcentage déduit de la lecture du capteur ethanol
      tempsSupplementaireTick = tempsSupplementaireTick/100;//divise par 100 pour un pourcentage
      OCR1A = tempsSupplementaireTick;
      TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt*/     
    }
}

void interruptinj4() {
    if (digitalRead(17) == LOW) {//on vérifie si c'était l'ouverture de l'injecteur
        digitalWrite(7, HIGH);//on commande l'injecteur dès que l'ecu envoie l'info        
        momentOuvertureInj = micros();
    }
    else if(digitalRead(17)==HIGH){//L'ECU stoppe l'injection
      TCNT1 = 0;//on initialise le timer 2 (en 8 bits) à 0
      tempsInjectionDeBase = micros() - momentOuvertureInj;
      tempsSupplementaireTick = tempsInjectionDeBase*2;//x2 car l tick c'est 0.5µs
      tempsSupplementaireTick = tempsSupplementaireTick*pourcentageProlongation;//pourcentage déduit de la lecture du capteur ethanol
      tempsSupplementaireTick = tempsSupplementaireTick/100;//divise par 100 pour un pourcentage
      OCR1A = tempsSupplementaireTick;
      TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt*/     
    }
}

ISR(TIMER1_COMPA_vect) {//ISR déclenchée quand OCRn est atteint par le timer
  //pour économiser du code, on peut tout couper à chaque fois  
	digitalWrite(10, LOW);
  digitalWrite(9, LOW);
  digitalWrite(6, LOW);
  digitalWrite(7, LOW);  
}

void loop() {
  if((millis()- dernierEthanol)>100){
    lectureEthanol();
  }
  if((millis()- dernierRPM)>30){
    compteTours();
  }
  if((millis()- dernierAffichage)>200){
    lectureTemperatureMoteur();
  }  
  if((millis()- dernierAffichage)>1000){
    ecran();
  }    
}

void interruptFlex(){  
  frontMontantFlex = micros();
  periodeFlex= frontMontantFlex - precedentfrontMontantFlex;  
  precedentfrontMontantFlex = frontMontantFlex;  
}

void lectureEthanol(){  
  frequence = 1000000/periodeFlex;//on calcule ici pour alléger l'ISR
  if (frequence >150){//erreur
    pourcentageEthanol = 10;//on considere que c'est de l'E10    
  }
  else{
    pourcentageEthanol = map(frequence, 50, 150, 0, 100);//on prend une fréquence qui va de x à x pour la convertir en pourcentage qui va de 0 à 100 //test avec vodka à 40-->170hz  spirytus à 95-->150
  }
    pourcentageProlongation = map(pourcentageEthanol, 0, 85, 0, 33);// quand on a 100% d'ethanol on injecte 33% plus longtemps donc faut convertir quand on n'a pas 100% a verifier si c'es 33% pour 85% ou 100% d'ethanol
     if(frequence==4294967295)
     capteurDebranche = true;
     else
     capteurDebranche = false;
  dernierEthanol=millis();
}

void compteTours(){    
    /*prévoir de le remettre à 0*/
    regimeMoteur = 120000000/tempsCycleMoteur;//60 000 000 * 2 car les rpm sont le double du temps de cycle (4-temps oblige)
    if(regimeMoteur>20000){
      regimeMoteur = 0;
    }
    injDutyCycle = tempsInjectionDeBase * pourcentageProlongation;    
    injDutyCycle = injDutyCycle /100;    
    injDutyCycle = tempsInjectionDeBase + injDutyCycle;    
    injDutyCycle = injDutyCycle*100;
    injDutyCycle = injDutyCycle/tempsCycleMoteur;
    
    dernierRPM=millis();
}

void lectureTemperatureMoteur(){
  VRT = analogRead(A0);              //Acquisition analog value of VRT
  //à voir plus tard
//    En dessous de 7degrés il faut rajouter 10 à 20%
  //
  derniereTemperature=millis();
}

void ecran(){
  unsigned long temps = millis();
  String ligneEcran = "";  
  tft.setCursor(0, 0);
  tft.setTextColor(ST77XX_GREEN,ST77XX_BLACK);
  tft.setTextSize(3);  
  ligneEcran ="E85:" + String(pourcentageEthanol) + "%";
  if(pourcentageEthanol<10){ligneEcran+=" "; }
  if (ligneEcran.length()>8) //pour éviter un bug d'affichage
    tft.println("E85:--");
  else  
    tft.println(ligneEcran); //27ms en moyenne pour afficher juste l'ethanol en slow spi
/*
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW,ST77XX_BLACK);
  ligneEcran ="RPM:" + String(regimeMoteur);
  if(regimeMoteur<1000){ligneEcran+="    "; }
  tft.println(ligneEcran);

  tft.setTextColor(ST77XX_BLUE,ST77XX_BLACK);    
  ligneEcran ="D.C:" + String(injDutyCycle) + "%";
  if(injDutyCycle<10){ligneEcran+="  "; }
  if (ligneEcran.length()<9) //pour éviter un bug d'affichage
    tft.println(ligneEcran);

  tft.setTextSize(1);
  tft.setTextColor(ST77XX_RED,ST77XX_BLACK);    
  ligneEcran ="Temp: " + String(VRT) + "  ";  
  tft.println(ligneEcran);

  ligneEcran =String(frequence) + " Hz  ";  
  tft.println(ligneEcran);
*/
  
/*
  String ligneEcran = "";
  u8x8.setFont(u8x8_font_artossans8_r);//u8x8_font_artossans8_r, u8x8_font_chroma48medium8_r pas mal, u8x8_font_torussansbold8_r excellent, u8x8_font_pcsenior_f aussi
  ligneEcran =String(regimeMoteur) + " RPM     ";
  u8x8.drawString(0,0, string2char(ligneEcran));
  
  ligneEcran = "";
  ligneEcran ="D.C: "+String(injDutyCycle) + "%        ";
  u8x8.drawString(0,2, string2char(ligneEcran));  
  
  u8x8.setFont(u8x8_font_profont29_2x3_f);//u8x8_font_artossans8_r, u8x8_font_chroma48medium8_r pas mal, u8x8_font_torussansbold8_r excellent, u8x8_font_pcsenior_f aussi
  ligneEcran = "";
  ligneEcran ="E: " +String(pourcentageEthanol) + "%   ";
  u8x8.drawString(0,4, string2char(ligneEcran));
*/
  dernierAffichage=millis();
  unsigned long duree = millis() - temps;  
}

void ecran(){
  
  String ligneEcran = "";
  u8x8.setFont(u8x8_font_artossans8_r);//u8x8_font_artossans8_r, u8x8_font_chroma48medium8_r pas mal, u8x8_font_torussansbold8_r excellent, u8x8_font_pcsenior_f aussi
  ligneEcran =String(regimeMoteur) + " RPM     ";
  u8x8.drawString(0,0, string2char(ligneEcran));
  
  ligneEcran = "";
  ligneEcran ="D.C: "+String(injDutyCycle) + "%        ";
  u8x8.drawString(0,2, string2char(ligneEcran));  
  
  u8x8.setFont(u8x8_font_profont29_2x3_f);//u8x8_font_artossans8_r, u8x8_font_chroma48medium8_r pas mal, u8x8_font_torussansbold8_r excellent, u8x8_font_pcsenior_f aussi
  ligneEcran = "";
  ligneEcran ="E: " +String(pourcentageEthanol) + "%   ";
  u8x8.drawString(0,4, string2char(ligneEcran));

  dernierAffichage=millis();
}

void inittimer() {//timer1 sur 16bits
  cli();
  // parametrage du timer 1
  TCCR1A = 0;//permet de définir le mode, ses bits 0 et 1 sont respectivement WGM20 etWGM21, 0 mode normal  
  TCCR1B = 0;
  TCNT1  = 0;
  TIFR1 = 0x00;//registre conprenant les flags d'overflow du timer
  TIMSK1 = 0 ;// autorise les interruptions et leurs flags (modes OCIE1B , OCIE1A et TOIE1)  
  TCCR1B |= (1 << CS11);    // 8 prescaler Définit le prescaler sur ses 3 premiers bits (en partant de la fin)
  sei();
}

Le SPI fonctionne aussi par interruption pour transmettre un octet. Son niveau de priorité est inférieur à une PCINT mais si vous êtes dans l'interruption liée au SPI, vous ne serez pas interrompu par la PCINT. C'est relativement court. Ensuite faut voir si la bibliothèque que vous utilisez pour l'écran ne fait pas un truc moche pour conserver la main pendant tout le transfert des données...

J'utilise cette librairie https://github.com/PaulStoffregen/Adafruit_ST7735/blob/master/Adafruit_ST7735.cpp a première vue ce n'est pas le cas. A noter que c'est un écran qui n'envoie rien sur MISO.
Merci.

Ce qu'il faudrait chiffrer, ce n'est pas le temps occupé par la routine d'affichage mais le temps qu'il lui reste pour afficher.
Il y a pas mal d'interruptions, le code n'est pas toujours optimisé. Donc, il serait intéressant de savoir quel temps il reste pour effectuer les tâches de fond? Comme ça tu pourrais répartir le rafraîchissement de l'affichage sur plusieurs itérations de loop par exemple.

Quand je parle de code peut-être pas optimisé je parle par exemple de ça:
on multiplie par 2 pour diviser par 100 ensuite.
L'idéal, ce serait de ne pas avoir à faire de division. C'est assez chronophage et c'est exécuté 4 fois.

ou ça

Le taux d'éthanol ne change peut-être pas de manière significative en 100ms.

ou ça

Ce serait malin d'éviter que les temps soit des multiples les uns des autres.
Comme ça la charge serait mieux répartie dans le temps.

En effet, j'ai pas optimisé en pensant que chaque interruption avait lieu l'une après l'autre à une fréquence maxi de 90Hz donc qu'il y avait largement le temps.
Tout à fait, la lecture de l'ethanol n'est pas nécessaire si fréquemment, d'autant plus qu'entre l'essence qui passe dans le capteur et dans les injecteurs il y a une certaine latence.

Oui d'ailleurs je vois que j'ai fait une erreur qui ne vérifie pas la température moteur (bien qu'inutile pour l'instant).

Je reteste ce soir et vous redis.

si c'est de Paul Stoffregenil n'y a pas beaucoup de chance qu'il y ait un truc moche :slight_smile:

Hello
as tu un lien sur ton capteur de % d'Ethanol dans un résevoir remplit en E85 mélangé à l'essence qui trainait au fond du réservoir?

Non aucun lien avec le capteur, il fonctionne très bien, pour l'instant c'est du SP95-E10 et à 1% près c'est ce qu'il détecte. Ca n'influence donc pas sur le temps de calcul dans mon code et l'allongement des temps d'injection ne dépasse pas le temps de cycle moteur.

Sinon j'ai vérifié le temps de mes interruptions, la partie ouverture d'injecteur prend 20µs et la partie fermeture 56. C'est pas ça qui fait rater des injections...

Bonjour à tous,

J'ai résolu mon problème en retirant la librairie PinChangeInterrupt.h pour déclarer les pcint "en dur" dans mon code.

Je na saurais pas vous dire quoi en particulier déconnait mais en tout ça le problème se situait là.

Merci

hello, par curiosité et pour comparaison, pourrais tu mettre ton code?
moi aussi, je me suis amusé à le faire avec les PCINT en dur

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.