CDI tester project

OK I have changed the comments a little to help me, so I hope you also think it is good description.
Also to consider, I leave the Rise / Fall switch at Rise all the time so it could be done just in code, no need for a switch anymore.
The other thing is that there should be a switch to choose Pos first or Neg first for this trigger pulse.

//You can use pots to set RPM, pulseWidthDegrees, and barWidth
unsigned int RPM = 1200;
//unsigned int RPM = 400;
unsigned int pulseWidthDegrees = 10;
unsigned int barWidthDegrees = 60;
unsigned long pulseWidthTime;
unsigned long timerTopValue;//sets 360 degree cycleTime
unsigned int timerTicksPerDegree;
const byte pinA = 4; //to DAC pin 6 - 12, called 6
const byte pinB = 5; //to DAC pin 5 

void setup() {
  pinMode(13, OUTPUT); //telltale led
  pinMode(pinA, OUTPUT);
  pinMode(pinB, OUTPUT);
  //initialize timer
  TCCR1A = 0;
  TCCR1B = 0;
  //set Timer1 mode Fast PWM to ICR1
  TCCR1B |= (1 << WGM13) | (1 << WGM12);
  TCCR1A |= (1 << WGM11);
 
  timerTopValue = 15000000UL / RPM;
  //15000000 timerTicksperMinute at 4 us per tick with prescaler 64
  //timerTopValue = 12500 at 1200 RPM 50ms per revolution
  timerTicksPerDegree = timerTopValue/360;
  ICR1 = timerTopValue;//initial placeholder
  pulseWidthTime = pulseWidthDegrees*timerTicksPerDegree;
  OCR1B = pulseWidthTime;

  //start Timer apply prescaler
  //TCCR1B |= (1 << CS12) | (1 << CS10); //1024 for led test
  TCCR1B |= (1<<CS11) | (1<<CS10);//64 for .5 us tick

  //enable B OutputCompare and Overflow interrupts
  TIMSK1 |= (1 << OCIE1B) | (1 << TOIE1);
}

void loop() {
}

ISR(TIMER1_COMPB_vect) {
  digitalWrite(13, LOW);    //turn off pulse for 0 volt
  digitalWrite(pinA, LOW);  //DAC 6 LOW
  digitalWrite(pinB, HIGH); //DAC 5 HIGH
}

ISR(TIMER1_OVF_vect) {
  //alternate ICR1 values to generate two outputs
  //360 degree cycle time broken into two pieces
  static byte count = 0;
  if (count == 0)
  {
    ICR1 = timerTicksPerDegree*(pulseWidthDegrees+barWidthDegrees); //first pulse and bar width
    digitalWrite(13, HIGH);    //turn on pulse for +12v
    digitalWrite(pinA, HIGH);  //DAC 6 HIGH
    digitalWrite(pinB, HIGH);  //DAC 5 HIGH
  }
  if (count == 1)
  {
   ICR1 = timerTicksPerDegree*(360-(pulseWidthDegrees+barWidthDegrees));//second pulse and dead band
    digitalWrite(13, HIGH);   //turn on pulse for -12v
    digitalWrite(pinA, LOW);  //DAC 6 LOW
    digitalWrite(pinB, LOW);  //DAC 5 LOW
  }
  count++;
  if (count == 2)
    count = 0;
}

OK I have changed the comments a little to help me, so I hope you also think it is good description.

Looks good to me.

The other thing is that there should be a switch to choose Pos first or Neg first for this trigger pulse.

I patched in the code for the first pulse polarity. You'll need to set up a toggle switch for this. I think you can use the R/F switch but in the current code it is on pin5 which is becoming an output.

//You can use pots to set RPM, pulseWidthDegrees, and barWidth
unsigned int RPM = 1200;
//unsigned int RPM = 400;
unsigned int pulseWidthDegrees = 10;
unsigned int barWidthDegrees = 60;
unsigned long pulseWidthTime;
unsigned long timerTopValue;//sets 360 degree cycleTime
unsigned int timerTicksPerDegree;
const byte pinA = 4; //to DAC pin 6 - 12, called 6
const byte pinB = 5; //to DAC pin 5
//set first pulse state with toggle switch like R/F or charging switch
//const byte setfirstPulseSwitch = ??;
//boolean firstPulsePos = true;
boolean firstPulsePos = false;

