Any strange behaviour, crashing issues etc, post them here! Problems compiling the firmware should go in the General support section rather than here
#52998
Hi,

I`m trying to get this flywheel working using the ardustim but I`m having some issues:
Wheel.JPG
Wheel.JPG (35.01 KiB) Viewed 5414 times
I`m using the change trigger in the setup and the code below syncs on the wider lobe. So there is a routine checking the width of a lobe compare with the others. When the wider lobe is found, the secondaryToothCount is set to 1. This syncs just fine, no problems.

Next, the toothCurrentCount starts to increase when the upward edge of a lobe is seen. This then starts counting the lobes and their degrees etc. Below, i`ve compensated for the uneven distances between lobes and the RPM works great, using the per-lobe measurement while cranking and the per-rotation when above cranking. The same for the getRPM. If I output the actual degrees for reach lobe and the output of getRPM, the calculations work perfect: the getRPM degrees increase and reach the next lobe degrees when that lobe is seen.

So all should be well. However, when looking at the LED`s on the Ardustim, I can see the led`s flicker when increasing or decreasing RPM. When the RPM is the same, they are lit nicely but flicker once every 1 or 2 seconds. So even though the trigger seems perfect, the ignition and injection routines don't seem to agree. Can anyone point me into a direction where to look? The only thing I haven't changed is the triggerSetEndTeeth so perhaps there`s some modification required there....

Thanks!
Code: Select all
void triggerSetup_GM7X()

{
  triggerActualTeeth = 6;
  triggerToothAngle = 720 / triggerActualTeeth; //The number of degrees that passes from tooth to tooth
  triggerFilterTime = 0;
  secondDerivEnabled = false;
  decoderIsSequential = false;
  toothCurrentCount = 0; //Default value
  decoderHasFixedCrankingTiming = true;
  triggerToothAngleIsCorrect = true;
  secondaryToothCount = 0;
  if(configPage2.nCylinders <= 4) { MAX_STALL_TIME = (1851UL * triggerToothAngle); }//Minimum 90rpm. (1851uS is the time per degree at 90rpm). This uses 90rpm rather than 50rpm due to the potentially very high stall time on a 4 cylinder if we wait that long.
  else { MAX_STALL_TIME = (3200UL * triggerToothAngle); } //Minimum 50rpm. (3200uS is the time per degree at 50rpm).


  toothAngles[1] = 0;   //Rising edge of tooth #1
  toothAngles[2] = 40;  //Rising edge of tooth #2
  toothAngles[3] = 110;  //Rising edge of tooth #3
  toothAngles[4] = 180;  //Rising edge of tooth #4
  toothAngles[5] = 220;  //Rising edge of tooth #5
  toothAngles[6] = 290;  //Rising edge of tooth #6



}

void triggerPri_GM7X()
{
  curTime = micros(); 
  if(READ_PRI_TRIGGER() == false){// Inverted due to vr conditioner.
    curGap3 = curTime;
    if (secondaryToothCount==1){
      toothOneMinusOneTime = toothOneTime;
      toothOneTime = curTime;
      currentStatus.startRevolutions++; //Counter 
      toothCurrentCount=1;
    }   
    else if (secondaryToothCount>1) {//Update the counter, but not when it`s zero.
      toothCurrentCount++;
    }
    if( (toothCurrentCount == 1) || (toothCurrentCount == 4 )){
      triggerToothAngle = 40;
    }
    else{
      triggerToothAngle = 70;
    }
  }
  else{
    targetGap = curTime - curGap3;

    if (targetGap > (lastGap * 4)){// Small lobe is 5 degrees, big lobe is 45.
        if (secondaryToothCount == 0 || secondaryToothCount == 6){// We measure the width of a lobe so on the end of one, but want to trigger on the beginning to even the events out. Therefore, secondaryToothCount tracks the DOWNWARD events, and toothCurrentCount updates on the upward events.
          currentStatus.hasSync = true;
        }
        else{
          currentStatus.syncLossCounter++;
        }
        secondaryToothCount = 1;
    }
    else if(secondaryToothCount == 6){
        secondaryToothCount = 1;
        currentStatus.syncLossCounter++;
    }
    else{
      secondaryToothCount++;
    }
    lastGap = targetGap;
    return;
  }
 
    validTrigger = true; //Flag this pulse as being a valid trigger (ie that it passed filters)

    if ( configPage4.ignCranklock && BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) )
    {
      endCoil1Charge();
      endCoil2Charge();
      endCoil3Charge();
      endCoil4Charge();
    }

    if(configPage2.perToothIgn == true)
    {
      int16_t crankAngle = toothAngles[toothCurrentCount] + configPage4.triggerAngle;
      crankAngle = ignitionLimits((crankAngle));
      if(toothCurrentCount > (triggerActualTeeth/2) ) { checkPerToothTiming(crankAngle, (toothCurrentCount - (triggerActualTeeth/2))); }
      else { checkPerToothTiming(crankAngle, toothCurrentCount); }
    }

    toothLastMinusOneToothTime = toothLastToothTime;
    toothLastToothTime = curTime;
    

  
}
void triggerSec_GM7X() { return; } //Not required
uint16_t getRPM_GM7X()
{
  uint16_t tempRPM = 0;
  //During cranking, RPM is calculated 6 times per revolution, once for each rising/falling of the crank signal.
  //Because these signals aren't even (Alternating 70 and 40 degrees), this needs a special function
  if(currentStatus.hasSync == true)
  {
    if( (currentStatus.RPM < currentStatus.crankRPM)  )
    {
      int tempToothAngle;
      unsigned long toothTime;
      if( (toothLastToothTime == 0) || (toothLastMinusOneToothTime == 0) ) { tempRPM = 0; }
      else
      {
        noInterrupts();
        tempToothAngle = triggerToothAngle;
        toothTime = (toothLastToothTime - toothLastMinusOneToothTime); //Note that trigger tooth angle changes between 70 and 110 depending on the last tooth that was seen (or 70/50 for 6 cylinders)
        interrupts();
        toothTime = toothTime * 36;
        tempRPM = ((unsigned long)tempToothAngle * 6000000UL) / toothTime;
        if (toothCurrentCount==1 || toothCurrentCount==4){
          tempRPM = tempRPM*1.75;
        }
        else if (toothCurrentCount==2 || toothCurrentCount==5 ){
          tempRPM = tempRPM*0.57;
        }
        revolutionTime = (10UL * toothTime) / tempToothAngle;
        MAX_STALL_TIME = 366667UL; // 50RPM
      }
    }
    else
    {
      tempRPM = stdGetRPM(360);
      MAX_STALL_TIME = revolutionTime << 1; //Set the stall time to be twice the current RPM. This is a safe figure as there should be no single revolution where this changes more than this
      if(MAX_STALL_TIME < 366667UL) { MAX_STALL_TIME = 366667UL; } //Check for 50rpm minimum
    }
  }

  return tempRPM;
}
int getCrankAngle_GM7X()
{
    //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees)
    unsigned long tempToothLastToothTime;
    int tempToothCurrentCount;
    //Grab some variables that are used in the trigger code and assign them to temp variables.
    noInterrupts();
    tempToothCurrentCount = toothCurrentCount;
    tempToothLastToothTime = toothLastToothTime;
    lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe
    interrupts();

    
    int crankAngle = toothAngles[toothCurrentCount] + configPage4.triggerAngle; 
    
    //Estimate the number of degrees travelled since the last tooth}
    elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime);


    // Compensating for the uneven teeth. Sometimes 40/70, sometimes 70/40 and sometimes just 1.
    if (toothCurrentCount==1 || toothCurrentCount==4){
      crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_TOOTH)*1.75;
    }
    else if (toothCurrentCount==2 || toothCurrentCount==5 ){
      crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_TOOTH)*0.57;
    }
    else{
      crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_TOOTH);
    }

    if (crankAngle >= 720) { crankAngle -= 720; }
    if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; }
    if (crankAngle < 0) { crankAngle += CRANK_ANGLE_MAX; }

    return crankAngle;
}
void triggerSetEndTeeth_GM7X()
{

  int tempEndAngle = (ignition1EndAngle - configPage4.triggerAngle);
  tempEndAngle = ignitionLimits((tempEndAngle));

 
  if( (tempEndAngle > 180) || (tempEndAngle <= 0) )
  {
    ignition1EndTooth = 2;
    ignition2EndTooth = 1;
  }
  else
  {
    ignition1EndTooth = 1;
    ignition2EndTooth = 2;
  }



  lastToothCalcAdvance = currentStatus.advance;
}
#53326
I`ve written a working trigger that can sync on the width of a tooth. It`s been validated and works like a charm including the trigger filter :). Sharing here so hopefully it can benefit someone else.
Code: Select all
/** Rewritten Harley code to use on the Vmax. The flywheel has a pattern of six lobes at 0, 70, 110, 180, 250 and 290 degrees. The first lobe is 45 degrees, all others 5 degrees. 
To sync, we look at the width of the first lobe by measuring the time between the up and down event. 
Once the wide lobe has found, the next rising event will be tooth one. Please note that the secondaryToothCount measures on the DOWN event, and the toothCurrentCount is incremented on the UP event. 
Therefore, the secondaryToothCount refers to the tooth BEFORE the toothCurrentCount counter. This is so we immediatly can sync once the wide tooth has been seen. In TunerStudio, a crank angle of -70 should be setup to compensate.

* @defgroup dec_harley Harley Davidson
* @{
*/
void triggerSetup_Harley()
{
  triggerToothAngle = 0; // The number of degrees that passes from tooth to tooth, ev. 0. It alternates uneven
  secondDerivEnabled = false;
  decoderIsSequential = false;
  MAX_STALL_TIME = (3333UL * 60); //Minimum 50rpm. (3333uS is the time per degree at 50rpm)
  if(initialisationComplete == false) { toothLastToothTime = micros(); } //Set a startup value here to avoid filter errors when starting. This MUST have the initi check to prevent the fuel pump just staying on all the time
  triggerFilterTime = 1500;
  validTrigger = true; // We must start with a valid trigger or we cannot start measuring the lobe width. We only have a false trigger on the lobe up event when it doesn't pass the filter. Then, the lobe width will also not be beasured.
  toothAngles[1] = 0;      //tooth #1
  toothAngles[2] = 40;     //tooth #2
  toothAngles[3] = 110;    //tooth #3
  toothAngles[4] = 180;    //tooth #4
  toothAngles[5] = 220;    //tooth #5
  toothAngles[6] = 290;    //tooth #6
}

