CDI tester project

Nice progress getting the code back to the 20x4 display.

Pins 6, 5, 4 do not update, require a reboot, how to have them reset parameters when they are changed?

Add the function setupSwitchPresets() into this section of loop(). The pinMode() calls in the function should be moved into setup().

if (millis() - lastAnalogRead >= analogReadInterval)
  {
      //all the pot/switch reading to change settings and display them code
   }

No charge pulses on pin 7.

Correct. I am working on the timer2 and charge pulse code now. With the dual pulses I think there may be some changes about how the charge pulses are coordinated and synced to the trigger pulse.

LCD would not work until I moved the code from line 248 to 129 and from 259 to 123

This does not make sense to me, but when I get the 20x4 on line I can revisit this. It does not make any difference it you are going to change the toggle switch settings while the code is running.

Also I prefer the Degrees Advanced, I had a calculation in the last code that did that quite well.

First, declare a new global variable called degreesAdvance and call it when/where you want to display it on the lcd.

Then add a new line to the code after the line of code which calculates the delayDegrees from the interrupt timing of the delayPeriod.

delayDegrees = (copy_delayPeriod / 4) / timerTicksPerDegree;
degreesAdvance = pickup - delayDegrees;

This will be the degrees before TDC of the return pulse.

When do you think you will have the hardware set up to test the dual pulses? Can you do that without the charge pulses?

'Add the function setupSwitchPresets() into this section of loop(). The pinMode() calls in the function should be moved into setup().

if (millis() - lastAnalogRead >= analogReadInterval)
  {
      //all the pot/switch reading to change settings and display them code
   }


This I do not really understand…??

OK re the charge pulses, actually now it may be good to have twice as many pulses for the charge output to drive the transformer that makes the AC.

‘This does not make sense to me, but when I get the 20x4 on line I can revisit this. It does not make any difference it you are going to change the toggle switch settings while the code is running.’
The way it was the Arduino would not even start until I commented out those lines.

'First, declare a new global variable called degreesAdvance and call it when/where you want to display it on the lcd.
Then add a new line to the code

delayDegrees = (copy_delayPeriod / 4) / timerTicksPerDegree;
degreesAdvance = pickup - delayDegrees;

This will be the degrees before TDC of the return pulse.’

OK I will work on this after I get the hardware set up.

‘When do you think you will have the hardware set up to test the dual pulses? Can you do that without the charge pulses?’

Yes I can, but the pulses look correct on the scope so I have little doubt it will work correctly when wired up. Bread board should be here tomorrow.
Tom

[/quote]

'Add the function setupSwitchPresets() into this section of loop(). The pinMode() calls in the function should be moved into setup().

This I do not really understand…??

//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

int 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


int RPM;
int pickup;
int barWidth;
int pulseWidth;


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
  //add toggle switch pinMode
  pinMode(setFallingSwitch, INPUT_PULLUP);
  pinMode(polaritySwitch, INPUT_PULLUP);
  pinMode(setChargePulseSwitch, INPUT_PULLUP);

  // initialize LCD with number of columns and rows:
  lcd.begin(20, 4); // initialize the lcd for 20 chars 4 lines, turn on backlight
  lcd.setCursor(0, 0); // lcd display setup of unchanging headings
  lcd.print("RPM:"); // print fixed characters
  lcd.setCursor(9, 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:");

  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;

    setupSwitchPresets();

    lcd.setCursor(4, 0);
    lcd.print("     "); // print blank spaces to clear old data
    lcd.setCursor(4, 0);
    lcd.print(RPM);

    lcd.setCursor(14, 0);
    lcd.print("  ");
    lcd.setCursor(14, 0);
    lcd.print(risefall); //print R or F at upper right side

    lcd.setCursor(16, 0);
    if (firstPulsePos)
      lcd.print("+");
    else
      lcd.print("-");

    lcd.setCursor(18, 0);
    if (chargePulse)
      lcd.print("AC");
    else
      lcd.print("DC");

    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;//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(19, 0);   //moved to line 129
  //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(17, 0);   // moved to line 123
  //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;
}

OK re the charge pulses, actually now it may be good to have twice as many pulses for the charge output to drive the transformer that makes the AC.

In the existing program, there are 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[/quote]

Do you want say 10 pulses of 15 degrees over the same 240 degrees? Do you want something different than equal duty cycle on/off to cover more of the 360 with pulses. Can we be closer to TDC with the pulses. Now, there is 120 degrees around 0 with no pulses?

Hi, No, never mind me, I was thinking wrong. Pulses are fine as is. Tom

Hi, OK, all tested, all working. Later today I may get to do final test of pulses for DAC just to confirm that they match rpm but I assume they do/will. Tom

Here is the code attached with the charge pulses back in. I have cleaned up some variables and nomenclature. The current code only displays one “width” and I wasn’t sure if it was barWidthDegrees or pulseWidthDegrees. You may want to change the display to show both.

BiPolarPulseV2.0.ino (9.75 KB)

Hi, I was adding the pots and changed a couple variable names and now it won’t compile!! I must have done something dumb!
Tom

tester_revision_2019.ino (9.88 KB)

Ok, Here’s your version which now compiles. Tere was a misplaced bracket and an undefined variable.
I now have my 20x4 online so I can follow your work more closely,

I see a couple of problems with the code as provided.

You are mapping two values from the same pot. Is that correct?

pickupValue = map(analogRead(pot2), 0, 1023, 10, 90);
    barWidth = map(analogRead(pot2), 0, 1023, 20, 80);

I think this should be pickupValue if you want the pot controlled variable and not the fixed one I had.

lcd.print(pickup);

I leave you to make these changes to the code if I’m correct.

