Controlling a high pressure gasoline pump with arduino

Hello everyone,

I'm afraid this is going to be a long one so buckle up. :wink:

I'm working on my final project for my bachelor's degree automotive technology and I need to put a standalone ECU on a Audi A1 1.4TFSI. a student last year did the install and first configuration of the ECU and stumbled on a problem: the high pressure fuel pump has a normally closed spill valve instead of a normally open spill valve. A picture of the difference in signal is shown below (longest signal being normally closed):


This means that the ECU sends out the shortest signal.

The previous student had contact with the engineers at Link and they said it couldn’t be done. That’s where I and the Arduino come in to play. So I now need to finish this project and need to find a solution for the pump.

My idea is to take the crankshaft position signal and the pump control signal as my inputs. The crankshaft position signal counts the amount of teeth and resets the signal when TDC comes by so it can count again. This way I can count for example 1 and teeth to start the signal (the start of the signal is always constant in this type of signal). Then I use the pump control signal the end my signal. The pump control signal variates in function of desired pressure, so if I use this signal, the ECU can still calculate the desired fuel pressure and can correct the signal if the pressure is too high or too low.

I programmed the signals separately to test if they worked and they did. Bringing them together was a bit harder but I managed to make something out of it and you can find it down below:

//poorten
const byte Sensor = 2;
const byte Pulse = 7;
const byte End = 3;

//pulsduur
unsigned long delta_new;
unsigned long delta_old;
volatile bool state;

//BDP-trigger
unsigned long trigger;
const float treshhold = 1.9;

//tandentelling
int teller = 1;


//eindherkenning
unsigned long delta_end;
volatile bool eind;
bool signal;

void setup() {
  
  pinMode(End, INPUT);
  pinMode(Sensor, INPUT);
  pinMode(Pulse, OUTPUT);
  
  attachInterrupt(digitalPinToInterrupt(Sensor), crankPos, RISING);
  attachInterrupt(digitalPinToInterrupt(End), end, FALLING);
  
  Serial.begin(115200);

}

void loop()
{
  BDP_herkenning();
  eind_herkenning();
  
  if(state == true){
    
    delta_old = delta_new;
    delta_new = pulseIn(Sensor, HIGH);
    teller++;
    
    state = false;
    
    
  }
  
 if(teller == 30 || teller == 15){
    tone(Pulse, 14000);
   	signal = true;
  }
  
}

void crankPos(){
  state = true;
}

void end(){
  eind = true;  
}

void BDP_herkenning(){
  
  trigger = delta_old * treshhold;
  
  if(delta_new >= trigger && delta_old != 0){
    
    
    Serial.println(teller);
    teller = 1;
  
  }
  
}
  
void eind_herkenning(){
  if(eind == true){
    delta_end = pulseIn(End, LOW, 400);
    
    
    if(delta_end == 0 && signal == true){
      noTone(Pulse);
      signal = false;
      delta_end = 1;
      
    }
    
    eind = false;
  }
  
}

In short, the interrupt on pin 2 gets triggered by the crankshaft position signal and counts a tooth and measures the pulse duration to see if it’s 1.9 times longer than the previous one (there a 2 missing teeth on top dead center). For some reason, it only counts 30 teeth instead of the 58 but it does this consistently so I didn’t bother it. On tooth 30 (is actually the first one) and tooth 15.

Then, interrupt on pin 3 gets triggered by the signal and measures the duration of LOW with a timeout of 400µs. I did this because I want it to return 0 when it is measuring when the signal ends. This way it doesn’t take a long time where the program is just stuck to measure a pulse where I need like a 10th of the pulse to recognize that it needs to stop the signal. So when it returns 0 and the signal was started, it stops the signal.

I measured with a scope the signal and this is what i got:

In blue you can see the crankshaft position signal, in red the pump signal of a normally closed pump, in green the signal of the arduino.

On idle, this works great, but I am having problems on 3000rpm and when I take my foot of the gas because the signal stops briefly because the engine isn’t getting any fuel. In these 2 cases, the Arduino just seems to stop. When the engine is back to idle, it still doesn’t work. I need to manually reset the Arduino before it works again.

If anybody know a way to fix this, or a whole different approach for this project, I am happy to listen.

Sorry for the extremely long text but I wanted to be as complete as possible. Thank you for your time.

I did not look in details but you have possible contention and timing challenges as your code does not have any critical section.

for example as you deal with the flags in the loop, you could miss a crankPos() signal if the interrupts is triggered again right before you do

this will lead to a wrong teller count and as you test only for equality

you might not trigger the signal.

pulseIn() depends on micros() so can't be used in an context where interrupts are disabled, so you would have to possibly detach the isr whilst you dealt with the crank ISR in the loop.

So if I understood that right you meant: move the state = false up to the top of the function and detach isr right after that?

Yes that is most people first reaction, but it won't work:

image

In the first part the valve is de-energized and open. Then the valve gets enegized which closes it. Then the valve is de-energized again but the pressure build up in the compression chamber keeps it closed.

so if we invert this we get: valve gets energized which opens it (normally open valve), then i gets de-energized which closed the valve and let the pressure build up, then it gets de-energized again which opens it again en lets all the pressure build-up away.

So we would end up with 0 bar pressure.

that would solve it only if in the worst case scenario only 1 ISR would be triggered whilst you are in that part of the code. if the ISR triggers faster then you'll miss them.

may be instead of just a flag in crankPos() that you capture in the ISR you should have a byte counter

volatile uint8_t crankCount = 0;
•••
void crankPosISR() {
  crankCount ++;
}

and in the loop you do

  // ---------- start critical section ------------
  noInterrupts()
  uint8_t crankCountCopy = crankCount;
  crankCount = 0; // or may be you don't reset it right away but only when it reached the target count
  interrupts();
  // ---------- end critical section ------------

  // now work only with the copy and let crankCount evolve again if interrupts occur 
  if (crankCountCopy != 0) {
    ... // deal with it


    if (crankCountCopy >= targetCrankCount) {
      ... // deal with it
    
      // ---------- start critical section ------------
      noInterrupts()
      crankCount = 0; // if you didn't reset it right away but only when it reached the target count
      interrupts();
      // ---------- end critical section ------------
    }
  }

the critical section is pretty short (approximately 3 clock cycle) so you are not likely to have two crank ISR triggered during that time and so you won't miss any count.

yeah indeed. And the problem is that I need to extend the signal back in time :joy:

Thank you very much, tommorow I will try this on the car and implement the byte counter if necessarry.

The TDC recognition with counter worked fine without the second interrupt so I am not sure if it wil be necessary. (and I still need to look into what this is and how this exactly works but i'll figure it out)

So i came up with an idea. I am going to put a capacitor in front of the pump signal to make it one big pulse essentially. This i way i can just trigger on the falling edge with no logic behind it to greatly improve respons times of the end recognition.

How does an interrrupt actually trigger? for example, you set to FALLING, does it wait to 1.5V or something before it triggers or does it trigger immediately?

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