//curGap = The difference between the rising lobes.
//curGap2 = the time in micros when the rising lobe is seen
//curGap3 = the time in micros when the falling lobe is seen

void triggerPri_Harley()
{
  curTime = micros();
  

  if(READ_PRI_TRIGGER() == false){// Inverted due to vr conditioner. So this is the rising lobe.
    curGap2 = curTime;
    curGap = curTime - toothLastToothTime;
    if ( (curGap >= triggerFilterTime) ){
      validTrigger = true;
      if (secondaryToothCount > 0) // We have sync based on the tooth width.
      {
          validTrigger = true; //Flag this pulse as being a valid trigger (ie that it passed filters)
          if (secondaryToothCount==1)
          {
            toothCurrentCount = 1;
            triggerToothAngle = 70;// Has to be equal to Angle Routine
            toothOneMinusOneTime = toothOneTime;
            toothOneTime = curTime;
            currentStatus.hasSync = true;
            setFilter((curGap/1.75));//Angle to this tooth is 70, next is in 40, compensating.
          }
          else if (secondaryToothCount==2)
          {
            toothCurrentCount = 2;
            triggerToothAngle = 40;
            setFilter((curGap*1.75));//Angle to this tooth is 40, next is in 70, compensating.
          }
          else if (secondaryToothCount==3)
          {
            toothCurrentCount = 3;
            triggerToothAngle = 70;
            setFilter(curGap);//Angle to this tooth is 70, next is in 70. No need to compensate.
          }
          else if (secondaryToothCount==4)
          {
            toothCurrentCount = 4;
            triggerToothAngle = 70;
            setFilter((curGap/1.75));//Angle to this tooth is 70, next is in 40, compensating.
          }
          else if (secondaryToothCount==5)
          {
            toothCurrentCount = 5;
            triggerToothAngle = 40;
            setFilter((curGap*1.75));//Angle to this tooth is 40, next is in 70, compensating.
          }
          else if (secondaryToothCount==6)
          {
            toothCurrentCount = 6;
            triggerToothAngle = 70;
            setFilter(curGap);//Angle to this tooth is 70, next is in 70. No need to compensate.
          }
          toothLastMinusOneToothTime = toothLastToothTime;
          toothLastToothTime = curTime;
          currentStatus.startRevolutions++; //Counter
          if (triggerFilterTime > 50000){//The first pulse seen 
            triggerFilterTime = 0;
          }
      }
      else{
        triggerFilterTime = 0;
        return;//Zero, no sync yet.
      }
    }
    else{
      validTrigger = false;
    }
  }
  else if (validTrigger == true){// Inverted due to vr conditioner. So this is the falling lobe. We only process if there was a valid trigger.
    curGap3 = curTime - curGap2;
    if (curGap3 > (lastGap * 4)){// Small lobe is 5 degrees, big lobe is 45.
        //Serial.println("Pulse 1!");
        if (secondaryToothCount == 0 || secondaryToothCount == 6){// We measure the width of a lobe so on the end of one, but want to trigger on the beginning to even the events out. Therefore, secondaryToothCount tracks the DOWNWARD events, and toothCurrentCount updates on the upward events.
          currentStatus.hasSync = true;
        }
        else{
          currentStatus.syncLossCounter++;
        }
        secondaryToothCount = 1;
    }
    else if(secondaryToothCount == 6){
        secondaryToothCount = 1;
        currentStatus.syncLossCounter++;
    }
    else{
      secondaryToothCount++;
    }
    lastGap = curGap3;
    return;
  }
  else if (validTrigger == false){
    validTrigger == true;//We reset this every time to ensure we only filter when needed.
  }
}


