DUE missing tooth signal generator

I'm new to this so I apologize if something doesn't make sense. I am using a DUE to generate signals for an Engine Control Unit. I have a couple of problems that I can't find answers for. A digital out put signal is used to generate a signal simulating a camshaft signal and a second signal simulates a signal from a crankshaft position sensor. The crank shaft has 3 sets of 20 teeth and each set is separated from the others with a "missing tooth.

Two major problems I'm dealing with; 1st, the duration of the tooth signals changes, maybe 3 different pulse width going from long to shorter with shorter maybe being 1/3 to 1/2 of long. The high state is high about 1/4 the time vice 1/2. The second problem is that I don't see a missing tooth. I have code to keep the tooth signal low at the end of the 20 teeth but it doesn't seem to be effective.

A copy of the sketch is below. Thanks in advance.

const int RPM_c = A0; // Coarse RPM input from JimStim to A0

const int RPM_f = A1; // Fine RPM input from JimStim to A1

const int Tooth_Out = 22; // Tooth output signal to pin 22

const int Cam_Out = 23;  //Cam output signal to pin 23

int n; //counter

int t; //timer value

void setup() {

pinMode(Tooth_Out, OUTPUT); // enable output for tooth

pinMode(Cam_Out, OUTPUT); // enable output for cam

analogReadResolution(12);

Serial.begin(9600); // Open serial port and sets baud rate

Serial.flush(); // flush residual characters

}

void loop() {

int RPM_C=analogRead(RPM_c); // Read coarse RPM 

int RPS_C = map(RPM_C, 0, 4095, 0, 100); // Convert digital counts to coarse Rev/Sec

int RPM_F=analogRead(RPM_f); // Read fine RPM 

int RPS_F = map(RPM_F, 0, 4095, 0, 5); // Convert digital counts to fine Rev/Sec

int RPS_T = RPS_C + RPS_F; // Add fine and coarse Rev/Sec' for a total Rev/Sec

int Tooth_Time = 1000000/((RPS_T*63)*2); // Convert total Rev/Sec to approximate tooth time in uSec (3175-79 uSec)

for (n = 0; n < 20; n++) // tooth signal for for 1st 120 degrees of crank rotation

{digitalWrite(Tooth_Out, HIGH);

delayMicroseconds(Tooth_Time); // delay for tooth

digitalWrite(Tooth_Out, LOW);

delayMicroseconds(Tooth_Time); // delay for gap between teeth

if (n == 8) {digitalWrite(Cam_Out, HIGH); t=micros(); Serial.println(t); Serial.println(Cam_Out);} // cam signal

delayMicroseconds(2*Tooth_Time); // delay for missing tooth

}

for (n = 0; n < 20; n++) // tooth signal for for 2nd 120 degrees of crank rotation

{digitalWrite(Tooth_Out, HIGH);

delayMicroseconds(Tooth_Time); // delay for tooth

digitalWrite(Tooth_Out, LOW);

delayMicroseconds(Tooth_Time); // delay for gap between teeth

delayMicroseconds(2*Tooth_Time); // delay for missing tooth

}

for (int n = 0; n < 20; n++) // tooth signal for for 3rd 120 degrees of crank rotation

{digitalWrite(Tooth_Out, HIGH);

delayMicroseconds(Tooth_Time); // delay for tooth

digitalWrite(Tooth_Out, LOW);

delayMicroseconds(Tooth_Time); // delay for gap between teeth

delayMicroseconds(2*Tooth_Time); // delay for missing tooth

}

for (int n = 0; n < 20; n++) // tooth signal for 4th 120 degrees of crank rotation

{digitalWrite(Tooth_Out, HIGH);

delayMicroseconds(Tooth_Time); // delay for tooth

digitalWrite(Tooth_Out, LOW);

delayMicroseconds(Tooth_Time); // delay for gap between teeth

if (n == 8) {digitalWrite(Cam_Out, LOW); t=micros() - t; Serial.println(t); Serial.println(Cam_Out); } // cam signal

delayMicroseconds(2*Tooth_Time); // delay for missing tooth

}

for (int n = 0; n < 20; n++); // tooth signal for for 5th 120 degrees of crank rotation

{digitalWrite(Tooth_Out, HIGH);

delayMicroseconds(Tooth_Time); // delay for tooth

digitalWrite(Tooth_Out, LOW);

delayMicroseconds(Tooth_Time); // delay for gap between teeth

delayMicroseconds(2*Tooth_Time); // delay for missing tooth

}

for (int n = 0; n < 20; n++) // tooth signal for for 6th 120 degrees of crank rotation

{digitalWrite(Tooth_Out, HIGH);

delayMicroseconds(Tooth_Time); // delay for tooth

digitalWrite(Tooth_Out, LOW);

delayMicroseconds(Tooth_Time); // delay for gap between teeth

delayMicroseconds(2*Tooth_Time); // delay for missing tooth

}

}

