Here is some code for the CDI modules which require the charge pulse. It's pretty will commented but here's a summary.
There is mode switch(pin6) for the charge pulses similar to the switch for trigger edge, but unlike the switch for the trigger edge, it is active in set up only and is not dynamic. Default is to not use the charge pulses. Let me know if that needs changing. I thought you would know which module you were testing.
The 5 charging pulses are turned on and off by an overflow interrupt from Timer2. I use Timer2 to time out the charge pulses, because when I used a micros() software timer (like the the one which controls the display frequency) I saw problems at the higher rpm due to blocking aspects of the code. The charge pulses from Timer 2 are gated by the trigger pulse from Timer1. One issue which came up is that because of the 8 bit limit of Timer2 I could not cover the complete range of charge pulse intervals from 500 to 3800 rpm with one prescaler setting. I chose to limit the lowest rpm in the charging mode to 615 instead of 500. If that is a major problem, let me know and I will try to work out a switch between two prescaler settings for Timer2.
I don't have a scope, and tested as best I could with Serial output, but you will need to check on both the long term timing (synchronization) of the of the charge pulses with the trigger, and the transient behavior on start up with the very first trigger pulses. Beyond the control of timer2 by timer1, I added some additional safety by always writing the charge pulse pin low at the spark trigger, independent of the timer 2 period control.
Take a look at this and get back to me with any questions.
// revision 9 10/24/2016 adds charging pulse with Timer 2
// lowest RPM = 615 with charge pulse to keep OCR2A <= 255
// CDI Tester Pulse Generator Serial Output Arduino Code
int pot1 = A3; // select the input pin for the pot (changed for this version because of lcd)
int potValue = 0; // variable to store the value coming from the sensor
int pulseValue = 0; // 2 variables from one pot
int timerTopValue = 12500;// changed from timerTopValue = 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;
float delayDegrees; // changed from int for decimal place display
int RPM;
volatile boolean interruptFlag;
unsigned long analogReadInterval = 1000; //read pots and map
unsigned long lastAnalogRead;
const byte setFallingSwitch = 5;
char risefall = 'R'; //default rising mode
const byte setChargePulseSwitch = 6;
boolean chargePulse = false ; //default powered mode
volatile byte timeSliceCount = 0; //TDC 0 degrees
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// set the LCD address to 0x3f for a 20 chars 4 line display
// Set the pins on the I2C chip used for LCD connections:
// addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address
void setup() {
//Serial.begin(115200);
//Serial.println("starting...");
lcd.begin(20, 4); // initialize the lcd for 20 chars 4 lines, turn on backlight
lcd.setCursor(1, 0); // lcd display setup of unchanging headings
lcd.print("RPM:"); // print fixed characters
lcd.setCursor(11, 0);
lcd.print("Mode:"); //to display code choice R or F
lcd.setCursor(1, 1);
lcd.print("Ms:");
lcd.setCursor(1, 2);
lcd.print("Us Delay:");
lcd.setCursor(1, 3);
lcd.print("Deg Advance:");
pinMode(outputPin, OUTPUT); // declare the outputPin as an OUTPUT
pinMode(chargePin, OUTPUT); // declare the chargePin as an OUTPUT
pinMode (setFallingSwitch, INPUT_PULLUP);//check for a LOW input to indicate switch is closed to ground
pinMode(setChargePulseSwitch, INPUT_PULLUP);
if (digitalRead(setChargePulseSwitch) == LOW)
chargePulse = true; //AC CDI
//Timer1 default set up .5ms trigger pulse every 50 ms
TCCR1A = 0;
TCCR1B = (1 << WGM12);//CTC mode to OCR1A
OCR1A = timerTopValue;
TIMSK1 |= (1 << OCIE1A);//interrupt enable compareA
TIMSK1 |= (1 << OCIE1B);//interrupt enable compareB
OCR1B = 125; // .5ms trigger pulse width //sets pulse out width, 500 = 2Ms, 250 = 1Ms, 125 = .5Ms
TCCR1B |= (1 << CS11) | (1 << CS10); //prescaler 64 4us/tick
//Timer2 default setup charge pulse 12 periods 30 degrees each; only 5 get turned on
//charge pulse timing interval = Timer1 trigger period/12 = timerTopValue/96
TCCR2A = 0;
TCCR2B = 0;
TCCR2A = 1 << WGM20; //lsb of mode 7 pwm to OCR2A
TCCR2B = 1 << WGM22; //msb of mode 7 pwm to OCR2A;
OCR2A = timerTopValue / 96; //96 = 12*4*2 (#periods, prescaler difference, up/down timer mode )
TIMSK2 = 0;
TIMSK2 = 1 << TOIE2; //enable overflow interrupt
// actually start timer in ISR(TIMER1_COMPA_vect
//prevent out of sync charge pulse, no prescaler set here
attachInterrupt(digitalPinToInterrupt(3), delayPeriodTiming, RISING);
}
void loop()
{
if (millis() - lastAnalogRead >= analogReadInterval)
{
lastAnalogRead += analogReadInterval;
potValue = analogRead(pot1);
pulseValue = analogRead(pot1);
//Timer2 OCR2A requires max value 255 => 615 lowest RPM
if (chargePulse)
{
RPM = map(potValue, 0, 1023, 615, 3800);
pulseValue = map(pulseValue, 0, 1023, 97, 16);// to show ms between pulse out on lcd
//RPM = 615;//for my serial test purposes 615-3800
}
else
{
RPM = map(potValue, 0, 1023, 500, 3800);
pulseValue = map(pulseValue, 0, 1023, 120, 16);// to show ms between pulse out on lcd
// RPM = 500;//for my serial test purposes 500-3800
}
timerTopValue = 15000000UL / RPM;
if (digitalRead(setFallingSwitch) == LOW) //set Falling
{
risefall = 'F';
}
else
{
risefall = 'R';
}
lcd.setCursor(6, 0);
lcd.print(" "); // print blank spaces to clear old data
lcd.setCursor(6, 0);
lcd.print(RPM);
lcd.setCursor(17, 0);
lcd.print(" ");
lcd.setCursor(17, 0);
lcd.print(risefall); //print R or F at upper right side
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(pulseValue);
lcd.setCursor(11, 2);
lcd.print(" ");
lcd.setCursor(11, 2);
lcd.print(copy_delayPeriod);
lcd.setCursor(14, 3);
lcd.print(" ");
lcd.setCursor(14, 3);
lcd.print(delayDegrees, 1);
}
if (trigger == true && interruptFlag == true )
{
trigger = false;
interruptFlag = false;
noInterrupts();
copy_delayPeriod = delayPeriod;
interrupts();
delayDegrees = 360.0 * (copy_delayPeriod) / (timerTopValue * 4.0); // for decimal place in degree display //value for display to 3 places in us
}
}
ISR(TIMER1_COMPA_vect) {
OCR1A = (timerTopValue);// value to set delay between pulses to trigger cdi
digitalWrite(chargePin, LOW); //guarantee off charge pin at trigger
digitalWrite(outputPin, HIGH); //turn on pin 8 trigger
if (risefall == 'R') //switch not set; default Rising trigger
{
delayPeriodStart = micros(); //start looking for response as pulse rises
trigger = true;
}
//start Timer 2 for charge pulses
if (chargePulse)
{
timeSliceCount = 0;
TCNT2 = 0;
OCR2A = timerTopValue/96; //set 12 periods
TCCR2B |= 1 << CS22 | 1 << CS21; //prescaleer 256 16us/tick
}
}
ISR(TIMER1_COMPB_vect) {
digitalWrite(outputPin, LOW); //turn off pin 8
if (risefall == 'F') //switch set for Falling trigger
{
delayPeriodStart = micros(); //start looking for response as pulse falls
trigger = true;
}
}
void delayPeriodTiming()
{
delayPeriod = micros() - delayPeriodStart;
interruptFlag = true;
}
ISR(TIMER2_OVF_vect)
//5 pulses of 30 degrees starting at 60 degrees
//ON at 60,120,180,240,300 = 2,4,6,8,10
//OFF at 90,150,210,270,330 = 3,5,7,9,11
{
if (timeSliceCount != 0 && timeSliceCount % 2 == 0)
{
digitalWrite (chargePin, HIGH);
}
else //if (timeSliceCount == 0 || timeSliceCount % 2 == 1)
{
digitalWrite(chargePin, LOW);
}
timeSliceCount++;
if (timeSliceCount == 12)
{
timeSliceCount = 0;
//stop Timer2 by clearing prescaler bits
TCCR2B &= ~1<< CS22;
TCCR2B &= ~1<< CS21;
}
}