An another delay and counter question

Hi guys;

I just finish program a frequency counter program. I experiment with two sampling technique. One with while() & millis() and the other use a for(). Both work fine, except the result of the for() is not "right" . I know it is cause by the variable inside the for() loop.

But my main problem is the delay() function. In one version , it is not executing, but the other, it is executing... Why ????

The only change I did is : Not working ---> attacheInterrupt() in setup, interrupts in loop , nointerrupts in loop.
Working ---> No in setup, attachInterrupt() in loop, detachInterrupt() in loop.

I mean by "not working" : Serial.print() execute to quickly, not slow down ( delay() not working ), but it did the frequncy sample.

Here the version that did not work.

/*
 size = 3192
 Version 1.0
 
 To measure a frequency pulse and display in the serial monitor.
 
 Connect the pulse source to pin 2. 
 The source have to be a +5 V to Gnd signal.
 
 In this experiment, I use a second Arduino to generated
 a pulse.  A 555 in astable Mode can be use. 
 Just connect pin 3 of the  555 to pin 2 of the Arduino.
 
 Program by Serge J Desjardins aka techone
 Toronto, Ontario, Canada
 
 Compile.
 Tested : The sampling is OK
          After a Serail.print the delay not working.

*/

byte freqpin = 2;

unsigned long timeone;

unsigned int frequency;
unsigned int timediff;

unsigned int tcount=0;

// sample time of 1 second or 1000 ms.
unsigned int sampletime=1000;
unsigned long sampletimetwo=1000000;
unsigned long j; 

volatile unsigned int count=0;

void setup()
{
 pinMode(freqpin, INPUT);
 // set interrupt pin 2 and use rising ( from gnd to +5 ) 
 attachInterrupt(0,freqinput,RISING); 
 Serial.begin(9600); 
} 

void loop()
{
 /* Use while() loop and use millis(). 
    Calculate the time difference and compare the 
    value with the sample time. 
 */
 tcount=0;
 count=0;
 // activate the interrupt
 timediff=0;
 interrupts();
 timeone=millis();
 while(timediff<sampletime)
 {
  tcount=count;
  timediff=millis()-timeone; 
 }
 // de-activated the interrupt
 noInterrupts();
 // Calculated frequency
frequency=tcount; 
 
 // Display frequency
 Serial.print("The time dealy is : ");
 Serial.println(timediff, DEC);
 Serial.print("Test while - ");
 Serial.print("Frequency : ");
 Serial.println(frequency,DEC);
 delay(3000); // <-- Not being executed

 count=0;
 tcount=0;
 
 /* Using a for() loop design. no need for millis() but
    you need to fine tune the constant sampletime to set
   the proper time for sampling.
 */  
 // activate the interrupts
  
 interrupts();
 timeone=millis();
 for (j=0;j<sampletimetwo;j++)
 {
  tcount=count; 
 }
 timediff=millis()-timeone; 
 // de-activated the interrupt
 noInterrupts();
 // Calculated frequency
frequency=tcount; 
 
 // Display frequency
 Serial.print("The time difference is : ");
 Serial.println(timediff,DEC);
 Serial.print("Test Loop - ");
 Serial.print("Frequency : ");
 Serial.println(frequency,DEC);
 Serial.println(" ");
 delay(3000);  // <-- Not being executed
}  

// The interrupt routine
void freqinput ()
{
  count++;
}

As for the working version...

The delay time are in ms. The frequency are in Hz. As you can see, to measure a frequency or a pulse, you need to measured how many pulses during 1 second.

Now, in the working version here the results :

The difference of the delay is : 1000
Test while - Frequency : 551
The time difference is : 884
Test Loop - Frequency : 488

It look like it is working well. except the Loop ( a for() loop ) The variable sampletimetwo need to be adjusted or "fine tuning".

Here the working version code :