void setup() {
  pinMode(13, OUTPUT); //telltale led
  pinMode(pinA, OUTPUT);
  pinMode(pinB, OUTPUT);
  /*
    pinMode(setFirstPulseSwitch, INPUT_PULLUP);
    if (digitalRead(setFirstPulseSwitch) == HIGH)
     firstPulsePos = true;
    else
     firstPulsePos = false; //first pulse negative
  */

  //initialize timer
  TCCR1A = 0;
  TCCR1B = 0;
  //set Timer1 mode Fast PWM to ICR1
  TCCR1B |= (1 << WGM13) | (1 << WGM12);
  TCCR1A |= (1 << WGM11);

  timerTopValue = 15000000UL / RPM;
  //15000000 timerTicksperMinute at 4 us per tick with prescaler 64
  //timerTopValue = 12500 at 1200 RPM 50ms per revolution
  timerTicksPerDegree = timerTopValue / 360;
  ICR1 = timerTopValue;//initial placeholder
  pulseWidthTime = pulseWidthDegrees * timerTicksPerDegree;
  OCR1B = pulseWidthTime;

  //start Timer apply prescaler
  //TCCR1B |= (1 << CS12) | (1 << CS10); //1024 for led test 8us tick
  TCCR1B |= (1<<CS11) | (1<<CS10);//64 for .5 us tick

  //enable B OutputCompare and Overflow interrupts
  TIMSK1 |= (1 << OCIE1B) | (1 << TOIE1);
}

void loop() {
}

ISR(TIMER1_COMPB_vect) {
  digitalWrite(13, LOW);    //turn off pulse for 0 volt
  digitalWrite(pinA, LOW);  //DAC 6 LOW
  digitalWrite(pinB, HIGH); //DAC 5 HIGH
}

ISR(TIMER1_OVF_vect) {
  //alternate ICR1 values to generate two outputs
  //360 degree cycle time broken into two pieces
  static byte count = 0;
  if (count == 0) //first pulse
  {
    ICR1 = timerTicksPerDegree * (pulseWidthDegrees + barWidthDegrees); //first pulse and bar width
    digitalWrite(13, HIGH);
    if (firstPulsePos == true)
      setPulsePos();
    else
      setPulseNeg();//first pulse neg if firstPulsePos == false
  }
  
  if (count == 1)//second pulse
  {
    ICR1 = timerTicksPerDegree * (360 - (pulseWidthDegrees + barWidthDegrees)); //second pulse and dead band
    digitalWrite(13, HIGH);
    if (firstPulsePos)
      setPulseNeg();
    else
      setPulsePos();//first pulse was negative
  }
  count++;
  if (count == 2)
    count = 0;
}

void setPulsePos()
{
  digitalWrite(pinA, HIGH);  //DAC 6 HIGH
  digitalWrite(pinB, HIGH);  //DAC 5 HIGH
}

void setPulseNeg()
{
  digitalWrite(pinA, LOW );  //DAC 6 LOW
  digitalWrite(pinB, LOW);  //DAC 5 LOW
}

Hi, OK it works changing the polarity. I have assigned different pins, here is my choice,
D3 - timing signal from clamp on probe.
D4 - polarity switch
D5 - rise / fall
D6 - controls AC - DC output
D7 - output for charge pulses
D8 - to DAC 5
D9 - to DAC 6 - 12

This is the schematic of the whole tester.
Thanks, Tom

CDI_tester.pdf (32.6 KB)

OK.

D4 - polarity switch
D5 - rise / fall
D6 - controls AC - DC output

Can you explain more to me about the three switches. I understand the polarity switch which will control which comes first +12 or -12.

Is D6 to use the current unipolar output or the new bipolar mode? Previously you said that the bipolar output would replace the current mode. If we are keeping it, what pin is the output on?
EDIT: Is pin6 the switch which currently sets the charge pulses active?

Do you still want to keep the rise/fall return timing start option? In the bipolar mode, Do you know which of the two pulses(+12v, -12v), and which edge do you want to start timing from?

Hi,
OK, here is a more detailed view. D7 is the charge pulse out, D6 controls if it is active or not. Some CDI use DC to power them so when it is not active, 12v DC is output to the power connector J1/J2. Some CDI are AC powered so when it is active it makes the AC power with Q3, Q4 and TR3. So D6 can stay the way it is now.

Can we keep the rise fall switch as is until I confirm how the DAC is going to act? I think I need it to be set at rise but time will tell.

Is all this OK or are we near the capacity of the Arduino?
Best, Tom

Thanks for the clarifications.

Can we keep the rise fall switch as is until I confirm how the DAC is going to act? I think I need it to be set at rise but time will tell.

I think that given how the existing system is working with the negative pulse on the collapse of the first pulse field, it makes sense to keep the timing from the first pulse. When the first pulse is positive going, the rise/fall to begin on either the lead edge or trail edge of the pulse makes sense.