I think before looking at code you need to more clearly your goal and issue.

For instance,

  • You say the crank shaft has 3 gears each with 20 teeth.
  • Clearly none are "missing" a tooth, could you perhaps mean the are off set by one tooth?
  • Your outputs need to change in duration when the speed changes. Is this required or is it just how the actual cam output will be. i.e. to sync the ???? with the cam is the exact wave shape required of just the correct period?

JohnRob,
I don't see where I said there where there are 3 gears each with 20 teeth. I acknowledge the details could be confusing. On the perimeter of the crankshaft there are a total of 60 teeth. a set of 20 teeth evenly spaced, followed by a "missing tooth" or notch, then another set of 20 teeth evenly spaced, again followed by a "missing tooth", then third set of 20 teeth evenly spaced, again followed by a missing tooth. The missing tooth is located 12 teeth before a cylinder is in it's Top Dead Center location.

As the engine RPMs change, the duration time each tooth is sensed by a variable reluctance sensor changes and the duration of the output from that sensor changes. Higher RPMs result in teeth being in a position to be sensed less by the sensor so the time is reduced. Similarly, lower RPMs result in teeth being in a position to be sensed by the sensor more so the time is increased. This information is used by the Engine Control Unit (ECU) to calculate engine RPM. The Cam shaft signal is generated by a hall effect sensor and is used by the ECU to determine which of the six cylinders is ready to fire so a signal can be sent from the ECU to one of 6 fuel injectors and the associated spark plug.

The DUE signal generator is intended to provide signals to the ECU on the bench to simulate input tooth and cam signals. Other signals such as Manifold Air Temperature, Coolant Temperatures Air / Fuel Ratio etc are provided by other means. Once all the inputs are provided to the ECU, it will generate output signals. The simulator originally intended for the ECU does not provide a cam signal and as such, bench simulations are not possible.

The shape of the signals is not critical. In reality as the engine speed increases the amplitude of the tooth signal increases but the 3.3 volt output is more than enough to meet the ECU requirements. The true tooth signal is close to a triangle wave but the first stages of the ECU front end convert the triangle wave to a square wave so the shape is not critical. The real cam signal is close to a square wave.The timing of the tooth signal and and its timing with the cam signal are critical. The ECU performs checks of these two signals and if they are not correct an error signal is generated and the ECU disables all outputs.

My goal is to generate tooth and cam signals that are acceptable for the ECU. The tooth signal should be a square wave with 20 cycles followed by a low signal for a "missing tooth. This cycle is continuously repeated.During the first 20 teeth, the cam signal should change state (high to low or low to high) 12 teeth before the next missing tooth. Three sets of 20 teeth pass and on the 4th set, the cam signal again changes state opposite from the previous change of state. The change of state of the cam signal is not critical to which of the three tooth sets.

Does this help or is more information required?

Thanks

Gene

I have code to keep the tooth signal low at the end of the 20 teeth but it doesn't seem to be effective.

delayMicroseconds(2*Tooth_Time);

You seem to have this code after every tooth pulse within the for loop() rather than after the 20 pulses. I think it should be placed between the 120 degree sections.

for (n = 0; n < 20; n++) // tooth signal for for 1st 120 degrees of crank rotation

{digitalWrite(Tooth_Out, HIGH);

delayMicroseconds(Tooth_Time); // delay for tooth

digitalWrite(Tooth_Out, LOW);

delayMicroseconds(Tooth_Time); // delay for gap between teeth

if (n == 8) {digitalWrite(Cam_Out, HIGH); t=micros(); Serial.println(t); Serial.println(Cam_Out);} // cam signal

//delayMicroseconds(2*Tooth_Time); // delay for missing tooth

}
delayMicroseconds(2*Tooth_Time); // delay for missing tooth