void triggerSec_Harley()
// Needs to be enabled in main()
{
  return;// No need for now. The only thing it could help to sync more quikly or confirm position.
} // End Sec Trigger


uint16_t getRPM_Harley()
{
  uint16_t tempRPM = 0;
  if (currentStatus.hasSync == true)
  {
    if ( currentStatus.RPM < (unsigned int)(configPage4.crankRPM * 100) )
    {
      // Kein Unterschied mit dieser Option
      int tempToothAngle;
      unsigned long toothTime;
      if ( (toothLastToothTime == 0) || (toothLastMinusOneToothTime == 0) ) { tempRPM = 0; }
      else
      {
        noInterrupts();
        tempToothAngle = triggerToothAngle;
        revolutionTime = (toothOneTime - toothOneMinusOneTime); //The time in uS that one revolution would take at current speed (The time tooth 1 was last seen, minus the time it was seen prior to that)
        toothTime = (toothLastToothTime - toothLastMinusOneToothTime); 
        interrupts();
        toothTime = toothTime * 36;
        tempRPM = ((unsigned long)tempToothAngle * 6000000UL) / toothTime;
      }
    }
    else {
      tempRPM = stdGetRPM(360);
    }
  }
  return tempRPM;
}


int getCrankAngle_Harley()
{
  //This is the current angle ATDC the engine is at. This is the last known position based on what tooth was last 'seen'. It is only accurate to the resolution of the trigger wheel (Eg 36-1 is 10 degrees)
  unsigned long tempToothLastToothTime;
  int tempToothCurrentCount;
  //Grab some variables that are used in the trigger code and assign them to temp variables.
  noInterrupts();
  tempToothCurrentCount = toothCurrentCount;
  tempToothLastToothTime = toothLastToothTime;
  lastCrankAngleCalc = micros(); //micros() is no longer interrupt safe
  interrupts();

  //Check if the last tooth seen was the reference tooth (Number 3). All others can be calculated, but tooth 3 has a unique angle
  int crankAngle;
  crankAngle=toothAngles[tempToothCurrentCount] + configPage4.triggerAngle;
  
  //Estimate the number of degrees travelled since the last tooth}
  elapsedTime = (lastCrankAngleCalc - tempToothLastToothTime);
  crankAngle += timeToAngle(elapsedTime, CRANKMATH_METHOD_INTERVAL_REV);

  if (crankAngle >= 720) { crankAngle -= 720; }
  if (crankAngle > CRANK_ANGLE_MAX) { crankAngle -= CRANK_ANGLE_MAX; }
  if (crankAngle < 0) { crankAngle += 360; }

  return crankAngle;
}

