Low freq reads too slow

I’m making a tachometer for my car that uses a string of LED’s. I have 2 problems.

  1. readings are erratic so display junps around. I thought I’d just average several readings but that leads me to the next problem.

  2. The LED’s are in an array so while reading freq they blink off (every 1/4 sec.) I think the problem is that I’m working with very low frequencies (30 - 200 hz) and it takes too long to wait for the on/off.

If I could multi-task…

I’m using interrupts to time several tasks. I don’t understand part of the code (TCCR2A = 0;TIMSK2 |= (1 << OCIE2A); etc). I just copied it and modified it til it did what I wanted.

So this is where I need counsel. Speed it up and make it steady.

When I write code, I first make it work no matter how ugly, then I try to make it more eficient, then I try to make it pretty. I’m in the middle of the process. I still think BASIC so, forgive me. I accept any KIND suggestions on than part too. :slight_smile:

Here’s the code.

//lightbar LED tach fo VW
//pin 2-5 group
//pins 6-10 bulb
//pin 13 RPM input
//v3 re-work timers and add dimming

unsigned long 
  PulseTHi,
  PulseTLo;
  
//slightly simpler code, poss. mixed pin order
int Neg[ 21 ] = { 0,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5 } ;
int Pos[ 21 ] = { 0,6,7,10,8,9,6,7,10,8,9,6,7,10,8,9,6,7,10,8,9};
// if mod positon 3, mod Standby: skip

int
  ReadNowTimer = 0,
  HiRangeTimer = 0,
  MemTimer = 0,

  intHold,
  intMem,
  intPeak = 1; //1 or LED test is LLLOOONNNGG
  
bool
  ReadNowFlag = true,
  HiRangeFlag = false,
  MemFlag = false,
  BlinkFlag=false,
  DimFlag=false; 

const int SignalPin =11, DimmerPin = 12; 

void setup() {
  for (int i=1;i<=5;i++){  //see v2 dfor old loop
    if (i<5){  //contortion bc numbers repeat
      pinMode(Neg[i*5], OUTPUT);
      digitalWrite(Neg[i*5], HIGH);
    }
    pinMode(Pos[i], OUTPUT);
    digitalWrite(Pos[i], LOW);  
  } 
  pinMode(SignalPin, INPUT_PULLUP); //_PULLUP); 

//LED test - turn all on
  for (int j = 1;j<75;j++){
    for (int i=1;i<21;i++){
      FlashLED (Neg[i], Pos[i]);
}  
  }

// ----- configure Timer 2 to generate a compare-match interrupt every 1mS
  noInterrupts();            // disable interrupts
  TCCR2A = 0;                // clear control registers
  TCCR2B = 0;
  TCCR2B |= (1 << CS22) |    // 16MHz/128=8uS
            (1 << CS20) ;
  TCNT2 = 0;                 // clear counter
  OCR2A = 125 - 1;           // 8uS*125=1mS (allow for clock propagation)
  TIMSK2 |= (1 << OCIE2A);   // enable output compare interrupt
  interrupts();              // enable interrupts

}

 
void loop()
{
  if (ReadNowFlag) {intPeak = ReadSpeed();}
  if (intPeak==0) {StandBy();}
  do{                                   // see v2 for old loop   
    if (BlinkFlag==false){
      for(int Cnt=1;Cnt<=intPeak;Cnt++){
        FlashLED (Neg[Cnt], Pos[Cnt]);
      }
    }
    else {delay(1);}  //give loop something to take up its time
   
    if (MemFlag){                           //flash mem light
      //int Neg=(intHold-1)/5+GroupStart;            //-1)/4+1; 
      //int Pos=(intHold-1)%5+BulbStart;            //1)%4+6;  
      FlashLED (Neg[intHold], Pos[intHold]);
    }
    
    if (HiRangeFlag){  //lite last LED if Hi range
      FlashLED (Neg[20], Pos[20]);
    } 
  }while (ReadNowFlag==false);  //read every 1/4 sec
}