/*
 size = 3276
 Version 1.0 B
 
 To measure a frequency pulse and display in the serial monitor.
 
 Connect the pulse source to pin 2. 
 The source have to be a +5 V to Gnd signal.
 
 In this experiment, I use a second Arduino to generated
 a pulse.  A 555 in astable Mode can be use. 
 Just connect pin 3 of the 555 to pin 2 of the Arduino.
 
 Program by Serge J Desjardins aka techone
 Toronto, Ontario, Canada
 
 Compile and Tested.

*/

byte freqpin = 2;

unsigned long timeone;

unsigned int frequency;
unsigned int timediff;

unsigned int tcount=0;

// sample time of 1 second or 1000 ms.
unsigned int sampletime=1000;
unsigned long sampletimetwo=1000000;
unsigned long j; 

volatile unsigned int count=0;

void setup()
{
 pinMode(freqpin, INPUT);
 // set interrupt pin 2 and use rising ( from gnd to +5 ) 
 attachInterrupt(0,freqinput,RISING); 
 Serial.begin(9600); 
} 

void loop()
{
 /* Use while() loop and use millis(). 
    Calculate the time difference and compare the 
    value with the sample time. 
 */
 // activate the interrupt
 tcount=0;
 count=0;
 timediff=0;
 attachInterrupt(0,freqinput,RISING);
 //interrupts();
 timeone=millis();
 while(timediff<sampletime)
 {
  tcount=count;
  timediff=millis()-timeone; 
 }
 // de-activated the interrupt
 detachInterrupt(0);
 //noInterrupts();
 // Calculated frequency
frequency=tcount; 
 
 // Display frequency
 Serial.print("The difference of the delay is : ");
 Serial.println(timediff,DEC);
 Serial.print("Test while - ");
 Serial.print("Frequency : ");
 Serial.println(frequency,DEC);
 delay(3000);
 
 count=0;
 tcount=0;
 
 /* Using a for() loop design. no need for millis() but
    you need to fine tune the constant sampletime to set
   the proper time for sampling.
 */  
 // activate the interrupts
  
 // interrupts();
 attachInterrupt(0,freqinput,RISING);
 timeone=millis();
 for (j=0;j<sampletimetwo;j++)
 {
  tcount=count; 
 }
 timediff=millis()-timeone;
 // de-activated the interrupt
 //noInterrupts();
 detachInterrupt(0);
 // Calculated frequency
frequency=tcount; 
 
 // Display frequency
 Serial.print("The time difference is : ");
 Serial.println(timediff,DEC);
 Serial.print("Test Loop - ");
 Serial.print("Frequency : ");
 Serial.println(frequency,DEC);
 Serial.println("\n");
 delay(3000);
 
}  

// The interrupt routine
void freqinput ()
{
  count++;
}

Techone:
Here the version that did not work.

...

// de-activated the interrupt
noInterrupts();
...

delay(3000); // <-- Not being executed

Not surprised it doesn't work. The delay () function uses interrupts.

Hello.. i don't know why you have used such methods for frequency counter, here is my frequency counter, very simple and i hope it will work for you.. in this i have used a "pulseIn" function, you can see about it here http://www.arduino.cc/en/Reference/PulseIn

unsigned long timePeriod = 0;
unsigned long frequency  = 0;

void setup()
{ pinMode(10,INPUT);
  Serial.begin(9600);
}

void loop()
{
  timePeriod = 2*pulseIn(10,LOW);  // timePeriod in MicroSeconds
  frequency  = 1000000/timePeriod;
  Serial.println(frequency);
  delay(1000);
}

Thank for the replies gentlemen.

@nick Gammon

Not surprised it doesn't work. The delay () function uses interrupts

Yep ....It look like it does... To bad, it did not say that little fact in the reference.

@Saad Khalil

Hello.. i don't know why you have used such methods for frequency counter, here is my frequency counter, very simple and i hope it will work for

Thank for your code. I did read PulseIn(), that particular instruction is usefull for a Ultra-Sonic or Laser application or to measure a distance between the transmitter and an object and back. ( reflection ).

