Hi all, I have a project I would like some help with. The code I have so far is attached and works great. It is on a pro mini 16Mhz clone. I repair CDI modules for mowers and other small engines and for a couple years I have had a 555 timer that I have used for testing. It is lacking in several ways. Now I have started this project that will have:
a LCD to display rpm from 500 to 3000
generate a pulse at intervals from 120Ms to 20Ms to trigger the CDI (one pulse / revolution)
generate pulses at 50Hz to 300Hz to simulate the charging coils. Here may be the tricky
part, as there are 6 pulses per revolution, but skip every 6th pulse and have that time interval
align with the above triggering pulse. (you don't want a triggering pulse at the same time the
charging coil has output because the part that the charging coil charges is shorted at that
moment)
input the trigger pulse and also input a signal from the coil and display the time interval as ° timing, as at low rpm there is a delay varying from 100us to 0us approx. The delay at low rpm is taken away at higher rpm to produce advance.
What I have so far is the LCD which works well and displays the rpm. Also for now I had it display the Ms between pulses just to be sure it was changing correctly. The pulse out I made 1Ms and used 2 transistors to switch 12 vdc to the input of the CDI, works well. I added the pin 7 output as the future high pulse rate simulating charging coils. I will feed this also with 2 transistors through a transformer to obtain approx 200 vac.
Thanks, hope it is not to ambitious!
(edited errors)
//Pulse Generator Arduino Code
#include "U8glib.h"
U8GLIB_ST7920_128X64_1X u8g(13, 11, 10); // SPI Com: SCK = en = 15, MOSI = rw = 16, CS = di = 17
int pot1 = A5; // select the input pin for the pot
int potValue = 0; // variable to store the value coming from the sensor
int pulseValue = 0; // 2 variables from one pot
int delayValue = 0;
int outputPin = 8; // select the pin for the output for trigger pulse
int chargePin = 7; // select the pin for the output for charge pulses
void draw() {
// graphic commands to redraw the complete screen should be placed here
u8g.setPrintPos(0, 15); // call procedure from base class, http://arduino.cc/en/Serial/Print
u8g.print("RPM =");
u8g.setPrintPos(50, 15);
u8g.print(potValue);
u8g.setPrintPos(8, 30);
u8g.print("Ms =");
u8g.setPrintPos(50, 30);
u8g.print(pulseValue);
u8g.setPrintPos(0, 45);
u8g.print("Advance =");
u8g.setPrintPos(80, 45);
u8g.print("10"); // for use in future code
}
void setup() {
Serial.begin(115200); // serial com speed for display
// flip screen, if required
// u8g.setRot180();
pinMode(outputPin, OUTPUT); // declare the outputPin as an OUTPUT
pinMode(chargePin, OUTPUT); // declare the chargePin as an OUTPUT
u8g.firstPage();
u8g.setFont(u8g_font_unifont);
delay (2000);
//Timer1 set up 2ms pulse every 50 ms
TCCR1A = 0;
TCCR1B = (1 << WGM12);//CTC mode to OCR1A
OCR1A = 12500; //50 ms to top value
TIMSK1 |= (1 << OCIE1A);//interrupt enable compareA
TIMSK1 |= (1 << OCIE1B);//interrupt enable compareB
OCR1B = 250; //1 ms pulse width
TCCR1B |= (1 << CS11) |(1<<CS10); //prescaler 64 4us/tick
}
void loop() {
// picture loop
u8g.firstPage();
do {
draw();
} while( u8g.nextPage() );
potValue = analogRead(pot1); // read the value from the pot for rpm
pulseValue = analogRead(pot1); // read the value from the pot for pulse
delayValue = analogRead(pot1); // read the value from the pot for pulse delay
potValue = map(potValue, 0, 1023, 500, 3000); // map pot for 500 - 3000 for rpm display
pulseValue = map(pulseValue, 0, 1023, 120, 20); // map pot for pulse delay 20 to 120 Ms
delayValue = map(delayValue, 0, 1023, 30000, 5000); // map pot for actual pulse delay
delay (10);
}
ISR(TIMER1_COMPA_vect) {
OCR1A = (delayValue); // value to set delay between pulses
digitalWrite(outputPin,HIGH); //turn on pin 8
digitalWrite(chargePin,HIGH); //turn on pin 7
}
ISR(TIMER1_COMPB_vect) {
digitalWrite(outputPin,LOW); //turn off pin 8
digitalWrite(chargePin,LOW); //turn off pin 7
}
Let me see if I have this correct. I understand 1,2, and 3 as follows.
You have a 1 ms CDI trigger pulse at 0 degrees. The period between trigger pulses is 20 to 120 ms. In the CDI system is this the trigger for the discharge of the capacitor to the coil?
Then, within that period you then want to output a ? ms pulse at 60°, another at 120°, another at 180°, another at 240° and one at 300°. That's five pulses spaced 360/6 with a gap at 0. This part seems straight forward. In the CDI system, are these pulses charging the capacitor?
I'm not certain about point 4
input the trigger pulse and also input a signal from the coil and display the time interval as ° timing, as at low rpm there is a delay varying from 100us to 0us approx. The delay at low rpm is taken away at higher rpm to produce advance.
The trigger fires. The capacitor discharges into the coil. Please explain more about the "signal from the coil". If you can get this into a 5v signal for input into the Arduino it should be possible to calculate the time from the trigger pulse. I'm not sure if this is better done with a pin change (or external interrupt) or a timer input capture.
For development, you can certainly simulate this pulse with one generated by the Arduino or from possibly a second one. Do you have a second pro mini to possibly use as a coil output signal generator?
I'm not sure if your project will require the millis() and micros() timer. You may need three timers --one to generate the trigger, one to generate the 5 pulses, and one to time the ignition delay. It's possible that this project could benefit from additional hardware timers. Are you bound to the pro mini, or is there the possibility of using a Mega with the additional timers.
I would recommend developing this system without the TFT display. Use a 4x20 lcd or serial to a laptop. Your program will be complicated enough without having to work around the blocking characteristics of the display draw while you are working through other issues.
I take it you mean 18,000rpm, not 18,000 Hz - that would be a pretty quick engine!
regards
Allan.
ps I've looked at these gadgets - the ones I've seen are potted in epoxy or similar, and I've always thought them unrepairable. How do you get them apart?
Let me see if I have this correct. I understand 1,2, and 3 as follows.
You have a 1 ms CDI trigger pulse at 0 degrees. The period between trigger pulses is 20 to 120 ms. In the CDI system is this the trigger for the discharge of the capacitor to the coil?
Yes this simulates the pusle coil and tells the CDI to fire.
Then, within that period you then want to output a ? ms pulse at 60°, another at 120°, another at 180°, another at 240° and one at 300°. That's five pulses spaced 360/6 with a gap at 0. This part seems straight forward. In the CDI system, are these pulses charging the capacitor?
Yes that is the a/c from the charging coil under flywheel that outputs around 200vac
I'm not certain about point 4
The trigger fires. The capacitor discharges into the coil. Please explain more about the "signal from the coil". If you can get this into a 5v signal for input into the Arduino it should be possible to calculate the time from the trigger pulse. I'm not sure if this is better done with a pin change (or external interrupt) or a timer input capture.
I can use a induction coil or a voltage divider to pick up a signal from the coil.
For development, you can certainly simulate this pulse with one generated by the Arduino or from possibly a second one. Do you have a second pro mini to possibly use as a coil output signal generator?
I'm not sure if your project will require the millis() and micros() timer. You may need three timers --one to generate the trigger, one to generate the 5 pulses, and one to time the ignition delay. It's possible that this project could benefit from additional hardware timers. Are you bound to the pro mini, or is there the possibility of using a Mega with the additional timers.
No I can use any other board as needed, the generation of the 5 pulses for charging is just a luxury,
I can use a high impedance transformer like I have now.
I would recommend developing this system without the TFT display. Use a 4x20 lcd or serial to a laptop. Your program will be complicated enough without having to work around the blocking characteristics of the display draw while you are working through other issues.
allanhurst:
I take it you mean 18,000rpm, not 18,000 Hz - that would be a pretty quick engine!
regards
Allan.
ps I've looked at these gadgets - the ones I've seen are potted in epoxy or similar, and I've always thought them unrepairable. How do you get them apart?
Ok, 6 charging coils with magnet turning at 3000 rpm would be 300Hz signal so output needs
to be 50 - 300Hz.
I find the cheaper ones are epoxy but MANY others are in something softer.
the generation of the 5 pulses for charging is just a luxury,
If you use the five pulses, what duration should they be?
If you use only one pulse, what duration should it be, and how should it be timed to the trigger?
Top line would only have 1 pulse at the time where the second line does
not.
The top line represents a system clock and the overall timing. There are no output pulses, but the other pulses are timed in relationship to the clock.
cattledog:
If you use the five pulses, what duration should they be?
These pulses would be fed to a small transformer so they can just be square wave.
If you use only one pulse, what duration should it be, and how should it be timed to the trigger?
.If I don't even have these pulses it is not that bad for a tester, I can just use a transformer off the line with isolation. Right now I have 2 12v to 120v transformers back to back so I also get my 12 vdc from them.
The top line represents a system clock and the overall timing. There are no output pulses, but the other pulses are timed in relationship to the clock.
Here is a some test code with the advance (coil pulse delay) measurement in microseconds patched into a serial version of your code.
I have simulated a delay pulse with a signal on pin 4 generated by a software timer which sends a pulse 5 milliseconds after the main trigger. From my testing, most of the timing error is due to the analogRead() going on. Putting the advance measurement on a timer input capture interrupt, instead of an external interrupt might be more accurate.
How accurate does the advance measurement need to be? What is the range of advance measurements?
//Pulse Generator Serial Output Arduino Code
int pot1 = A5; // select the input pin for the pot
int potValue = 0; // variable to store the value coming from the sensor
int pulseValue = 0; // 2 variables from one pot
int delayValue = 0;
int outputPin = 8; // select the pin for the output for trigger pulse
int chargePin = 7; // select the pin for the output for charge pulses
volatile boolean trigger = false;
volatile unsigned long delayPeriod;
unsigned long copy_delayPeriod;
volatile unsigned long delayPeriodStart;
void setup() {
Serial.begin(115200); // serial com speed for display
pinMode(outputPin, OUTPUT); // declare the outputPin as an OUTPUT
pinMode(chargePin, OUTPUT); // declare the chargePin as an OUTPUT
delay (2000);
pinMode(4, OUTPUT); //signal out for delay test
//Timer1 set up 1ms pulse every 50 ms
TCCR1A = 0;
TCCR1B = (1 << WGM12);//CTC mode to OCR1A
OCR1A = 12500; //50 ms to top value
TIMSK1 |= (1 << OCIE1A);//interrupt enable compareA
TIMSK1 |= (1 << OCIE1B);//interrupt enable compareB
OCR1B = 250; //1 ms pulse width
TCCR1B |= (1 << CS11) | (1 << CS10); //prescaler 64 4us/tick
attachInterrupt(digitalPinToInterrupt(3), delayPeriodTiming, RISING);
}
void loop() {
potValue = analogRead(pot1); // read the value from the pot for rpm
pulseValue = analogRead(pot1); // read the value from the pot for pulse
delayValue = analogRead(pot1); // read the value from the pot for pulse delay
potValue = map(potValue, 0, 1023, 500, 3000); // map pot for 500 - 3000 for rpm display
pulseValue = map(pulseValue, 0, 1023, 120, 20); // map pot for pulse delay 20 to 120 Ms
delayValue = map(delayValue, 0, 1023, 30000, 5000);
//my test for trigger pulse delay response)
if (trigger == true)
{
if (micros() - delayPeriodStart >= 5000) //5 ms delay
{
trigger = false;
digitalWrite(4, HIGH);//simulate pulse
digitalWrite(4, LOW);
noInterrupts();
copy_delayPeriod = delayPeriod;
interrupts();
Serial.print("RPM =");
Serial.println(potValue);
Serial.print("Ms =");
Serial.println(pulseValue);
Serial.print("AdvanceMicroseconds =");
Serial.println(copy_delayPeriod); // for use in future code
}
}
}
ISR(TIMER1_COMPA_vect) {
OCR1A = (delayValue); // value to set delay between pulses
digitalWrite(outputPin, HIGH); //turn on pin 8
digitalWrite(chargePin, HIGH); //turn on pin 7
}
ISR(TIMER1_COMPB_vect)
{
digitalWrite(outputPin, LOW); //turn off pin 8
digitalWrite(chargePin, LOW); //turn off pin 7
delayPeriodStart = micros();//start looking for response
trigger = true;
}
void delayPeriodTiming()
{
delayPeriod = micros() - delayPeriodStart;
}
I am repairing just one such CDI right now and will get (hopefully) some measurements from it regarding pulse delay. I anticipate that 5 Ms delay will give 18° advance when it is taken away. I would think a range of 20° would be more than adequate. Also +/- .5° if obtainable would be fine, these are not race car engines!
So far unable to measure and delay! I have one OEM CDI and a cheap Chinese one that should have delay, I am looking at the pulse and the primary of the ign coil with dual trace scope (Tektronix 585A) at any speed pulses are at same time!
I loaded the above code and it compiled and loaded but apparently locks up, the small led flashes once and goes out, no response after that??
I'm running it now on a Uno. Did you jumper pin 4 to pin 3? Did you set the Serial monitor to 115200 baud? I am also running a jumper from 3.3v to A5 for the analog read. Ms =56 RPM = 2110 delay = 5400 us.
RPM =2137
Ms =56
AdvanceMicroseconds =5052
RPM =2137
Ms =56
AdvanceMicroseconds =5052
RPM =2137
Ms =56
AdvanceMicroseconds =5060
RPM =2127
Ms =56
AdvanceMicroseconds =5232
RPM =2117
Ms =56
AdvanceMicroseconds =5224
RPM =2130
Ms =56
AdvanceMicroseconds =5504
I still have the 12864B display and it is a serial setup. I would like to understand "I would recommend developing this system without the TFT display. Use a 4x20 lcd or serial to a laptop. Your program will be complicated enough without having to work around the blocking characteristics of the display draw while you are working through other issues."
Yes 3 and 4 are jumped,
Thanks, Tom
The CDI aspects of your program require critical timing. We already placed one aspect of your program on a hardware timer (the trigger pulse) to work around the display. In my opinion it is more important to get the core functionality of your program working first. If you need to use millis() timers (i.e. software timers instead of hardware) don't tie your hands. It may be possible to work around the display as now written, but I don't have the hardware to help you do that.
There are other possible displays to use, or the entire 12864B display program can be re-crafted to draw a tiny bit of the display image and then return to the program.
I now have a serial monitor, silly me, it is built in. Will get a more suitable one soon. The pot I have for rpm, potValue, now does nothing and there is no output from pin 7 or 8. Values on monitor do not change, but ms jumps around a lot from low values to really high.
cattledog,
Success! I had a power supply problem and the board was not getting enough voltage. Now it all works, display on pc shows values and pot1 is consistent and linear. (before it was really flaky) Now to turn pulse delay into ° advance? I have 2 cdi that should have advance built in, they use ic MB4213 for delay but neither of them have any delay. Maybe they are bad and this is exactly why I need this tester!
Best, Tom
I'm glad that you are finding success with your project. In my testing I found that much of the variation in delay timing was coming from the continual analorRead dof the pots in loop. Because the changing of this pot is manual input I thought it would be OK to put the readings at one second intervals. There is far greater stability of the delay values. If the precision of the tester is not adequate with this modification, we can use a running average of several delay values to smooth it more.
I think that 360 * delay/puseValue *1000 should give you degrees of delay. I've calculated as an int, but you may want to use a float for fractional degrees.
//Pulse Generator Serial Output Arduino Code
int pot1 = A5; // select the input pin for the pot
int potValue = 0; // variable to store the value coming from the sensor
int pulseValue = 0; // 2 variables from one pot
int delayValue = 0;
int outputPin = 8; // select the pin for the output for trigger pulse
int chargePin = 7; // select the pin for the output for charge pulses
volatile boolean trigger = false;
volatile unsigned long delayPeriod;
unsigned long copy_delayPeriod;
volatile unsigned long delayPeriodStart;
int delayDegrees;
unsigned long analogReadInterval = 1000;//read pots and map
unsigned long lastAnalogRead;
void setup() {
Serial.begin(115200); // serial com speed for display
pinMode(outputPin, OUTPUT); // declare the outputPin as an OUTPUT
pinMode(chargePin, OUTPUT); // declare the chargePin as an OUTPUT
delay (2000);
pinMode(4, OUTPUT); //signal out for delay test
//Timer1 set up 1ms pulse every 50 ms
TCCR1A = 0;;
TCCR1B = (1 << WGM12);//CTC mode to OCR1A
OCR1A = 12500; //50 ms to top value
TIMSK1 |= (1 << OCIE1A);//interrupt enable compareA
TIMSK1 |= (1 << OCIE1B);//interrupt enable compareB
OCR1B = 250; //1 ms pulse width
TCCR1B |= (1 << CS11) | (1 << CS10); //prescaler 64 4us/tick
attachInterrupt(digitalPinToInterrupt(3), delayPeriodTiming, RISING);
}
void loop() {
if (millis() - lastAnalogRead >= analogReadInterval)
{
lastAnalogRead += analogReadInterval;
potValue = analogRead(pot1);
pulseValue = analogRead(pot1);
delayValue = analogRead(pot1);
potValue = map(potValue, 0, 1023, 500, 3000);
pulseValue = map(pulseValue, 0, 1023, 120, 20);
delayValue = map(delayValue, 0, 1023, 30000, 5000);
}
//my test for trigger pulse delay response)
if (trigger == true)
{
if (micros() - delayPeriodStart >= 5000) //5 ms delay
{
trigger = false;
digitalWrite(4, HIGH);//simulate pulse
digitalWrite(4, LOW);
noInterrupts();
copy_delayPeriod = delayPeriod;
interrupts();
delayDegrees = 360*(copy_delayPeriod)/(pulseValue*1000UL);
Serial.print("RPM =");
Serial.println(potValue);
Serial.print("Ms =");
Serial.println(pulseValue);
Serial.print("Delay Microseconds =");
Serial.println(copy_delayPeriod); // for use in future code
Serial.print("Delay Degrees = ");
Serial.println(delayDegrees);
}
}
}
ISR(TIMER1_COMPA_vect) {
OCR1A = (delayValue); // value to set delay between pulses
digitalWrite(outputPin, HIGH); //turn on pin 8
digitalWrite(chargePin, HIGH); //turn on pin 7
}
ISR(TIMER1_COMPB_vect)
{
digitalWrite(outputPin, LOW); //turn off pin 8
digitalWrite(chargePin, LOW); //turn off pin 7
delayPeriodStart = micros();//start looking for response
trigger = true;
}
void delayPeriodTiming()
{
delayPeriod = micros() - delayPeriodStart;
}