void FlashLED (int N, int P){
  digitalWrite(N, LOW);
  digitalWrite(P, HIGH);   //this was delay(1), changed to try dimming.
    delayMicroseconds(400);  //700&100 w 200us delay?
    if (intPeak==0){delay(50);}  // longer for standby - 2 50 ms, the 40 micros won't matter
  digitalWrite(N, HIGH);
  digitalWrite(P, LOW); 
  if (DimFlag){delayMicroseconds(600);}
}

int ReadSpeed(){
  int x;
  
  /*
  1000RPM=500*4=2000 pulse/min=33.33pulse/sec (hz)

  new lay-out   oo0oooo0oooo0oooo0oo
                  1k  1.5   2k  2.5
                  1k   2    3    4
  Lo 1-2.5k, ea LED=100 RPM =3.33hz: start 800 rpm, 27hz                 
  Hi  1-4k,  ea LED=200 RPM =6.67hz: start 600 RPM, 53hz  // Hi  2-5k,  ea LED=200 RPM =6.67hz: start 1600 RPM, 53hz  overlap 1600-2700
  */
  PulseTHi=pulseIn(SignalPin,HIGH,100000); //rem'd for test
  PulseTLo=pulseIn(SignalPin,LOW,100000);
  x=1080000/(PulseTHi+PulseTLo);//in hz
  if(x<0){x=0;}  //elim -1
    // fix, 999 RPM truncates to lower rather than round up
  
  if (x !=0) {  // 100,10 to do float math w/ int
    x=(((x*100)/33)+5)/10;     // hz>100rpm (+.5 to rnd up)  //x=x/4-1;           //in LEDs
      if (x>27|| HiRangeFlag) {  // if is or was HI
       if (x>27) {  // if still Hi re-set timer
         HiRangeTimer=0;  
         HiRangeFlag=true;                 
       }
       x=x/2-2;  //re-scale, RPM>LEDs (-2, LEDs start @600, <------------------------   ****change here to go 2000-5000 scale****   --------------------------
      }
      else {
        x=x-7;  //LEDs start @800
        if (HiRangeFlag) {
          intMem=intMem/2;//-.5;  //re-scale if down scaled
        }
      }

    if (x<1) {x=1; BlinkFlag = !BlinkFlag;} // always have at least 1 lit, blink if off scale //toggling flag gives 1/2" blink
    else if (x>20) {x=20; BlinkFlag = !BlinkFlag;}
    else {                                        
      BlinkFlag=false;
      if (x<intMem-2){  //2 LEDs less
        MemFlag=true;
        intHold=intMem;
      }  
      intMem=x;  
    }
  }
  ReadNowFlag=false;
  DimFlag=digitalRead(DimmerPin);
  return x;
   
}

void StandBy(){
   //cylon effect
  while (intPeak==0){ 
    for(int Cnt=2;Cnt<=20;Cnt++){  //2,19: skip first/last for better effect
      if (Pos[Cnt]!=10){   //skip off set LED for better effect
        FlashLED (Neg[Cnt], Pos[Cnt]);
      } 
    }
    for(int Cnt=19;Cnt>=1;Cnt--){
      if (Pos[Cnt]!=10){
        FlashLED (Neg[Cnt], Pos[Cnt]);
      }
    }
    //intPeak=ReadSpeed(); don't need. on timer
  }
  
 
}

