CDI tester project

Hi, I am working on formula to display degrees, don't really need it but I may learn something.
I am attaching the schematic, not finished but enough there to see what I have.

CDI_tester.pdf (22.8 KB)

Here is the bug in the program which required delay(2000) in setup.

int timerTopValue =  0;

When the first TIMER1_COMPA_vector interrupt was triggered, and OCR1A was set to timerTopValue, it was being set to 0 because this interrupt occurred before the first read of the pot to set the period. Because of this 0 value, the subsequent interrupts were occurring so fast that I think the millis() function was blocked from advancing and the code did not enter the section where the pot was read. I'm not quite sure why the delay in setup fixed this.

There are two solutions, simple one is to initialize the timerTopValue with the 12500 count used in the initial setup. You can also change the line in the timer set up to reflect the non zero initial assignment.

//int timerTopValue =  0;
int timerTopValue = 12500; //50 ms with 64 prescaler   // changed from timerTopValue = 0
 // OCR1A = 12500;  //50 ms to top value
  OCR1A = timerTopValue;

The other would be to call an analogRead() in set up and assign the mapped value to RPM and calculate an intitial timerTopValue at that time. With either of these changes, the sketch does not require the delay in set up.

cattledog,

Other issue solved as you said.

I think I have math that will give me the correct degree answer but I do not know how to write it in code.

delayDegrees = copy_delayPeriod / ((pulseValue / 360) / (1000));

This will not compile so is there another way to write it?

Where pulseValue must be divided by 360 first, and then that answer must be divided by 1000.
Then that answer must be divided into copy_delayPeriod

Tom

I think I have math that will give me the correct degree answer but I do not know how to write it in code.

delayDegrees = copy_delayPeriod / ((pulseValue / 360) / (1000));

I don't understand what was the problem with the existing code. What values is it giving you that appear to be incorrect? Can you give some numerical examples?

  delayDegrees = 360.0 * (copy_delayPeriod) / (timerTopValue * 4.0);  // for decimal place in deg display

timerTopValue*4 is the pulseValue (i.e. the repeating trigger period ~16-120 ms) expressed in microseconds instead of timer ticks. copy_delayPeriod is also in microseconds. The ratio of the delay to the total period gets converted to degrees by multiplying by 360.

Suppose the RPM = 500 and therefore the trigger period will be 120 ms = 120000us

If the delay is 3000us then 360*(3000/120000) = 9 degrees.

I may be confused, but when delay says 92, degree says 0.3 and when delay says 596, degree says 1.8.
Both taken at 500 rpm. With more delay, degree should be less. Unless we should not call that line Deg Advance, and should call it Degree retarded. I have no problem either way but what it says now, taken literally is not correct even though it may be right.

With more delay, degree should be less.

Not really. Think of a one armed clock with the arm sweeping around. If the trigger goes off when the hand is at 12 and the signal comes back when the hand is at 1, then the delay of the response is 1/12 = 30 degrees. If the pulse comes back when the hand is at 2 then the delay of the response is 2/12 = 60 degrees. If the pulse comes back when the hand is at 3 then the delay of the response is 3/12 = 90 degrees.

If you understand degrees retarded better than degrees delayed it seems like an ok term to use.

Hi, Yes you are right, but in the beginning I was, and still do think of it as advance. That is because I come from the world of engines and mechanical things as well as electronics. It has to do with the way "advance" is arrived at, using delay from a predetermined spot which is the most advanced that the spark will ever occur at high rpm. So I will think of it as degrees retarded, no problem, it makes it simple and then there is not the issue of calibrating for each different cdi.

With the test board, the degrees seem to make sense now, being approx what I expect, I need to install the switch for mode. I still don't really understand why with a cdi of no delay, the measurement of delay is from 96 to 136.