To use as a frequency counter, in your code for example, you only measured the LOW time only, assuming the pulse you measured is 50 % duty. But in practice, it is not the case. So in my code, I use an interrupt ( Rising ) , measure how many "pulses" in 1 second. In a regular frequency counter ( electronic ) , it use a 1 second reference ON pulse, ANDed it with the numbers of pulses being counted, than show the resulted. Therefore, I simply using this technique to measure the numbers of pulses in one second. So --- > Numbers of Pulses = Frequency.

I hope you understand my raisoning.

Techone:
@nick Gammon

Not surprised it doesn't work. The delay () function uses interrupts

Yep ....It look like it does... To bad, it did not say that little fact in the reference.

It is generally not a good idea to turn interrupts off unless you fully understand the ramifications. Detaching the interrupt would have had the same effect (the counter would stop counting).

Techone:
To bad, it did not say that little fact in the reference.

However it does say in the documentation for noInterrupts:

Interrupts allow certain important tasks to happen in the background and are enabled by default. Some functions will not work while interrupts are disabled, and incoming communication may be ignored.

So before you call a function like that, with a warning like that, you might want to inquire about the nature of "certain important tasks".

@ Nick Gammon

Thank for your explaination. It look like interrupts/noInterrupts disturbed delay() ( to my surprise ), the serial com, I assume also the software.serial, and any libraries using interrups. So that is why I change to attachInterrupt/detachInterrupt technique. It simply enable/disable pin 2 interrupt. And my program work.

you only measured the LOW time only, assuming the pulse you measured is 50 % duty. But in practice, it is not the case.

