Go Down

Topic: An another delay and counter question (Read 10985 times) previous topic - next topic

Techone

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. 

Code: [Select]

/*
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++;



 

Techone

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 : 

Code: [Select]

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 :

Code: [Select]

/*
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++;





nickgammon



Here the version that did not work. 

Code: [Select]

...

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

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

 


Not surprised it doesn't work. The delay () function uses interrupts.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

saadkhalil123

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
Code: [Select]
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);
}

Techone

Thank for the replies gentlemen.

@nick Gammon

Quote
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

Quote
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.

nickgammon


@nick Gammon

Quote
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).
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon


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


However it does say in the documentation for noInterrupts:

Quote
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".
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Techone

@ 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.

saadkhalil123

Quote
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..  ;)

Techone

@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.

Code: [Select]

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);
}


 

Techone

@Nick Gammon

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

saadkhalil123

WoW!!!!
I got it now Techone ;)
Thanksssssss alot :)

cmiyc


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


Probably not, it uses a timer interrupt to count.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

Techone

Quote
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 :

Code: [Select]


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++)
     { 
    }
}

 

cmiyc


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.


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.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

Go Up