With test board set to 0 main shows 16 in the F mode, and with test set to 5100 main shows 5116.
With cdi of no delay, main shows 96 to 136, ideally this would be 0. Ideas? It is not important unless it is an easy fix, it may be that for some reason it really does take that long from pulse in to spark out.

Tom

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;
  }
}

cattledog,

Hi, I looked it over, will try it soon. One question came up yesterday when I tested a CDI that fires from falling edge. It gave long Us delay times like I had before when it was measuring from leading edge. So many CDIs do the same thing in function but with slightly different characteristics!

So I put this 100Us delay in and it fixed it, but any less than 100 did not correct it and more than 100 had no more effect. Just wanted to understand. With the 100Us delay, the Us Delay reads 8 or 12, mostly 12.

ISR(TIMER1_COMPB_vect) {
  digitalWrite(outputPin, LOW);                                          //turn off pin 8
  digitalWrite(chargePin, LOW);                                          //turn off pin 7

  if (risefall == 'F')                                                   //switch set for Falling edge of pulse for trigger
  {
    delayMicroseconds(100);                                             // delay for 4518 cdi
    delayPeriodStart = micros();                                        //start looking for response as pulse falls
    trigger = true;
  }
}

void delayPeriodTiming()
{
  delayPeriod = micros() - delayPeriodStart;
  interruptFlag = true;
}

One question came up yesterday when I tested a CDI that fires from falling edge. It gave long Us delay times like I had before when it was measuring from leading edge. So many CDIs do the same thing in function but with slightly different characteristics!

When we code for the trigger on the falling edge but the actual trigger is on the leading edge, we get long delay times equal to the fundamental repeating trigger period plus the actual delay. The code did not start looking for the return pulse until after it had arrived and we were catching the next one.

When we code for the trigger on the leading edge, but the actual trigger is on the falling edge, we get delay times equal to the trigger pulse length plus the actual delay. Trigger pulse length was last set at 125 Timer1 counts (each equal to 4 microseconds by prescaler 64) which is 500 microseconds.

When we code for the trigger edge which actually matches the device, we were reading the correct delay.

You are now reporting a new issue in that the code with the falling edge delay measurement start time and the module delay being falling edge triggered you see "large" numbers unless there is a 100 microsecond delay.

First, are you absolutely certain the CDI unit delay is falling edge triggered? How do you know?

If indeed this strangely behaving module were actually rising edge triggered, but measured with falling edge code, the added 100 microsecond delay could push the time at which the delayed pulse was sensed to back after the falling edge and it would measure a small delay.

I can't see any other way that added delay would turn a large measured delay into a small one unless it pushed the arrival of the delayed pulse echo to after the end of the trigger pulse.

What are the actual delay numbers you see, and at what RPM? That data last time was the key to figuring out what was going on.

Have you ever seen issues with the Rising edge triggered code? Have you ever seen issues with Falling edge triggered code and the two Arduino test setup?

Do you know how the modules sense the RPM? We have coded everything based on the premise that the module figures RPM by the time between similar edges and the delayed spark pulse is fired from that edge. But it's conceivable that there are other methods in use.

The delayMicroseconds(100) in the code can only affect the Arudino and its measurement as the trigger pulse was ended before the delay and the real delay is controlled by the module hardware. What does the scope tell us about the relationship between the trigger pulse and when the spark pulse is really being fired?

Adding the delayMicroseconds(100) into the ISR is not good practice and could have unintended consequences for other timing, so its best to figure this out.

cattledog,

Ok, I had time today to look into this and was very surprised by what I found. This was on a CDI that is DC powered meaning it has its own DC to high voltage AC inverter inside. When I looked at it on the scope (like I should have done at first) The signal from the coil primary had an incredible amount of ringing or oscillation after the pulse firing the coil. This was playing havoc with the optocoupler. I put a diode across the coil and completely eliminated this problem. I will have to keep remaining myself that we have fairly established that the software is working very well and problems that arise are outside that scope!!