What do we want to do when the first pulse is negative going? Unless you think differently, I will leave R to mean the lead edge, and F to mean the trailing edge of the lead pulse, whatever the direction, + or -.

I will start integrating the the new pulse and switches into the existing code. If you need anything more, (beyond what we have which demonstrates the pulses) to use as simple test code when developing the hardware, let me know.

Is all this OK or are we near the capacity of the Arduino?

We are not close at all. The additions are not very code intensive, and this is from the existing code you posted.

Sketch uses 6860 bytes (21%) of program storage space. Maximum is 32256 bytes.
Global variables use 329 bytes (16%) of dynamic memory, leaving 1719 bytes for local variables. Maximum is 2048 bytes.

OK, what I am not sure of is how the pulse timing will work which is why I thought finally to leave rise/fall as is, well sort of anyway. Before, it only makes the one output and the transformer makes the pos and neg, with the DAC I think it still needs to time from the first edge of the output. But that output will be different than before. Hope you understand, I think you have it sorted!
Best, Tom

Hi Tom--

Here's what I have so far. I hope it is enough to use to get your hardware set up.
I've put some of the code into functions, used the new pin numbers, coded for the toggled switch presets. I have not coded anything with the charge pulses, Timer2, and how the charge pulses coordinate with the bipolar pulses.

I am using a much improved library for the i2c lcd. It's called hd44780 and is available through the library manager for download. It is auto configuring for the i2c address and the configuration of the driver pins. It is faster, and lets you change out display without having to figure out if it's any different from the previous one. Use these magic words at the head of the sketch.

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h> // include i/o class header
hd44780_I2Cexp lcd; // declare lcd object: auto locate & config display for hd44780 chip

I've used a 16x2 with lcd.begin(16,2) in setup and you will need lcd.begin(20,4), and can adjust the the display code for what you want to show.

As before, I'm uncertain about how you want to present the time delay from the trigger pulse to the delayed response from the module. Delay/ Advance? :confused:

Let me know if this appears to be headed in the right direction.

//V1.0 set RPM potentiometer/configuration swtiches/hd44780 lcd/revised pin numbers

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h> // include i/o class header
hd44780_I2Cexp lcd; // declare lcd object: auto locate & config display for hd44780 chip

//utility definitions
#define positive HIGH
#define negative LOW
#define firstPulse 0
#define secondPulse 1

//You can use pots to set RPM, pulseWidthDegrees, and barWidth
unsigned int RPM = 1200;//adjusted with potementiometer
unsigned int pulseWidthDegrees = 10;
unsigned int barWidthDegrees = 60;
int pickupPositionOffset = 0; //timing offset from TDC

unsigned long pulseWidthTime;//degrees converted to time
unsigned long timerTopValue;//sets 360 degree cycleTime
unsigned int timerTicksPerDegree;

//output pins
const byte chargePin = 7; //charge pulses
const byte pinB = 8; //to DAC pin 5
const byte pinA = 9; //to DAC pin 6 - 12, called 6

//configuration switch setup
const byte polaritySwitch = 4;
boolean firstPulsePos = positive; //first pulse polarity
const byte setFallingSwitch = 5;
char risefall = 'R'; //default rising mode start timing from rising edge
const byte setChargePulseSwitch = 6;
boolean chargePulse = false ;  //AC CDI default is DC

const byte pot1 = A3; //input pin for pot which sets rpm

//return pulse interrupt variables
volatile boolean trigger = false;
volatile boolean interruptFlag;
volatile unsigned long delayPeriod;
unsigned long copy_delayPeriod;
volatile unsigned long delayPeriodStart;
float delayDegrees; // changed from int to float for decimal place display

void setup()
{
  Serial.begin(115200);
  //set outputs for 0v
  pinMode(13, OUTPUT); //telltale led
  digitalWrite(13, LOW);
  pinMode(pinA, OUTPUT);
  digitalWrite(pinA, LOW);  //DAC 6 LOW
  pinMode(pinB, OUTPUT);
  digitalWrite(pinB, HIGH); //DAC 5 HIGH
  pinMode(3, INPUT_PULLUP); //interrupt pin, return pulse drives low
  pinMode(chargePin, OUTPUT); // declare the chargePin as an OUTPUT

  // initialize LCD with number of columns and rows:
  lcd.begin(16, 2);
  lcd.setCursor(0, 0); // lcd display setup of unchanging headings
  lcd.print("RPM:"); // print fixed characters
  lcd.setCursor(9, 0);
  lcd.print("Mode:");
  lcd.setCursor(0, 1);
  lcd.print("1stPulse:"); //first pulse + or -
  lcd.setCursor(11, 1);
  lcd.print("CP:"); //charge pulse AC CDI Y or N

  setupTimer1();
  setupSwitchPresets();

  //15000000 timerTicksperMinute at 4 us per tick with prescaler 64
  //timerTopValue = 12500 at 1200 RPM 50ms per revolution
  timerTopValue = 15000000UL / RPM;
  timerTicksPerDegree = timerTopValue / 360;
  pulseWidthTime = pulseWidthDegrees * timerTicksPerDegree;
  TCNT1 = 0;
  OCR1B = pulseWidthTime;//timer ticks
  attachInterrupt(digitalPinToInterrupt(3), delayPeriodTiming, FALLING);
  delay(5000);//display setup parameters
  lcd.setCursor(0, 1);
  lcd.print("DelayDeg:");
}

