I want to measure the signal frequency on 3 arduino pins (2, 1, 0), but I have no clue on how to do that. Could anyone point me what to read, or maybe tell me how the algorithm should go, or share some nice piece of code that does that?
What is the frequency range?
Well, I would like it to be ~10Hz - ~4MHz, but I will settle with less if it's impossible. The board is Arduino Uno.
on 3 arduino pins (2, 1, 0),
Since you are using the hardware serial pins, you won't be able to use Serial, so what would you do with the information you received?
PaulS:
Since you are using the hardware serial pins, you won't be able to use Serial, so what would you do with the information you received?
Process the received frequency and output the result on LCD display. Though, I'm kinda giving up, I don't understand a thing in these "interrupt" thingies ![]()
I thought you already had this bit licked. You even told me it takes 250ms-300ms to sample ![]()
But anyhow. The kind of thing you want is something like this.
void setup()
{
lcd.begin(16, 2);
//although this is interrupt 0 it's
//actualy triggered by pin 2
attachInterrupt(0,countFrequency,RISING);
}
unsigned long lastUpdate=0;
void loop()
{
char line1[20];
char line2[20];
unsigned long t=millis();
if ((t-lastUpdate)>=1000)
{sprintf(line1,"Frequency");
sprintf(line2,"%d Hz",freqCount);
freqCount=0;
lastUpdate=t;
}
}
void countFrequency()
{
freqCount++;
}
KenF:
I thought you already had this bit licked. You even told me it takes 250ms-300ms to sampleBut anyhow. The kind of thing you want is something like this.
void setup()
{
lcd.begin(16, 2);
//although this is interrupt 0 it's
//actualy triggered by pin 2
attachInterrupt(0,countFrequency,RISING);
}
unsigned long lastUpdate=0;
void loop()
{
char line1[20];
char line2[20];
unsigned long t=millis();
if ((t-lastUpdate)>=500)
{sprintf(line1,"Frequency");
sprintf(line2,"%d Hz",freqCount);
freqCount=0;
lastUpdate=t;
}
}
void countFrequency()
{
freqCount++;
}
I wish, that's just what I've planned
But all of the sudden, BAM, interrupts, timers and zillion of other stuff I never heard about before appeared
I'm reading about it right now, but can't say I understand much.
Thanks for the answer, will look at your code now ![]()
And from I have figured out from now, my plan to feed the input frequency on the three pins I mentioned won't work, will it? I can only use INT pins, 2 & 3, right?
Garurumon:
I wish, that's just what I've plannedBut all of the sudden, BAM, interrupts, timers and zillion of other stuff I never heard about before appeared
I'm reading about it right now, but can't say I understand much.
You learn by doing... seeing what works. Keep going!
Garurumon:
Thanks for the answer, will look at your code nowAnd from I have figured out from now, my plan to feed the input frequency on the three pins I mentioned won't work, will it? I can only use INT pins, 2 & 3, right?
Your making an LC meter right? So your switches define what you are measuring, but you only need have one place for measuring surely? So one interrupt is all you need?
Your making an LC meter right?
I don't even know what an LC meter is, so I never would have jumped to that conclusion. It's why I asked what you were trying to do, to supplement the how part.
PaulS:
I don't even know what an LC meter is, so I never would have jumped to that conclusion. It's why I asked what you were trying to do, to supplement the how part.
It measures inductance or capacitance.
He mentioned it in a previous thread.
Hey chilli, you were asking for schematics right?
These are the F/HF inputs: http://i.imgur.com/qUQm4ql.jpg
And L/C uses a standard: http://electronics-diy.com/img/lc_meter_pic16f84a.jpg
I'm making L/C/F meter, so I need another input for frequency ![]()
And about one interrupt pin, well, I would need at least 2, since 3 is not possible. I already made the box, and I want it to look at simple as possible, I'll even maybe diss the prescaler part completely, since I haven't done anything at frequencies that high, and it will be useless to me for some time. Plus, I have an awesome looking box sitting and collecting dust, and maybe my next project will be frequency counter using 7-segment displays ![]()
The reason is, the prescaler outputs around 70MHz when the inputs are floating, and that would interfere with other inputs if they were to use the same pin. And I don't want to use any more switches. I know, dumb, but it's just me ![]()
Ah I see. You are looking to run this on an Arduino copying ideas from a PIC?
Fair enough, looks doable.
Well yeah
I think it's possible ![]()
Btw KenF, I looked at your code and I think I understand it. But I probably don't heh. Here's what I came up with:
/* L/C/F meter by Milos G. */
#include <LiquidCrystal.h> // includes LCD library
#define Pi 3.14159 // math.Pi
LiquidCrystal lcd(8, 9, 10, 11, 12, 13); // LCD pins
// choosing pins for selecting mode via rotary encoder
const int CONF1 = A0;
const int CONF2 = A1;
const int CONF3 = A2;
int state = 0; // state of the rotary switch
const int REED = 1; // pin 1 will controll the reed relay, it's also a PWM pin
const int Ccal = pow(10, -9); // value of an accurate capacitor
int Fcal1 = 0, Fcal2 = 0; // values before and after including Ccal
const int LCIN = 3; // pin that measures L/C related stuff
const int FIN = 2; // pin that measures frequency
float counter0 = 0; // global variable that increments during a given time interval, LC
float counter1 = 0; // same, F
unsigned long lastUpdate = 0;
const int UU1 = 4, UU2 = 5, UU3 = 6, UU4 = 7, UU5 = A3, UU6 = A4, UU7 = A5, UU8 = 0; // unused pins
void setup() {
// set LCD's dimensions:
lcd.begin(16, 2);
// setting up pin modes
pinMode(CONF1, INPUT_PULLUP); // if LOW -> we measure L
pinMode(CONF2, INPUT_PULLUP); // if LOW -> we measure C
pinMode(CONF3, INPUT_PULLUP); // if LOW -> we measure F, in other cases we measure HF
pinMode(REED, OUTPUT); // controlles the reed relay
pinMode(LCIN, INPUT);
pinMode(FIN, INPUT);
// terminating unused pins:
pinMode(UU1, INPUT_PULLUP);
pinMode(UU2, INPUT_PULLUP);
pinMode(UU3, INPUT_PULLUP);
pinMode(UU4, INPUT_PULLUP);
pinMode(UU5, INPUT_PULLUP);
pinMode(UU6, INPUT_PULLUP);
pinMode(UU7, INPUT_PULLUP);
pinMode(UU8, INPUT_PULLUP);
// interrupts
attachInterrupt(0, count0, RISING); // pin 2 actually, in0 pin
attachInterrupt(1, count1, RISING); // pin 3, int1 pin
// determining the position of the selector:
selector();
}
void loop() {
int old_state; // so we can compare old and new one, to see if it changed
// We have 3 situations
switch (state) {
case 1:
{
old_state=state;
selector();
if(old_state!=state)
{
calibration();
break; // breaks if the switch used
}
lcd.setCursor(0, 0);
lcd.print("L:");
float L = inductance();
// writing the Lx on the screen
lcd.setCursor(0, 1);
lcd.print(L);
lcd.print("H");
break;
}
case 2:
{
old_state=state;
selector();
if(old_state!=state)
{
calibration();
break; // breaks if the switch used
}
lcd.setCursor(0, 0);
lcd.print("C:");
float C = capacitance();
lcd.setCursor(0, 1);
lcd.print(C);
lcd.print("uF");
break;
}
case 3:
{
old_state=state;
selector();
if(old_state!=state)
{
calibration();
break; // breaks if the switch used
}
lcd.setCursor(0, 0);
lcd.print("F:");
lcd.setCursor(0, 1);
lcd.print(frequency1());
lcd.print("Hz");
break;
}
}
}
// LC meter calibration
void calibration() {
if (state == 1 || state == 2) {
Fcal1 = frequency0();
digitalWrite(REED, HIGH);
Fcal2 = frequency0();
digitalWrite(REED, LOW);
}
}
// function that calculates Lx
float inductance() {
float L = 0;
float F = frequency0();
L = ( pow(Fcal1/F, 2) - 1 ) * ( pow(Fcal1/Fcal2, 2) - 1) * 1/Ccal * pow(1/(2*Pi*Fcal1), 2);
return L;
}
// function that calculates Cx
float capacitance() {
float C = 0;
float F = frequency0();
C = ( pow(Fcal1/F, 2) - 1 ) / ( pow(Fcal1/Fcal2, 2) - 1 ) * Ccal;
return C;
}
// function that reads selector's position
void selector() {
if (digitalRead(CONF1) == LOW)
state = 1;
else if (digitalRead(CONF2) == LOW)
state = 2;
else
state = 3;
}
// frequency counter interface, LC
float frequency0() {
float freq = 0;
counter0 = 0;
unsigned long time = millis();
if ((time-lastUpdate)>=500)
{
freq = counter0;
counter0 = 0;
lastUpdate = time;
}
return freq;
}
float frequency1() {
float freq = 0;
counter1 = 0;
unsigned long time = millis();
if ((time-lastUpdate)>=500)
{
freq = counter1;
counter1 = 0;
lastUpdate = time;
}
return freq;
}
// frequency counter
void count0() {
counter0++;
}
void count1() {
counter1++;
}
OK well it's not the way I'd do it but could work. with rather a lot of issues to be ironed out.
First OFF all that value of 500 wants to be changed to 1000. (milliseconds in a second).
Also when you look at the value returned by frequency1 (the function you rely on to provide the current frequency) in the event that the call doesn't coincide with an update (which will only be once every second), it will return 0;
I think you have to re-evaluate your thinking on what dependencies actually exist.
The interrupt takes care of incrementing a counter for every rising edge. To convert this to a frequency count, all we need to do is take a spot reading of this counter every second. So this is a separate process from the increment but is also totally independent on when we want to display it. It's even independent of whether the user wants this information or not. It merely consists of taking note of the the counter and then zeroing it, the logic involved is actually simpler than deciding whether we want it or not.
You could even, potentially, set up the spot check of the counter as an ISR that occurs every second but this would leave your loop function looking rather bare as it would simply need to send the value to the display (on a timely basis) when the switch is in the correct position.
KenF:
First OFF all that value of 500 wants to be changed to 1000. (milliseconds in a second).
Oh yeah, totally forgot about that, I'll just multiply the counter by two to get the frequency, I guess that's a reasonable refresh rate. I'll even maybe cut the sample rate at half.
KenF:
Also when you look at the value returned by frequency1 (the function you rely on to provide the current frequency) in the event that the call doesn't coincide with an update (which will only be once every second), it will return 0;
Hey, wouldn't this simplify things:
float frequency1() {
float freq = 0;
counter1 = 0;
unsigned long time = millis();
do {
freq = counter1;
} while (time<=250);
counter1 = 0; // reset the global counter
freq *= 4; // frequency is multiplied by 4, since 1Hz=1/s
delay(15);
return freq;
}
This way, there should be a loop inside the external frequency measuring function that would read the number of interrupts until the timer reaches 250ms. Then the loop is abandoned, counter reset, and the function will do this every time it is summoned. Are loops beside the "loop()" really that bad?
Btw, I managed to find 4-pole 3-way rotary switch, so I'll be using just one pin on the micro to measure the interrupts, since the LM311 is outputting the frequency all the time, and would interrupt too much this way.
And, there's another thing that popped in my mind last night. If I understood correctly, the "attachInterrupt" stops any ongoing process and jumps to do something else, and then returns to what was it doing before. If that is the case, wouldn't it interrupt the millis() function? I read somewhere that unlike the PIC MCUs who need 4, atmel's are taking one clock edge to finish one instruction, and if I remember correctly the brief course I had few years back in assembler language, the interrupting and incrementing the counter would take 3 instructions, jmp to line where it increments, incrementation, and jmp to line where it left. That would last ~0.1875us @ 16MHz clock speed. So, as the input frequency gets higher, the higher the error gets. For example, if I was to measure 4MHz signal in a 500ms interval, that would be 210^6 interrupts, squeezing in additional 210^60.187510^-6= 375mS, making it highly inaccurate at higher frequencies. Does this make any sense?
If this is true, and millis() does get paused, I could easily calculate the error according to the counter.
If it indeed takes 3 instructions for interrupt + increment, then I could say that the max measurable frequency could be 4MHz with the current setup (since 3 instructions @16MHz would be completed before the next rising edge of 4MHz signal). The sample rate would change with frequency, but that is not critical.
PS: sorry if I have some grammatical errors or something, English is not my native language
And thanks again for helping me!
EDIT:
Btw, if I was to use global variable "counter" for storing the 1/4th of the frequency, I guess that "int" var type won't be sufficient for high frequency/numbers, right? What type should I use for it?
attachInterrupt doesn't actually interrupt anything. It merely puts the address of the chosen ISR (interrupt service routine) into the interrupt table. You usually do this during setup.
When the interrupt occurs it will call the ISR (the one you gave it in the attachInterrupt call).
Millis are still updated within ISRs. There is a minor bug that occurs if millis clocks over to zero within the ISR and then you attempt to read it (still within the ISR) but the actual counter that millis relies on continues running throughout.
The overhead of stacking return calls etc.. will produce a small overhead in dealing with an interrupt so it has some bearing on the overall fequency you can expect to cope with. Perhaps if you get this built you can let us know what frequency you get to before running into the wall.
Your latest function would work (with some minor debugging) but it still effectively causes a closed loop to wait for the measurement. Which somewhat negates the point of having interrupts doing your counting.
Isn't this simpler
unsigned int lastCount=0;
void loop()
{
unsigned int t=millis()
if ( (t - lastCount ) >= 1000 )
{freqCount = counter1;
counter1 = 0;
}
Then when you want the frequency it's just sitting there in the variable freqCount. No need for a function, just read the variable and display it.
Huh, well that is great to hear! Thought for a second that it'll get even more complicated ![]()
I'm going to the store to buy missing parts, then I'll breadboard the whole thing and let you know what happens. ![]()
KenF:
unsigned int lastCount=0;
void loop()
{
unsigned int t=millis()
if ( (t - lastCount ) >= 1000 )
{freqCount = counter1;
counter1 = 0;
}
Then when you want the frequency it's just sitting there in the variable freqCount. No need for a function, just read the variable and display it.
But what's the point of "lastCount" variable when it's zero all the time? Couldn't I eliminate the whole thing?
And I see what you mean, but I thought functions are there to make the code look simpler ![]()
Garurumon:
But what's the point of "lastCount" variable when it's zero all the time? Couldn't I eliminate the whole thing?
It's NOT zero all the time. It gets set to zero when your program first runs. within your loop after one second, it gets updated with THAT time. then a second later it gets updated AGAIN. So it always holds the time that the last count was copied from counter1 to freqCount.
Hmmm, should I add the lastUpdate=t; in the brackets?
Btw, with the following code, I get nothing on the second line, not even a zero, why could that be?
/* L/C/F meter by Milos G. */
#include <LiquidCrystal.h> // includes LCD library
#define Pi 3.14159 // math.Pi
LiquidCrystal lcd(8, 9, 10, 11, 12, 13); // LCD pins
// choosing pins for selecting mode via rotary encoder
const int CONF1 = A0;
const int CONF2 = A1;
const int CONF3 = A2;
int state = 0; // state of the rotary switch
const int REED = 4; // pin 1 will controll the reed relay, it's also a PWM pin
const int Ccal = pow(10, -9); // value of an accurate capacitor
int Fcal1 = 0, Fcal2 = 0; // values before and after including Ccal
const int LCFIN = 3; // pin that measures L/C related stuff
float counter = 0; // global variable that increments during a given time interval, LC
//unsigned long lastUpdate = 0;
const int UU1 = 1, UU2 = 5, UU3 = 6, UU4 = 7, UU5 = A3, UU6 = A4, UU7 = A5, UU8 = 0, UU9 = 3; // unused pins
void setup() {
// set LCD's dimensions:
lcd.begin(16, 2);
// setting up pin modes
pinMode(CONF1, INPUT_PULLUP); // if LOW -> we measure L
pinMode(CONF2, INPUT_PULLUP); // if LOW -> we measure C
pinMode(CONF3, INPUT_PULLUP); // if LOW -> we measure F, in other cases we measure HF
pinMode(REED, OUTPUT); // controlles the reed relay
pinMode(LCFIN, INPUT);
// terminating unused pins:
pinMode(UU1, INPUT_PULLUP);
pinMode(UU2, INPUT_PULLUP);
pinMode(UU3, INPUT_PULLUP);
pinMode(UU4, INPUT_PULLUP);
pinMode(UU5, INPUT_PULLUP);
pinMode(UU6, INPUT_PULLUP);
pinMode(UU7, INPUT_PULLUP);
pinMode(UU8, INPUT_PULLUP);
pinMode(UU9, INPUT_PULLUP);
// interrupts
attachInterrupt(0, count, RISING); // pin 2 actually, in0 pin
// determining the position of the selector:
selector();
}
void loop() {
int old_state = state; // so we can compare old and new one, to see if it changed
// We have 3 situations
switch (state) {
case 1:
{
selector();
if(old_state!=state)
{
calibration();
break; // breaks if the switch used
}
lcd.setCursor(0, 0);
lcd.print("L:");
float L = inductance() * 1000000;
// writing the Lx on the screen
lcd.setCursor(0, 1);
lcd.print(L);
lcd.print("uH");
break;
}
case 2:
{
selector();
if(old_state!=state)
{
calibration();
break; // breaks if the switch used
}
lcd.setCursor(0, 0);
lcd.print("C:");
float C = capacitance() * 1000000;
lcd.setCursor(0, 1);
lcd.print(C);
lcd.print("uF");
break;
}
case 3:
{
selector();
if(old_state!=state)
{
calibration();
break; // breaks if the switch used
}
lcd.setCursor(0, 0);
lcd.print("F:");
lcd.setCursor(0, 1);
lcd.print(frequency());
lcd.print("Hz");
break;
}
}
}
// LC meter calibration
void calibration() {
if (state == 1 || state == 2) {
Fcal1 = frequency();
digitalWrite(REED, HIGH);
Fcal2 = frequency();
digitalWrite(REED, LOW);
}
}
// function that calculates Lx
float inductance() {
float L = 0;
float F = frequency();
L = ( pow(Fcal1/F, 2) - 1 ) * ( pow(Fcal1/Fcal2, 2) - 1) * 1/Ccal * pow(1/(2*Pi*Fcal1), 2);
return L;
}
// function that calculates Cx
float capacitance() {
float C = 0;
float F = frequency();
C = ( pow(Fcal1/F, 2) - 1 ) / ( pow(Fcal1/Fcal2, 2) - 1 ) * Ccal;
return C;
}
// function that reads selector's position
void selector() {
if (digitalRead(CONF1) == LOW)
state = 1;
else if (digitalRead(CONF2) == LOW)
state = 2;
else
state = 3;
}
// frequency counter interface, LC
float frequency() {
float freq = 0;
unsigned long time = millis();
counter = 0;
do {
freq = counter;
} while (time<=200);
counter = 0;
freq *= 5;
delay(15);
return freq;
}
// frequency counter
void count() {
counter++;
}
Here are some pics of the problem and hardware setup: http://imgur.com/a/f9zUa
Garurumon:
Btw, with the following code, I get nothing on the second line, not even a zero, why could that be?
state is initialised to 0 (on line 11)
Within the loop function there is no case that applies to 0. So selector() never gets called. So the switch never gets looked at and state never changes.