Have you had the charge pulses on the scope? If so, do they look correct in relationship to the trigger pulse?

tester_revision_2019.ino (9.86 KB)

I just noted another error in the analogRead() pot mapping section. The placement of the new readings had broken the if/else construction of the rpm mapping with and without the charge pulses. That section should look like this.

if (millis() - lastAnalogRead >= analogReadInterval)
  {
    //add other potentiometer adjustments here
    lastAnalogRead += analogReadInterval;
    if (chargePulse)//lowest rpm with charge pulse is 615
      RPM = map(analogRead(pot1), 0, 1023, 615, 4000);
    else
      RPM = map(analogRead(pot1), 0, 1023, 500, 4000);
    pickupValue = map(analogRead(pot2), 0, 1023, 10, 90);//two variables on same pot
    barWidth = map(analogRead(pot2), 0, 1023, 20, 80);//two variables on same pot

  }

EDIT:

With the new adjustable variable pickupValue the code for degreesAdvance should change.

//degreesAdvance = pickup - delayDegrees;
    degreesAdvance = pickupValue - delayDegrees;

Hi, OK I will work with this for a while. Yes there are 3 pots so should be 1 is rpm, 2 is pickup, 3 is bar width.
Many thanks, Tom

Hi, OK with just the rpm pot it works. With the installation of either of the other 2 it crashes.
Tom

tester_revision_2019_A.ino (9.99 KB)

You can not use A4 and A5 for the new adjustment pots. They are common with the SDA and SCL pins for the i2c lcd.
I have changed them to A2 and A1 and the code does not crash. One other correction is that pulseWidth needs a non zero value, and I have set it to 10 degrees. There was also an incorrect arrangement of the if/else on the rpm mapping with the charge pulses which I have corrected.

tester_revision_2019_B.ino (9.94 KB)

OK, thanks. I will do some more testing and see it the DAC will work! But waiting on the dc-dc converter from China. Tom

Hi, on line 98 it says FALLING. I have a tester with the code here to check the time from output trigger on pin 9 to the signal back on pin 3. I think I may be ahead of myself here but it is off by 600 uS. When delay is 0 it reports 600. Probably this is fine tuning and must wait for DAC to be working. Also I need confirmation this code would be accurate?
Here is shot of the charge pulses and the pin 6 - 12 of DAC.
Thanks, Tom

// variable delay output for testing main board Us Delay line
// with display
int pot1 = A3;            // select the input pin for the pot (changed for this version because of lcd)
int delayValue = 0;       // variable to store the value coming from the sensor
unsigned long analogReadInterval = 1000;//read pot and map
#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(9600);
  lcd.begin(20, 4);                                                        // initialize the lcd for 20 chars 4 lines, turn on backlight
  lcd.setCursor(2, 0);                                                     // lcd display setup of unchanging headings
  lcd.print("Us Delay:");                                                  // print fixed characters
  
   attachInterrupt(digitalPinToInterrupt(3),pulse, FALLING);               //added this line to have any pulse out**********
   pinMode(7,OUTPUT);
   
}

void loop()
  {
    delayValue = analogRead(pot1);
    delayValue = map(delayValue, 0, 1023, 0, 5100);                           

    lcd.setCursor(12, 0);
    lcd.print("       ");                                                  //print blank spaces to clear old data
    lcd.setCursor(12, 0);
    lcd.print(delayValue);
    delay (500);
  }

void pulse()
{
  delayMicroseconds(delayValue);
  digitalWrite(7,HIGH);
  delayMicroseconds(200);                                                  // modify the pulse length to see what is sensed by main board*******
  digitalWrite(7,LOW);
}

I think I may be ahead of myself here but it is off by 600 uS. When delay is 0 it reports 600.

My simulated response sketch is different, and has always used a rising interrupt edge and a 10us pulse width for the response pulse.

//pin 3 interrupt as as input 
//pin 4 as pulse output
void setup() {
 pinMode(4,OUTPUT);
 attachInterrupt(digitalPinToInterrupt(3),pulse,RISING);//interrupt on pin3
}

void loop() {}

void pulse()
{
  delayMicroseconds(1000);
  //delayMicroseconds(0);
  digitalWrite(4,HIGH);
  delayMicroseconds(10);
  digitalWrite(4,LOW);
}

With delayMicroseconds(1000) I read 1008, and with delayMicroseconds(0) I see 8 or 16.

We have to figure out where the 600us in your sketch is coming from. Are the grounds connected? Try my sketch as the module response simulator and see what you get. I'm not sure why your comments say couldn't get a response with a RISING interrupt.

We can get better precision than delayMicroseconds(), but that's not the main issue at this point.

I'm a little unclear about whether the interrupt on the main sketch should be set for RISING or FALLING. As you say its currently at FALLING because the input pin is set for INPUT_PULLUP to keep if from floating. I believe that this may be a change from the original code of several years ago. I don't remember if the response from the cdi module is open collector with switch to ground or if its an active signal. We should fine tune this when we have the new hardware and an actual module.

Hi, OK got the DAC running and everything works, well mostly! First issue is that if I start it up wile set in Falling mode, the display has 2 areas that display wrong info, confused. If I start it up in Rising it is OK.

There are a couple other things that I am unsure of like the timing and computation of Advance display, it will likely be always from the end of the first sine until D3 sees a signal. The thing I don’t understand right now is that the barWidth setting influences this and I don’t think it should.

Also attached is the DAC output set to barWidth of 80
Tom

I start it up wile set in Falling mode, the display has 2 areas that display wrong info, confused. If I start it up in Rising it is OK.

What are the two incorrect display values/positions in the Falling mode?

DAC output at bar = 80

The word RPM changes to something else, the us: gets changed to a bunch of numbers. Tom