// -------------------------------
// task scheduler (1mS interrupt)
// -------------------------------
ISR(TIMER2_COMPA_vect)
{
  // ----- timers
  /*
     Each timer counts the number of milliseconds since its associated task was
     last performed. Each additional task requires another timer and flag.
  */
   
  // ----- task1
  ReadNowTimer++;
  if (ReadNowTimer > 125 - 1) { // read @ 1/8 sec. (was 250ms
    ReadNowTimer = 0;
    ReadNowFlag = true;
  }

  // ----- task2
  if (HiRangeFlag){
    HiRangeTimer++;
    if (HiRangeTimer > 5000 - 1) {  // change range
      HiRangeTimer = 0;
      HiRangeFlag = false;
    }
  }

  // ----- task3
  if (MemFlag){
    MemTimer++;
    if (MemTimer > 1500 - 1) {  // Mem lite 
      MemTimer = 0;
      MemFlag = false;
      intHold=0;
      intMem=0;
    }
  }
}

Hi,
What RPM resolution are you aiming for?

Tom... :slight_smile:

The range is about 30 to 200, well, 150. The difference in hz from one LED to another is 3.3 in low scale and 6.6 in high. 33hz per 1000RPM. 10 LED low, 5 high scale.

I debated between using hardware interupts and timers to figure the freq. or using FreqMeasure library. Either way I had to resolder wires. I went with the library as I've never messed with interrupts and thought it would be easier. Didn't get it to work.

Below will take up to 200 ms (worst case); it might explain the 1/4 sec. Is SignalPin the pin that gets the 200Hz signal?

  PulseTHi = pulseIn(SignalPin, HIGH, 100000); //rem'd for test
  PulseTLo = pulseIn(SignalPin, LOW, 100000);

Variables that are used in both the interrupt and in the other parts of your code should be declared volatile.

bwaii:
I'm making a tachometer for my car that uses a string of LED's. I have 2 problems.

What is producing the pulses that you are using to measure the speed?

I suspect that recording the time (micros() ) for a pulse using an interrupt and calculating the time since the previous pulse would be be better - no blocking of the Arduino between pulses.

The code in this link is derived from a program I used to control the speed of a small DC motor

...R

Hi,
What model Arduino are you using?
Can you post a schematic of your project please?
Where did you get the code you have modified?

Thanks… Tom… :slight_smile:

Robin2:
What is producing the pulses that you are using to measure the speed?

5v square wave from spark module tach pin. I had it going to straight to a 817(?) optoisolator but it drew too much current and killed the engine. So I added a pn2222a transistor with a 1k on the collector. I’m getting a nice 4v square at the board.

TomGeorge:
What model Arduino are you using?
Can you post a schematic of your project please?
Where did you get the code you have modified?

mini pro clone. Never got the FTDI to work, but that’s another thread.

I don’t remember where I got the code. It’s the part that starts “no interrupts” to “interupts” and then the TSR at the end.

I’ll send a pic of schematic.

sterretje:
Is SignalPin the pin that gets the 200Hz signal?

Variables that are used in both the interrupt and in the other parts of your code should be declared volatile.

yes, pin 11

changed to volatile

FYI I ended up using FreqMeasure and decreasing the delay on the flags. Here’s the code.

//lightbar LED tach fo VW
//
//v4 add FreqMeasure Library to fix pause and "splatter"

/* 
 *  use pin 8, no analogwrite to 9, 10
*FreqMeasure.begin();   //Begin frequency measurement.
*FreqMeasure.available();  //Returns the number of measurements available to read, or 0 (false) if none are unread. 
         If your program spends time on other tasks and a relatively fast waveform is being measured, several readings may be available.
*FreqMeasure.read();   //Read a measurement. An unsigned long (32 bits) containing the number of CPU clock cycles that elapsed during 
         one cycle of the waveform. Each measurement begins immediately after the prior one without any delay, so several measurements 
         may be averaged together for better resolution.
*FreqMeasure.countToFrequency(count); // Convert the 32 bit unsigned long numbers from read() to actual frequency.
*FreqMeasure.end(); Stop frequency measurement. PWM (analogWrite) functionality may be used again. 
*/