void triggerSetEndTeeth_Harley()
{
  lastToothCalcAdvance = currentStatus.advance;
}
And the init.ino:
Code: Select all
    case DECODER_HARLEY:
      triggerSetup_Harley();
      triggerHandler = triggerPri_Harley;
      //triggerSecondaryHandler = triggerSec_Harley;
      getRPM = getRPM_Harley;
      getCrankAngle = getCrankAngle_Harley;
      triggerSetEndTeeth = triggerSetEndTeeth_Harley;

      //primaryTriggerEdge = RISING; //Always rising
      primaryTriggerEdge = CHANGE;
      attachInterrupt(triggerInterrupt, triggerHandler, primaryTriggerEdge);
      break;

Finally, remark the secondaryToothCount in the main loop in speeduino.ino as shown below:
Code: Select all
      toothLastMinusOneToothTime = 0;
      currentStatus.hasSync = false;
      BIT_CLEAR(currentStatus.status3, BIT_STATUS3_HALFSYNC);
      currentStatus.runSecs = 0; //Reset the counter for number of seconds running.
      currentStatus.startRevolutions = 0;
      toothSystemCount = 0;
      //secondaryToothCount = 0; 
      MAPcurRev = 0;
      MAPcount = 0;
      currentStatus.rpmDOT = 0;
#53636
What has this been tested on? I think you can only use "change" trigger on a hall effect sensor without a vr conditioner.

(Others will know more but thought it worth mentioning in case someone finds this thread in future and my suspicions are correct)
#54672
RempageR1 wrote: Tue Jan 11, 2022 4:00 pm… The only thing you have to think about is that the VR conditioner inverts the signal.
Some VR conditioner modules invert the signal, and others do not. Be sure to read your documentation, ask the seller, or ask here for the specific module you have. ;)
#54691
PSIG wrote: Wed Jan 12, 2022 7:42 am
RempageR1 wrote: Tue Jan 11, 2022 4:00 pm… The only thing you have to think about is that the VR conditioner inverts the signal.
Some VR conditioner modules invert the signal, and others do not. Be sure to read your documentation, ask the seller, or ask here for the specific module you have. ;)
Yes, good point. This was for my V3 VR-conditioner reference board using the MAX9926.

Ok, I feel so stupid. I was running the Uno output[…]

Let's all have a laugh for second. There's trash[…]

It has been done more than a few times, there was […]

Thanks yes, I would really appreciate all the help[…]

Still can't find what you're looking for?