for (n = 0; n < 20; n++) // tooth signal for for 2nd 120 degrees of crank rotation

{digitalWrite(Tooth_Out, HIGH);

delayMicroseconds(Tooth_Time); // delay for tooth

digitalWrite(Tooth_Out, LOW);

delayMicroseconds(Tooth_Time); // delay for gap between teeth

//delayMicroseconds(2*Tooth_Time); // delay for missing tooth

}
delayMicroseconds(2*Tooth_Time); // delay for missing tooth

Cattledog,
That seems to have fixed all my issues. I'll have to spend some more time confirming this tomorrow. Thank you

I understand now. My issue was I immediately think of a meshing assembly when I hear "Gear" :slight_smile:

So you basically have a CPS (crank position sensor).

Thinking of an approach before code; You need a variable frequency square wave with every 21st pulse blanked out. I would think the blanking would be a simple counter that would logically disable each 21st pulse.

What granularity can you accept? I realize an engine has no granularity that I'm aware of.

What will control the frequency of the output?

JohnRob,
Your assessment of blanking the 21st pulse is correct.

I'm not aware of repeatability requirements but I do know that when cranking an engine to start, the time between teeth changes a small amount do to the pressure in the cylinder changing.

The simulator signals are supposed to be generated in a JimStim simulator (JimStim - The Megasquirt Stim with a Wheel Simulator). I've removed a microprocessor and am using a DIP-16 plug as the interface with the Due and the JimStim. The JimStim has a fine and coarse potentiometer that provide a 0-3.3 Vdc signal for speed.

That said, Cattledog had a recommendation that I thought fixed by issues but it winds up not all. The JimStim has a transistor between the microprocessor and the output from the board so I had to reverse the tooth and gap high and low digitalWrites. I also had to change the count of teeth between the missing tooth and the cam signal. That still may not be correct but that is in my court.

The problem I have right now is that it appears that digitalWrite signals are not being developed for the 5th set of teeth. All the others have the missing tooth followed by 20 teeth but after the fourth set, I have a two missing teeth signals and the teeth for the 6th set. The code for sets 2, 3, 5, and 6 are all the same so I'm not sure why there is a delta. A screen shot of the oscilloscope is included.


The latest version of the sketch is below.

Thanks

const int RPM_c = A0; // Coarse RPM input from JimStim to A0
const int RPM_f = A1; // Fine RPM input from JimStim to A1
const int Tooth_Out = 22; // Tooth output signal to pin 22
const int Cam_Out = 23;  //Cam output signal to pin 23
const int Offset = 3; // Cam to Notch Sync
int n; //counter one

void setup() {
pinMode(Tooth_Out, OUTPUT); // enable output for tooth
pinMode(Cam_Out, OUTPUT); // enable output for cam
analogReadResolution(12);
Serial.begin(9600); // Open serial port and sets baud rate
Serial.flush(); // flush residual characters


}