void loop()
{
  const unsigned long analogReadInterval = 250;//250; //read pots and map
  static unsigned long lastAnalogRead = 0;
  if (millis() - lastAnalogRead >= analogReadInterval)
  {
    //add other potentiometer adjustments here
    lastAnalogRead += analogReadInterval;
    RPM = map(analogRead(pot1), 0, 1023, 500, 4000);
    //adjust timer values for RPM
    timerTopValue = 15000000UL / RPM;
    timerTicksPerDegree = timerTopValue / 360;
    pulseWidthTime = pulseWidthDegrees * timerTicksPerDegree;
    OCR1B = pulseWidthTime;

    lcd.setCursor(4, 0);
    lcd.print("           "); // print blank spaces to clear old data
    lcd.setCursor(5, 0);
    lcd.print(RPM);
    lcd.setCursor(9, 1);
    lcd.print("       ");
    lcd.setCursor(10, 1);
    lcd.print(delayDegrees);
  }

  if (trigger == true && interruptFlag == true )
  {
    trigger = false;
    interruptFlag = false;
    noInterrupts();
    copy_delayPeriod = delayPeriod;//microseconds
    interrupts();
    //Serial.print(copy_delayPeriod);
    //Serial.print('\t');
    //divide by 4 to convert microseconds to timer ticks
    delayDegrees = (copy_delayPeriod / 4) / timerTicksPerDegree;
    //Serial.println(delayDegrees);
    //delayDegrees = pickup - (360.0 * (copy_delayPeriod) / (timerTopValue * 4.0)); //for decimal place in deg display.
  }
}

void setupTimer1()
{
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  //set Timer1 mode Fast PWM to ICR1
  TCCR1B |= (1 << WGM13) | (1 << WGM12);
  TCCR1A |= (1 << WGM11);
  //start Timer apply prescaler
  // TCCR1B |= (1 << CS12) | (1 << CS10); //1024 for led test 8us tick
  TCCR1B |= (1 << CS11) | (1 << CS10); //64 for .5 us tick
  //enable B OutputCompare and Overflow interrupts
  TIMSK1 |= (1 << OCIE1B) | (1 << TOIE1);
}

ISR(TIMER1_OVF_vect)
{
  //alternate ICR1 values to generate two outputs over 360 degrees
  //360 degree cycle time broken into two pieces
  //timerTopValue adjusted with RPM pot
  //timerTicksPerDegree = timerTopValue / 360; //gets new value to update ICR1
  static byte pulse = firstPulse;
  if (pulse == firstPulse)
  {
    ICR1 = timerTicksPerDegree * (pulseWidthDegrees + barWidthDegrees); //first pulse and bar width
    digitalWrite(13, HIGH);
    if (firstPulsePos == true)
      setPulse(positive);
    else
      setPulse(negative);
    //set timing start at lead edge of first pulse
    if (risefall == 'R')
    {
      delayPeriodStart = micros();                                          //start looking for response as pulse rises
      trigger = true;
    }
    else //risefall == 'F' set timing at trailing edge of first pulse
    {
      //convert pulseWidthTime in timer ticks to microseconds
      //prescaler 8 = 4us/timer tick
      delayPeriodStart = micros() + pulseWidthTime * 4;                                        //start looking for response as pulse rises
      trigger = true;
    }
    pulse = secondPulse; //next pulse
  }
  else //second pulse
  {
    ICR1 = timerTicksPerDegree * (360 - (pulseWidthDegrees + barWidthDegrees)); //second pulse and dead band
    digitalWrite(13, HIGH);
    if (firstPulsePos)
      setPulse(negative);
    else
      setPulse(positive);
    pulse = firstPulse; //next pulse
  }
}