#include <FreqMeasure.h>

  
//slightly simpler code, poss. mixed pin order
int Neg[ 21 ] = { 0,5,5,5,5,5,4,4,4,4,4,3,3,3,3,3,2,2,2,2,2} ;
int Pos[ 21 ] = { 0,9,11,10,7,6,9,11,10,7,6,9,11,10,7,6,9,11,10,7,6};
// if mod positon 3, mod Standby: skip

int
  ReadNowTimer = 0,
  HiRangeTimer = 0,
  MemTimer = 0,
  FreqMeasureTimer = 0,
 
  intPeak = 1; //1 or LED test is LLLOOONNNGG
 
volatile int
  FreqCount= 0,

  intHold= 0,
  intMem= 0;
 
volatile double FreqSum =0;  
  
volatile bool
  ReadNowFlag = true,
  HiRangeFlag = false,
  MemFlag = false;

bool  
  BlinkFlag=false,
  DimFlag=false; 

const int  SignalPin =8, DimmerPin = 12; //

void setup() {
    for (int i=1;i<=5;i++){  //see v2 dfor old loop
    if (i<5){  //contortion bc numbers repeat
      pinMode(Neg[i*5], OUTPUT);
      digitalWrite(Neg[i*5], HIGH);
    }
    pinMode(Pos[i], OUTPUT);
    digitalWrite(Pos[i], LOW);  
    } //end for
  pinMode(SignalPin, INPUT_PULLUP); //_PULLUP); 

//LED test - turn all on
  for (int j = 1;j<75;j++){
    for (int i=1;i<21;i++){
      FlashLED (Neg[i], Pos[i]);
    }  
  }

// ----- configure Timer 2 to generate a compare-match interrupt every 1mS
  noInterrupts();            // disable interrupts
  TCCR2A = 0;                // clear control registers
  TCCR2B = 0;
  TCCR2B |= (1 << CS22) |    // 16MHz/128=8uS
            (1 << CS20) ;
  TCNT2 = 0;                 // clear counter
  OCR2A = 125 - 1;           // 8uS*125=1mS (allow for clock propagation)
  TIMSK2 |= (1 << OCIE2A);   // enable output compare interrupt
  interrupts();              // enable interrupts

FreqMeasure.begin();
} // end setup

 
void loop() {
  if (ReadNowFlag) {intPeak = ReadSpeed();}
  if (intPeak==0) {StandBy();}
  do{                                   // see v2 for old loop   
    if (BlinkFlag==false){
      for(int Cnt=1;Cnt<=intPeak;Cnt++){
        FlashLED (Neg[Cnt], Pos[Cnt]);
      }
    }
    else {delay(1);}  //give loop something to take up its time
   
    if (MemFlag){                           //flash mem light
      //int Neg=(intHold-1)/5+GroupStart;            //-1)/4+1; 
      //int Pos=(intHold-1)%5+BulbStart;            //1)%4+6;  
      FlashLED (Neg[intHold], Pos[intHold]);
    }
    
    if (HiRangeFlag){  //lite last LED if Hi range
      FlashLED (Neg[20], Pos[20]);
    } 
  }while (ReadNowFlag==false);  //read every 1/4 sec
} // end loop

void FlashLED (int N, int P){
  digitalWrite(N, LOW);
  digitalWrite(P, HIGH);   //this was delay(1), changed to try dimming.
    delayMicroseconds(400);  //700&100 w 200us delay?
    if (intPeak==0){delay(50);}  // longer for standby - 2 50 ms, the 400 micros won't matter
  digitalWrite(N, HIGH);
  digitalWrite(P, LOW); 
  if (DimFlag){delayMicroseconds(1000);}//was 600
}
//____________________