void loop()
{
int RPM_C=analogRead(RPM_c); // Read coarse RPM 
int RPS_C = map(RPM_C, 0, 4095, 0, 100); // Convert digital counts to coarse Rev/Sec
int RPM_F=analogRead(RPM_f); // Read fine RPM 
int RPS_F = map(RPM_F, 0, 4095, 0, 5); // Convert digital counts to fine Rev/Sec
int RPS_T = RPS_C + RPS_F; // Add fine and coarse Rev/Sec' for a total Rev/Sec
int Tooth_Time = 1000000/((RPS_T*63)*2); // Convert total Rev/Sec to approximate tooth time in uSec (3175-79 uSec)

for (n = 0; n < 20; n++) // tooth signal for 1st 120 degrees of crank rotation
{digitalWrite(Tooth_Out, LOW);
delayMicroseconds(Tooth_Time); // delay for tooth
digitalWrite(Tooth_Out, HIGH);
delayMicroseconds(Tooth_Time); // delay for gap between teeth
if (n == Offset) {digitalWrite(Cam_Out, LOW);} // cam signal
}
delayMicroseconds(2*Tooth_Time); // delay for missing tooth


for (n = 0; n < 20; n++) // tooth signal for 2nd 120 degrees of crank rotation
{
digitalWrite(Tooth_Out, LOW);
delayMicroseconds(Tooth_Time); // delay for tooth
digitalWrite(Tooth_Out, HIGH);
delayMicroseconds(Tooth_Time); // delay for gap between teeth
}
delayMicroseconds(2*Tooth_Time); // delay for missing tooth

for (n = 0; n < 20; n++) // tooth signal for 3rd 120 degrees of crank rotation
{
digitalWrite(Tooth_Out, LOW);
delayMicroseconds(Tooth_Time); // delay for tooth
digitalWrite(Tooth_Out, HIGH);
delayMicroseconds(Tooth_Time); // delay for gap between teeth
}
delayMicroseconds(2*Tooth_Time); // delay for missing tooth

for (n = 0; n < 20; n++) // tooth signal for 4th 120 degrees of crank rotation
{
digitalWrite(Tooth_Out, LOW);
delayMicroseconds(Tooth_Time); // delay for tooth
digitalWrite(Tooth_Out, HIGH);
delayMicroseconds(Tooth_Time); // delay for gap between teeth
if (n == Offset) {digitalWrite(Cam_Out, HIGH);} // cam signal
}
delayMicroseconds(2*Tooth_Time); // delay for missing tooth

for (n = 0; n < 20; n++); // tooth signal for 5th 120 degrees of crank rotation
{
digitalWrite(Tooth_Out, LOW);
delayMicroseconds(Tooth_Time); // delay for tooth
digitalWrite(Tooth_Out, HIGH);
delayMicroseconds(Tooth_Time); // delay for gap between teeth
}
delayMicroseconds(2*Tooth_Time); // delay for missing tooth

for (n = 0; n < 20; n++) // tooth signal for 6th 120 degrees of crank rotation
{
digitalWrite(Tooth_Out, LOW);
delayMicroseconds(Tooth_Time); // delay for tooth
digitalWrite(Tooth_Out, HIGH);
delayMicroseconds(Tooth_Time); // delay for gap between teeth
}
delayMicroseconds(2*Tooth_Time); // delay for missing tooth

}

I think your scope is running too slow, you have multiple traces so its hard to distinguish what is going on. If you scope doesn't have a fancy trigger, perhaps you could put a pulse on another output and trigger from that.

You probably should use a logic analyzer. For simple stuff they can be reasonably priced.

I do not see anywhere after the fourth for() loop that there is a double wait. I just see this statement at all places
delayMicroseconds(2*Tooth_Time); // delay for missing tooth

I haven't worked through the numbers for Tooth_Time, but there is a maximum value of 16383 for delayMicroseconds() and you may be running into that if you were using 4*Tooth_Time.

//delayMicroseconds(4*Tooth_Time);
delayMicroseconds(2*Tooth_Time);
delayMicroseconds(2*Tooth_Time);

Cattledog,
I did some playing with serial.prints and times between various portions of the sketch and it appears that the delayMicroseconds won't work for me for whatever reason. I think I'll just have to slug through the various documents to get code for a timer interrupt for the due. Thanks for the suggestions.

I would try and understand the "whatever reason". It appears that using delayMicroseconds() was working when you developed the code using that approach.

After a bunch of homework for this, I come to the conclusion that I'm asking the Due to do more than it is capable of doing.

When connected to the ECU, I see the tooth and cam signals but get near constant synchronization errors. When "playing" with various parameters, I can occasionally sync up below around 300 RPM but this is way too low to be a meaningful help. The speeds I'm looking for seem to be too fast for the Due. Using the delayMicroseconds() function, I can get wave-forms that have the right shapes but are too long based on times measured on an oscilloscope.

I've made several efforts at using the ARM timers as identified in an Application Note (https://copperhilltech.com/free-download-arduino-due-timer-control-application-note/) and similar sites,but I never seemed to be able to get the sketch to compile. The details of the ARM Cortex-M3 processor’s details are right now beyond me and I've got other things I need to do. Thanks for the reviews.

I had to get back into this for multiple reasons. Attached is a sketch that works; it starts the 84 MHz clock and puts it in service. The are some "instability" issues I've noticed when it is connected to the ECU but I'm not sure if the problem is with the Arduino or the ECU. Some teeth show longer than expected times - none shorter. One must get down into the microsecond range to see this.
RPM_Generator.ino (7.0 KB)

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