ISR(TIMER1_COMPB_vect) {
  digitalWrite(13, LOW);    //turn off pulse for 0 volt
  digitalWrite(pinA, LOW);  //DAC 6 LOW
  digitalWrite(pinB, HIGH); //DAC 5 HIGH
}

void setPulse(byte state)    //state negative = LOW state positive = HIGH
{
  digitalWrite(pinA, state );  //DAC 6 state
  digitalWrite(pinB, state);  //DAC 5 state
}

void setupSwitchPresets()
{
  pinMode(setChargePulseSwitch, INPUT_PULLUP);
  if (digitalRead(setChargePulseSwitch) == LOW)
    chargePulse = true; //AC CDI
  else
    chargePulse = false;
  lcd.setCursor(14, 1);
  if (chargePulse)
    lcd.print("Y");
  else
    lcd.print("N");

  pinMode(polaritySwitch, INPUT_PULLUP);
  if (digitalRead(polaritySwitch) == HIGH)
    firstPulsePos = true; //first pulse positive
  else
    firstPulsePos = false; //first pulse negative
  lcd.setCursor(9, 1);
  if (firstPulsePos)
    lcd.print("+");
  else
    lcd.print("-");

  pinMode (setFallingSwitch, INPUT_PULLUP); //check for a LOW input to indicate switch is closed to ground
  if (digitalRead(setFallingSwitch) == LOW) //set Falling
  {
    risefall = 'F';
  }
  else
  {
    risefall = 'R';
  }
  lcd.setCursor(14, 0);
  lcd.print(risefall);
}

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

Can you explain to me why you have the difference in the rpm mapping in this section of your code when charge pulses are active?

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
    }

Hi and thanks!

When you say 'As before, I'm uncertain about how you want to present the time delay from the trigger pulse to the delayed response from the module. Delay/ Advance? ' I am not completely sure what you are asking.

When you ask 'Can you explain to me why you have the difference in the rpm mapping in this section of your code when charge pulses are active?' The if section is for charge pulse output mode and the else section is when it is off and just DC out to CDI.

I tell you, it has been so long I have forgot some of this!
Tom

I tell you, it has been so long I have forgot some of this!

Me too! Start experimenting with the code I provided, and maybe your questions will get us both back to speed.

OK I will start this weekend if I can get caught up!! I also make power steering kits for military trucks, and just got a big order.
Tom

Hi, also had a question about the pots. This is way ahead of itself but I saw this yesterday. The one for rpm can easily be a pot as it is not critical and I just sweep it the range I want. The bar width and length are a small range and should stay set at an exact number. You may remember I inquired about using an encoder in the past and now I see a 4131-103 digital pot. I wonder if it would be an easy way to do what I want or is there another way to input the numbers for those settings. To be honest I have just been writing to the code each time I need to change it!!
Tom

The digital pot does not seem much of an advantage over an analog pot(perhaps a 10 turn) with ranges to give values for +/- 1 from a known starting point. The digital pots may use more pins than an analog one.

For a small range, a rotary encoder is not a bad solution for user input, but again they use more pins than the analog pot.

User input for any program is always a tricky issue. Simple Bluetooth2.0 and a phone might be another solution.

To be honest I have just been writing to the code each time I need to change it!!

If this is not a significant problem, then its probably best to continue making adjustments this way until we get farther down the road with the new operating pulses.

Oh yes, I agree, it was just an academic question. I ordered a breadboard, have never used one! In the end I will have a printed circuit board made for the final version.
Thanks, Tom

Hello, OK I have installed the library for LCD and it works bot does not display any values, not even rpm which I see is fixed for now. What functions should I test / work on first?
Tom

Hello, OK I have installed the library for LCD and it works bot does not display any values, not even rpm which I see is fixed for now. What functions should I test / work on first?

I assume you are running the code posted in #184. Are you using a 20x4 lcd or 16x2?

Do you see the screen from setup which shows RPM with no number following and then the values of the three toggle switch settings, which if there are no switches will default to the Mode:R 1stPulse + and CP N?

The RPM is not fixed, but is set by a pot on A3

RPM = map(analogRead(pot1), 0, 1023, 500, 4000);

If you tie A3 to 3.3v you should see RPM near 2800.

I would work first to get a pot on A3, or otherwise, change the line with the RPM mapping to a fixed RPM.

The second line with DelayDegree should show 0.00 until there is a return pulse on the interrupt pin 3.

Yes, 184's code with 20 x 4 lcd.
I will explore the settings and display in morning.
Thanks, Tom

I've got a 20x4 on order, and should be able to work in your format when it arrives.

Hi, on lines 61 to 69 and then 102 to 109 there is lcd info that seems to conflict in a way I don't understand.
Tom