I really do need to thank you for the help you have provided, I do car audio repair daily so if you ever have a need perhaps I can repay you.

I had a thought about what to use for a 'box' for this all to live in and thought of a dvd duplicator or external cd drive case since they usually have small power supplies with 3.3, 5.0, and 12 volt outputs, and have the available room of 2 or 3 5.25" drives. Is there something else I might consider?

Best, Tom

cattledog,

Initial response is there is nothing on pin 7. Does this require a feedback on pin 3 to start? If so that is a catch 22 since there is no high voltage for the CDI to make a spark.

edit: I see that I need to reset the Arduino after changing the setting. Now testing.

Initial response is there is nothing on pin 7. Does this require a feedback on pin 3 to start?

No charge pulses are independent of any signal on pin 3.

The default is for no charge pulse unless pin 6 (setChargePulsePin) is grounded. Do you have a 2 position switch on pin 6? If not, you can just run a wire from 6 to ground.

OK, but what happens is if the Arduino is running when I jump pin 6 to ground the charge pulses do not start until I reset. If I remove ground from pin 6 the charge pulses do not stop until I reset. It is not a big issue and I still need to work on hardware to turn those pulses into something usable.
Tom

Just discovered that the pulse out on pin 8 has changed. What used to be the on time is now the off time and off time is on time making very long pulses with .5Ms off.

Just discovered that the pulse out on pin 8 has changed. What used to be the on time is now the off time and off time is on time making very long pulses with .5Ms off.

There is nothing I see in the code I wrote which could do that. To confirm, I just went and tested the code posted in #146 with a frequency/duty cycle analysis program I use, and the trigger pulse on pin 8 was indeed the 500us (.5ms) HIGH expected with the low period length determined by the rpm.

Are you running the exact code I posted? If not please post the code you are running.

When you run the code without the pin 6 jumper to ground and the chargePulses are disabled, do you see the pin 8 problem?

Do a "find" on the word "outputPin" and see where it is turned on and off. Is there anything in your hardware which could invert the pulse?

Go back to the version you were last running before the chargePulse modifications. Do you see the pin 8 pulse timing change or is it correct? If indeed you have two versions of code, one of which is correct on/off and the other which is reversed on the same hardware, please post both versions.

Right now, I can't confirm your findings.

Got to take a quick look today, transistor for pulse out had failed and was passing the inverted signal from its driver. It is a 4403 and is rated for 600 ma continuous collector current so I don't know why it failed. Still expecting some Honda CDI with retard in soon so can have real time info on delay reading.
Tom

Hi, I have progressed to a new set of testing where it needs a simple form of a sine wave. A DAC will suffice but I need simple control from the Arduino to do this such as D1=1 D2=0 => zero volt,
D1=1 D2=1 => +12volt, D1=0 D2=0 => -12volt. So I need to use 2 output pins rather than 1. The width of the output should stay the same.

// last change 7/22/18
// 8/17 added code for pulse out width
// 7/14/2017 added correct math and setting for position of pickup
// 10 10/28/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 for rpm
int pot2 = A4; // select the input pin for the pot for pickup location in degrees
int pot3 = A5; // select the input pin for the pot for width of bar on flywheel in degrees
int potValue = 0; // variable to store the value coming from the sensor
int pickupValue = 0; // position of pickup in degrees
int potWidth = 0; // variable to set width of bar on flywheel
int timerTopValue = 12500; // changed from timerTopValue = 0
int outputPin = 4; // select the pin for the output for trigger pulse, changed from 8
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 to float for decimal place display
int RPM;
int pickup;
int barWidth;
int pulseWidth;                                            
volatile boolean interruptFlag;
unsigned long analogReadInterval = 250; //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 dc 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(12, 0);
  lcd.print("Mode:"); //to display code choice R or F
  lcd.setCursor(1, 1);
  lcd.print("Pos:");
  lcd.setCursor(9, 1);
  lcd.print("Width:");
  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; //sets trigger pulse width, 500 = 2Ms, 250 = 1Ms, 125 = .5Ms, set to 1250 for 5013 cdi ***************
  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
  // to prevent out of sync charge pulse, no prescaler set here

  attachInterrupt(digitalPinToInterrupt(3), delayPeriodTiming, FALLING);

}

