Tuesday, July 28, 2020

Building an Engine Controller: The Signal

Any engine control unit uses crankshaft and camshaft position to determine the timing of the various events needed to run an engine. The D16 series Honda engine has three variable reluctance (VR) wheels located in the distributor. The first wheel has one tooth on it which provides an index for the other two wheels. The second has 4 teeth on it and are positioned such that the pistons are at top dead center (TDC) when the tooth is positioned over the magnet. Finally the third wheel has 16 teeth on it to provide a crankshaft position signal (CPS). A photo of the interior of the distributor and a three dimensional drawing of the Honda VR wheels is shown.




I’ll cover a little on how a VR wheel works for those who might not be familiar with them. A permanent magnet is connected to a two transistor circuit which amplifies the signal produced when a ferrous object enters the magnetic field. When the ferrous object leaves the magnetic field the signal goes negative. The resulting AC signal can then be used for timing. You can look up magnetic reluctance for a more detailed explanation.

These signals must be converted to a useful digital waveform that can be subsequently used by the microprocessor. This can be done by using the MAXIM 9926 chip, the National Semi LM 1815 or I used the ON Semi NCV1124. The ON Semi chip was used because it needs a very minimal part count for functionality and I can hand solder the surface mount package to an adapter board. Here's the NCV1124 Data Sheet I initially used the LM 1815 chip but there were two problems using it. It has become obsolete, and at low rotational velocities it misses some of the VR output signals. This is the as built circuit that I used.


To develop and test the VR signal generator circuit I simulated the spinning crankshaft by attaching a two speed motor to the cam shaft with the valve arms removed from the head. This gave me a free spinning system with a variable rotational velocity. You can see it in the next photo.


I then tested the circuit and used the subsequent waveforms to develop a signal simulator using an Arduino Nano Board. The code for the camshaft simulator Nano board is given by:

/*This routine simulates the output from the distributor of a

1980's to 2000 Honda motor

*/

#include <digitalWriteFast.h>

int rpm;

String inputString = ""; // a String to hold incoming data

bool stringComplete = false; // whether the string is complete

// constants won't change. Used here to set the pins

const int outCYL = 2;// the number of the pin

const int outTDC = 3;// the number of the pin

const int outCPS = 4;// the number of the pin

int outStateCYL = LOW;

int outStateTDC = LOW;

int outStateCPS = LOW;

// Time variables

unsigned long newCYL;

unsigned long oldCYL;

unsigned long newTDC;

unsigned long oldTDC;

unsigned long newCPS;

unsigned long oldCPS;

unsigned long intervalCYL;

unsigned long intervalTDC;

unsigned long intervalCPS;

unsigned long cycle;

unsigned long delayTDC, durTDC, delayCPS, durCPS, durCYL;

void setup() {

// set the pins and the serial set-up for checking things:

pinModeFast(outCYL, OUTPUT);

pinModeFast(outTDC, OUTPUT);

pinModeFast(outCPS, OUTPUT);

Serial.begin(9600);

// reserve 200 bytes for the inputString:

inputString.reserve(200);

//initialize a cycle value

cycle = 50000;

Serial.println("Enter a RPM value");

}

