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++;
}
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++;
}
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);
}
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.
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".
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..
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);
}
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.
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.
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);
}
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"