int ReadSpeed(){
  int x;
  
  /*
  1000RPM=500*4=2000 pulse/min=33.33pulse/sec (hz)

  new lay-out   oo0oooo0oooo0oooo0oo
                  1k  1.5   2k  2.5
                  1k   2    3    4
  Lo 1-2.5k, ea LED=100 RPM =3.33hz: start 800 rpm, 27hz                 
  Hi  1-4k,  ea LED=200 RPM =6.67hz: start 600 RPM, 53hz  // Hi  2-5k,  ea LED=200 RPM =6.67hz: start 1600 RPM, 53hz  overlap 1600-2700
  */
  
     float frequency = FreqMeasure.countToFrequency(FreqSum / FreqCount)+.5;
      FreqSum = 0;
      FreqCount = 0;
      x=frequency;
      //}
        
  if(x<0){x=0;}  //elim -1
     
  if (x !=0) {  // 100,10 to do float math w/ int
    x=(((x*100)/33)+5)/10;     // hz>100rpm (+.5 to rnd up)  //x=x/4-1;           //in LEDs
      if (x>27|| HiRangeFlag) {  // if is or was HI
       if (x>27) {  // if still Hi re-set timer
         HiRangeTimer=0;  
         HiRangeFlag=true;                 
       }
       x=x/2-2;  //re-scale, RPM>LEDs (-2, LEDs start @600, <------------------------   ****change here to go 2000-5000 scale****   --------------------------
      }
      else {
        x=x-7;  //LEDs start @800
        if (HiRangeFlag) {
          intMem=intMem/2;//-.5;  //re-scale if down scaled
        }
      }

    if (x<1) {x=1; BlinkFlag = true;} //!BlinkFlag;} // always have at least 1 lit, blink if off scale //toggling flag gives 1/2" blink
    else if (x>20) {x=20; BlinkFlag = true;}//!BlinkFlag;}
    else {                                        
      BlinkFlag=false;
      if (x<intMem && MemFlag==false){  //2 LEDs less
        MemFlag=true;
        intHold=intMem;
      }  
      intMem=x;  
    }
  }
  ReadNowFlag=false;
  DimFlag=digitalRead(DimmerPin);
  return x;
} // end readspeed
//_________________________

void StandBy(){
   //cylon effect
  while (intPeak==0){ 
    for(int Cnt=2;Cnt<=20;Cnt++){  //2,19: skip first/last for better effect
      if (Pos[Cnt]!=10){   //skip off set LED for better effect
        FlashLED (Neg[Cnt], Pos[Cnt]);
      } 
    }
    if (ReadNowFlag) {intPeak = ReadSpeed();}
    for(int Cnt=19;Cnt>=1;Cnt--){
      if (Pos[Cnt]!=10){
        FlashLED (Neg[Cnt], Pos[Cnt]);
      }
    }
  if (ReadNowFlag) {intPeak = ReadSpeed();}
  }
} // end standby

// -------------------------------
// task scheduler (1mS interrupt)
// -------------------------------
ISR(TIMER2_COMPA_vect)
{
  // ----- timers
  /*
     Each timer counts the number of milliseconds since its associated task was
     last performed. Each additional task requires another timer and flag.
  */
   
  // ----- task1
  ReadNowTimer++;
  if (ReadNowTimer > 25 - 1) { //  (was 250ms
    ReadNowTimer = 0;
    ReadNowFlag = true;
  }

  // ----- task2
  if (HiRangeFlag){
    HiRangeTimer++;
    if (HiRangeTimer > 5000 - 1) {  // hold hi range
      HiRangeTimer = 0;
      HiRangeFlag = false;
    }
  }

  // ----- task3
  if (MemFlag){
    MemTimer++;
    if (MemTimer > 1500 - 1) {  // hold Mem lite 
      MemTimer = 0;
      MemFlag = false;
      intHold=0;
      intMem=0;
    }
  }

    // ----- task4
  FreqMeasureTimer++;
  if (FreqMeasureTimer > 10 - 1) { 
    FreqMeasureTimer = 0;
    if (FreqMeasure.available()) {      // average several reading together
      FreqSum = FreqSum + FreqMeasure.read();
      FreqCount++;
    }
  }
} // end ISR

Still tweeking but it works