void loop() {

if (stringComplete) {

rpm = inputString.toInt();

cycle = 1000000 / (rpm / 60);

Serial.print("Cycle Time = ");

Serial.print(cycle);

Serial.println(" us");

Serial.println("Enter a RPM value");

// clear the string:

inputString = "";

stringComplete = false;

}

newCYL = micros();

durCYL = cycle / 25;

delayTDC = cycle / 100;

durTDC = cycle / 25;

delayCPS = delayTDC - 100;

durCPS = cycle / 28;

if (newCYL - oldCYL >= cycle ) {

oldCYL = newCYL;

}

intervalCYL = newCYL - oldCYL;

//Serial.print(" ");

if (intervalCYL >= 0 && intervalCYL <= 3100) {

outStateCYL = LOW;

digitalWriteFast(outCYL, outStateCYL);

}

else {

outStateCYL = HIGH;

digitalWriteFast(outCYL, outStateCYL);

}

//TDC

if (intervalCYL >= delayTDC && intervalCYL < durTDC) {

outStateTDC = LOW;

digitalWriteFast(outTDC, outStateTDC);

}

if (intervalCYL >= durTDC && intervalCYL < delayTDC + cycle / 4) {

outStateTDC = HIGH;

digitalWriteFast(outTDC, outStateTDC);

}

if (intervalCYL >= delayTDC + cycle / 4 && intervalCYL < durTDC + cycle / 4) {

outStateTDC = LOW;

digitalWriteFast(outTDC, outStateTDC);

}

if (intervalCYL >= durTDC + cycle / 4 && intervalCYL < cycle / 2) {

outStateTDC = HIGH;

digitalWriteFast(outTDC, outStateTDC);

}

if (intervalCYL >= delayTDC + cycle / 2 && intervalCYL < durTDC + cycle / 2) {

outStateTDC = LOW;

digitalWriteFast(outTDC, outStateTDC);

}

if (intervalCYL >= durTDC + cycle / 2 && intervalCYL < 3 * cycle / 4) {

outStateTDC = HIGH;

digitalWriteFast(outTDC, outStateTDC);

}

if (intervalCYL >= delayTDC + 3 * cycle / 4 && intervalCYL < durTDC + 3 * cycle / 4) {

outStateTDC = LOW;

digitalWriteFast(outTDC, outStateTDC);

}

if (intervalCYL >= durTDC + 3 * cycle / 4 ) {

outStateTDC = HIGH;

digitalWriteFast(outTDC, outStateTDC);

}

//---------------------------------------//

//CPS

if (intervalCYL >= delayCPS && intervalCYL < durCPS) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS && intervalCYL < delayCPS + cycle / 16) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + cycle / 16 && intervalCYL < durCPS + cycle / 16) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + cycle / 16 && delayCPS + intervalCYL < cycle / 8) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + cycle / 8 && intervalCYL < durCPS + cycle / 8) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + cycle / 8 && intervalCYL < 3 * cycle / 16) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + 3 * cycle / 16 &&;amp; intervalCYL < durCPS + 3 * cycle / 16) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + 3 * cycle / 16 && intervalCYL < delayCPS + cycle / 4) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + cycle / 4 && intervalCYL < durCPS + cycle / 4) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + cycle / 4 && intervalCYL < delayCPS + 5 * cycle / 16) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + 5 * cycle / 16 && intervalCYL < durCPS + 5 * cycle / 16) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + 5 * cycle / 16 && intervalCYL < delayCPS + 3 * cycle / 8) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + 3 * cycle / 8 && intervalCYL < durCPS + 3 * cycle / 8) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + 3 * cycle / 8 && intervalCYL < delayCPS + 7 * cycle / 16) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + 7 * cycle / 16 && intervalCYL < durCPS + 7 * cycle / 16) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + 7 * cycle / 16 && intervalCYL < delayCPS + cycle / 2) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + cycle / 2 && intervalCYL < durCPS + cycle / 2) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + cycle / 2 && intervalCYL < delayCPS + 9 * cycle / 16) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + 9 * cycle / 16 && intervalCYL < durCPS + 9 * cycle / 16) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + 9 * cycle / 16 && intervalCYL < delayCPS + 5 * cycle / 8) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + 5 * cycle / 8 && intervalCYL < durCPS + 5 * cycle / 8) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + 5 * cycle / 8 && intervalCYL < delayCPS + 11 * cycle / 16) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + 11 * cycle / 16 && intervalCYL < durCPS + 11 * cycle / 16) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + 11 * cycle / 16 && intervalCYL < delayCPS + 3 * cycle / 4) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + 3 * cycle / 4 && intervalCYL < durCPS + 3 * cycle / 4) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + 3 * cycle / 4 && intervalCYL < delayCPS + 13 * cycle / 16) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + 13 * cycle / 16 && intervalCYL < durCPS + 13 * cycle / 16) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + 13 * cycle / 16 && intervalCYL < delayCPS + 7 * cycle / 8) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + 7 * cycle / 8 && intervalCYL < durCPS + 7 * cycle / 8) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + 7 * cycle / 8 && intervalCYL < delayCPS + 15 * cycle / 16) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= delayCPS + 15 * cycle / 16 && intervalCYL < durCPS + 15 * cycle / 16) {

outStateCPS = LOW;

digitalWriteFast(outCPS, outStateCPS);

}

if (intervalCYL >= durCPS + 15 * cycle / 16 ) {

outStateCPS = HIGH;

digitalWriteFast(outCPS, outStateCPS);

}

}

void serialEvent() {

while (Serial.available()) {

// get the new byte:

char inChar = (char)Serial.read();

// add it to the inputString:

inputString += inChar;

// if the incoming character is a newline, set a flag so the main loop can

// do something about it:

if (inChar == '\n') {

stringComplete = true;

}

}

}


I now had a system where I could do all the programming and debugging at my desk. I hooked up the outputs from my crank timer board to another microprocessor board and I was ready to program the main board. One advantage of doing this was that it allowed me to use the development suite extensively and move up the learning curve. With this system connected and the Serial Monitor in the Arduino IDE is opened it asks for a RPM input. When it’s entered the crank timer board changes the interval timing so I could check my spark and injection timing at various simulated crank velocities. The first photo is of the oscilloscope screen of the simulated CYL and CPS signals. The second photo is of the simulated CYL and TDC signals.


No comments:

Post a Comment