i think you mean (if i'm wrong please correct me) that pulseIn is used there where how much pulse is Low, for same amount it will be high.. did u mean this??? so yes i'm assuming that both will be equal thats why i have used pulseIn..
sorry for bad english.. :wink:

@Saad Khalil

To answer your question : YES. A pulseIn(anypin, HIGH/LOW) measure the time the pulse in HIGH or LOW only. A pulse at 50 % duty cycle. ( T= Ton + Toff <-- Ton = Toff ) , your program will work. but to measure a pulse at 5 % duty . T = Ton + Toff .

Let try for example 500 Hz. T= 2 ms. a 5 % duty --> Ton = 0.1 ms Toff = 1.9 ms So you program will do : time period = 2 * 1900 us = 3800 us Frequency = 1000000 / 3800 us = 263 Hz .... not 500 Hz. a 237 Hz difference. So therefore your program have a flaw. You need to re-design your code. Your code did not take into account the duty cycle change of the pulse being measured. Mine is simply count ( any pulses ) for 1 second. And it could be modified to use has a RPM counter. Example : to measure the RMP of a fan. ( ( pulse /s ) / number of blades ) * 60 = RPM ---> change frequency = tcount with frequency = (tcount/3)*60 in my program --> if you have 3 blade. I hope you understand.

Here your modified code : Not compile and not tested.

unsigned long timePeriod = 0;
unsigned long frequency  = 0;
unsigned long ton = 0;
unsigned long toff =0;

void setup()
{ pinMode(10,INPUT);
  Serial.begin(9600);
}

void loop()
{
  ton = pulseIn(10,HIGH); // Measured Time On
 toff = pulseIn(10,LOW); // Measure Time Off
 timePeriod = ton + toff; // add both time 
  frequency  = 1000000/timePeriod; // calculated the frequency
  Serial.println(frequency);
  delay(1000);
}

@Nick Gammon

If I use a noInterrupts() instruction, the millis() function will not work, right ?

WoW!!!!
I got it now Techone :wink:
Thanksssssss alot :slight_smile:

Techone:
If I use a noInterrupts() instruction, the millis() function will not work, right ?

Probably not, it uses a timer interrupt to count.

Probably not, it uses a timer interrupt to count

Thank James C4S. So an noInterrupts() will CANCEL every interrupts , including the internal interrupts inside the ATMega... I got it. I will keep this in mind, in my next programing design.

So the only way I can use a "delay" after a noInterrupts() instruction, it to use a for() loop within a for() loop. If I can figure out the variable inside the loop.

I will figure it out ...

Like :

void home_made_delay( int delayvalue )

int calibrated_data = 1000;  // <-- that value is the problem to figure it out to make a proper delay

for ( int i=0;<delayvalue;i++)
 {
    for ( int j=0;j<calibrated_data;j++)
     {  
    }
}

Techone:
So an noInterrupts() will CANCEL every interrupts , including the internal interrupts inside the ATMega... I got it. I will keep this in mind, in my next programing design.

Well, all interrupts are internal to the ATmega. Functions like millis() and delay() were created by the Arduino team, not Atmel. Since there are cases where timing is critical, it is helpful to have a way to disable all functions that disrupt stable timing.

Techone:
So the only way I can use a "delay" after a noInterrupts() instruction, it to use a for() loop within a for() loop. If I can figure out the variable inside the loop.

Yes, loops are going to be the only way if you insist on disabling all interrupts (which I'm still not clear why you are). The alternative is to disable (detach) and enable (attach) only the interrupts you are using instead of doing so globally. Then the Arduino functions like delay() will continue to work.

Keep in mind that if you create a loop where the variables aren't used elsewhere, the complier is likely to optimize the loop out. So make sure you add some throw-away action.

Techone:
So the only way I can use a "delay" after a noInterrupts() instruction, it to use a for() loop within a for() loop. If I can figure out the variable inside the loop.

Basically you shouldn't have that requirement. You turn interrupts off, briefly, to do something "atomic" like grab a variable and make sure both bytes are being retrieved before an interrupt changes one of them.

As I said before, use detachInterrupt to cancel a particular one that you set up.

@Nick Gammon & James C4S

Thank both of you of your answers, that explain the non-execution problem of my first program. Than I realise that may the cause , so I use attachInterrupt() and detachInterrupt() and it work just fine. I will keep in mind in my next program design.

Thank guys.

@Saad Khalil

I check and verify your program ( including the modification ). The frequency is +/- 2 Hz. That change maybe a problem at higher frequency. I will keep my program in my opinion. Your program will be useful in some others applications.

Here is my results :

// compile & tested
The Frequency is : 554
The time period is : 1803
The Time On is : 1001
The Time Off is : 802
The Frequency is : 556
The time period is : 1798
The Time On is : 1002
The Time Off is : 796
The Frequency is : 552
The time period is : 1809
The Time On is : 1001
The Time Off is : 808

The code :

byte freqpin = 2;

unsigned long timePeriod = 0;
unsigned long frequency  = 0;
unsigned long ton = 0;
unsigned long toff =0;

void setup()
{ pinMode(freqpin,INPUT);
  Serial.begin(9600);
}

void loop()
{
  ton = pulseIn(freqpin,HIGH); // Measured Time On
 toff = pulseIn(freqpin,LOW); // Measure Time Off
 timePeriod = ton + toff; // add both time 
  frequency  = 1000000/timePeriod; // calculated the frequency
  Serial.print("The Frequency is : ");
  Serial.println(frequency,DEC);
  Serial.print("The time period is : ");
  Serial.println(timePeriod,DEC);
  Serial.print("The Time On is : ");
  Serial.println(ton,DEC);
  Serial.print("The Time Off is : ");
  Serial.println(toff,DEC);
  delay(3000);
}

Hmmmm.. Nice..

The frequency is +/- 2 Hz. That change maybe a problem at higher frequency

Is this the error???

The frequency is +/- 2 Hz. That change maybe a problem at higher frequency

Is this the error???

To be honnest, I don't know... According to the measurment, the frequency is about 554 Hz +/- 2 Hz deviation. The Toff ( 12 us variation ) vary more than Ton ( 1 us variation ). It is possible the frequency source is not 100 % perfect. ( I am using an Arduino -Breadboard version ) or the program is simply not in sync with the input frequency. For me, this type of "error" is acceptable.

Your program is perfect to measured the duty cycle of a incoming pulse. My program will simply "count pulses per second"

or the program is simply not in sync with the input frequency.

Oh.. Can there be this type of error?? :~
It will be then problem in many of this like programs???