void loop()
{
  if (millis() - lastAnalogRead >= analogReadInterval)
  {
    lastAnalogRead += analogReadInterval;
    potValue = analogRead(pot1); //rpm
    pickupValue = analogRead(pot2); // pickup position
    barWidth = analogRead(pot3);  
  
    //Timer2 OCR2A requires max value 255 => 615 lowest RPM
    if (chargePulse)
    {
      RPM = map(potValue, 0, 1023, 615, 10000);  //this section needs 2 pots added to set pickup and pulseWidth **************************
      pickup = 74; //map(pickupValue, 0, 1023, 0, 65); so advance will read based on delay,52 for yamaha 350 2 pu, 74 for 1 pu, 72 for tw200, 25 for 5013
      pulseWidth = (60000000/RPM)/360; //time for 1° in uS
      barWidth = 60; //barWidth = pulseWidth + 1000;  //10 for yamaha 350 2 pu, 60 for 1 pu, 60 for tw200
      //RPM = 615; //for my serial test purposes 615-3800
    }
    else
    {
      RPM = map(potValue, 0, 1023, 500, 3800);
      pickup = 25; //map(pickupValue, 0, 1023, 0, 50); //to set position of pickup so advance will read
      pulseWidth = map(potWidth, 0, 1023, 271, 44); //add Width to non charging case
     // RPM = 500;//for my serial test purposes 500-3800
    }

    timerTopValue = 15000000UL / RPM;
    OCR1B = pulseWidth;

    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(18, 0);
    lcd.print("  ");
    lcd.setCursor(18, 0);
    lcd.print(risefall); //print R or F at upper right side

    lcd.setCursor(6, 1);
    lcd.print("   ");
    lcd.setCursor(6, 1);
    lcd.print(pickup);

    lcd.setCursor(16, 1);
    lcd.print("   ");
    lcd.setCursor(16, 1);
    lcd.print(pulseWidth);

    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);   //delayDegrees, 1);
  }
 
  if (trigger == true && interruptFlag == true )
  {
    trigger = false;
    interruptFlag = false;
    noInterrupts();
    copy_delayPeriod = delayPeriod;
    interrupts();

    delayDegrees = pickup - (360.0 * (copy_delayPeriod) / (timerTopValue * 4.0)); //for decimal place in deg display.
    }
}

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  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);

  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;
  }
}

Hi Tom.

It's nice to hear that you are still in business with the CDI tester. I need to take a day to reacquaint myself with the project. While I'm doing that, can you explain a bit more about what you are doing with the AC output. Is this related to the ignition pulse, the charging pulse, or is a a new third output?

Any wiring diagrams and more information about what you are trying to do will be of help. How will you be working the DAC?

Are you needing to add two switches which will be read to determine the output? Like the mode switch and charging switch set a program path take?

Any wiring diagrams and more information about what you are trying to do will be of help.

I think to use 2 output pins on Arduino, call them D1 and D2. DAC will have 12+ and 12- supply voltage. This is only for the ignition pulse. The pulse width should change with rpm, I think I remember it does that now, and the time between the 2 pulses should change in relation to barWidth.

when all input pins of DAC get a 1 (+5v) the DAC output +12v (if powered by +12)
when all input pins of DAC get a 0 (gnd) the DAC output -12v (if powered by -12)
when pin5 of DAC get a 1 and others got a 0 the DAC output 0v

Then 2 pins of arduino are enough to get pos and neg square pulses and zero volt.
Say you connect D1 to pin 5 and D2 to pins 6,7,8,9,10,11,12 together

Thanks, Tom