Arduino ECU #2
Autor: Pedro | Publicado: 2016-03-09T22:37:00-03:00

Giving sequence to the series of the fuel injection posts, here is a pseudo-code of the system. I decided not include the full working code, because every application will be different and will require different solutions and it will result in a code that is radically different. So, this is to read, understand, and from it, I'm sure everyone can replicate the system.
First of all I'll assume you have already read the first part, otherwise, this thing ll not make any sense....

Declaration of necessary things:
First as in every arduino sketch you have to describe all the variables you'll need.
Gone with the basic variables, I'll focus on the stuff I really struggled to make work.
First is a fuel injection map (that thing everyone like to mess around on their cars)
and a ignition map (the storage of these variables goes the same way, so just use the example of the ignition timing here:

const byte avanco[16][11] = {
{-5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5}, //0 &nbsp; &nbsp; &nbsp;- coloquei retardo para ligar o motor
{ 8, &nbsp;6, &nbsp;5, &nbsp;2, &nbsp;0, &nbsp;0, &nbsp;3, 10, 15, 14, &nbsp;9}, //500rpm - marcha lenta
{23, 22, 20, 18, 15, 15, 18, 20, 19, 16, 11}, //1000rpm
{27, 27, 27, 27, 26, 23, 23, 24, 21, 18, 13}, //1500rpm
{30, 30, 30, 30, 30, 30, 30, 27, 24, 21, 16}, //2000rpm
{38, 38, 38, 38, 38, 38, 38, 35, 32, 29, 24}, //2500rpm index5
{45, 45, 45, 45, 45, 45, 45, 42, 39, 36, 31}, //3000rpm
{46, 46, 46, 46, 46, 46, 46, 43, 40, 37, 32}, //3500rpm
{46, 46, 46, 46, 46, 46, 46, 43, 40, 37, 32}, //4000rpm
{46, 46, 46, 46, 46, 46, 46, 43, 40, 37, 32}, //4500rpm
{46, 46, 46, 46, 46, 46, 46, 43, 40, 37, 32}, //5000rpm index10
{46, 46, 46, 46, 46, 46, 46, 43, 40, 37, 32}, //5500rpm
{ 5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5}, //6000rpm
{ 5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5}, //6500rpm
{ 5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5}, //7000rpm
{ 5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5, &nbsp;5}};//7500rpm index15
//0 &nbsp;10 &nbsp;20 &nbsp;30 &nbsp;40 &nbsp;50 &nbsp;60 &nbsp;70 &nbsp;80 &nbsp;90 &nbsp;100 //kPa

this is an array of arrays witch can be accessed, depending of the condition the engine is.
each collum represents a specific engine RPM and each row, an engine load

on the setup:

void setup()
{
attachInterrupt(1, RPM, FALLING); // 1 pino3, chama a funcao RPM, no FALLING edge

TCCR1A = 0;
TCCR1B = 0;

TCCR1B |= (1 &lt;&lt; CS12); &nbsp; &nbsp; &nbsp;// / &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-so o bit CS12-preescaler 256 (vide tabela AVR)

//esse timer estoura com 1.04s

TCCR1B |= (1 &lt;&lt; ICNC1); //seta o Input Capture Noise Canceler faz um buffer de 4 clocks para //mudar de estado

TIMSK1 |= (1 &lt;&lt; OCIE1A); &nbsp; &nbsp;// enable timer 1 compare interrupt A
TIMSK1 |= (1 &lt;&lt; OCIE1B); &nbsp; &nbsp;// enable timer 1 compare interrupt B
TIMSK1 |= (1 &lt;&lt; OCIE1C); &nbsp; &nbsp;// enable timer 1 compare interrupt C
TIMSK1 |= (1 &lt;&lt; TOIE1); &nbsp; &nbsp; // enable timer overflow interrupt
}
On the setup, you should assign every pin and etc, but the main thing is to setup the interruptions and timer1 configuration.


The main code:
Here is a part that seem to not make sense..... anyway, that's the way I did:

void loop()
{
&nbsp; pressao = analogRead(pin_pressao);
&nbsp; calculo1();
}

Yeah.... the mais code is just it.... read all sensors and call all the calculation functions.
why?
Because this way is easier not to just add another function, or another sensor reading, but to coordinate the timing of each operation.
In my case, the calculation necessary is so heavy for the arduino, it take almost 3ms to operate. It may seem not like much, but an engine at 12000 rpm give me just 5ms each turn.
To compensate for that, I broke the calculation into 2 functions: "calculo1" and "calculo2". Since the engine takes 2 turns to complete a cycle, I can run part of the math on every turn, and read the sensors at the time I decide it's the best.


The Interruptions:
Here's the crucial part. Since the arduino can be doing all kinds of crazy math while the engine rev, it's important that the code keep track of what's going on, and do the actuation at the precise moment.

void RPM()
{
&nbsp;&nbsp;interrupts();
&nbsp; if (volta == 1){
&nbsp; &nbsp; t_ciclo = TCNT1;
&nbsp; &nbsp; TCNT1 = 0;
&nbsp; &nbsp; if (Corte == 0){
&nbsp; &nbsp; &nbsp; digitalWrite (bico1, HIGH);
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;OCR1B = T_Injecao_Total_int;
&nbsp; &nbsp; volta=2;
&nbsp; }else if(volta==2){
&nbsp; &nbsp; t_volta = TCNT1;
&nbsp; &nbsp; volta=1;
}
This interruption comes from the TDC sensor. Instead of using a degree wheel or any fancy device, the system (at least for the kart engine, the six cylinder uses a slightly different strategy) uses just the TDC sensor.
The system marks each of the engines turn, and triggers the fuel injector (bico1) just once per CYCLE. The injector start spraying the fuel on TDC and the reason for this is just to simplify the code and because the arduino has just 2 time comparators to use, and one of them is used to close the injector when time reaches the value calculated (OCR1B) the variable "Corte" is there just for cut-off strategies.
The TCNT1 is the value of timer1 at any given moment, and it is reseted every cicle.

ISR(TIMER1_COMPB_vect) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// timer compare interrupt service routine
{
&nbsp; interrup1B();
}

void interrup1B(){
&nbsp; interrupts();
&nbsp; digitalWrite (bico1, LOW);
}

ISR(TIMER1_COMPA_vect) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// timer compare interrupt service routine
{
&nbsp; interrup1A();
}
void interrup1A(){// interrupcao para controlar a bobina de ignicao
&nbsp; interrupts();
&nbsp; if (bob == LOW){
&nbsp; &nbsp; digitalWrite (bobina, HIGH); //liga a bobina
&nbsp; &nbsp; bob = HIGH; //indica que a bobina foi ligada
&nbsp; &nbsp; OCR1A = TCNT1 + dw_t; //seta essa interrupcao novamente para daqui o tempo de carga
&nbsp; }else{
&nbsp; &nbsp; digitalWrite (bobina, LOW); //efetivamente o disparo da bobina!
&nbsp; &nbsp; bob = LOW; //indica que a bobina foi desligada e nao faz mais nada...
&nbsp; }
}
Here are the interrupts that control the closing time of the injector, and turns the ignition coil on and off. These interrupts works as is. The only thing to notice here is that the interruption A that control the ignition, turns the ignition ON, and reassigns itself to turn OFF, this works because the coil need to be on for a fixed amount of time depending of the battery voltage.
Another thing that need attention is the fact that every interruption just call a function, and the first thing of the function is to turn the interruption on, which make possible to run an interruption inside an interruption (totally INCEPTION!)

Calculations:
On another post....