That's the little brother of the 4040, so the wiring and use would be pretty much the same as what I suggested.
If you run it at a lower clock rate (10-20 MHz) then you could probably do it on breadboard, too.
@scorp84
I improved on my previous version, I rewrote the counting loop from a while do into a do while.
it seems to measure pulsewidths now in 5/16th (< 1/3) of a micro. Please confirm if my math / code is correct.
measure the count++ loop sketch
//
// FILE: PulseWidthMeter.pde
// AUTHOR: Rob Tillaart
// DATE: 2012-mar-28
//
// LINK: http://arduino.cc/forum/index.php?action=post;topic=96971.0
//
unsigned int count = 0;
void setup()
{
Serial.begin(9600);
Serial.println("pulse width meter 0.1");
pinMode(3, INPUT);
}
void loop()
{
count = 0;
while ((PIND & B00001000) == B00000000); // wait for HIGH
unsigned long start = micros();
[color=red] do count++;
while (PIND & B00001000); // start counting until LOW[/color]
unsigned long stop = micros();
Serial.print("CNT: ");
Serial.println(count, DEC);
Serial.print(stop-start, DEC);
Serial.println(" microseconds ");
Serial.print((1.0*count)/(stop-start), 3);
Serial.println(" count per microseconds ");
delay(1000);
}
Output:
CNT: 9264
2916 microseconds
3.177 count per microseconds
CNT: 27698
8712 microseconds
3.179 count per microseconds
CNT: 22776
7164 microseconds
3.179 count per microseconds
CNT: 11891
3744 microseconds
3.176 count per microseconds
CNT: 9634
3032 microseconds
3.177 count per microseconds
results in ~3.178 counts per usec. => 1/3.178 = 0.3147 uSec/count
this implies 0.3147 x 16 = 5.035 => 5 instructions per loop instead of 6 in the previous version.
The 0.035 indicates a 0.7% error margin which is not too bad (probable causes the rounding of micros() 0.1-0.2% + crystal inaccuracy?).
[please confirm these measurements & math]
This results in a new 0.3 version of the pulseWidthMeter sketch:
//
// FILE: PulseWidthMeter.pde
// AUTHOR: Rob Tillaart
// DATE: 2012-mar-28
//
// LINK: http://arduino.cc/forum/index.php?action=post;topic=96971.0
//
unsigned int count = 0;
void setup()
{
Serial.begin(9600);
Serial.println("pulse width meter 0.3");
pinMode(3, INPUT);
}
void loop()
{
count = 0;
while ((PIND & B00001000) == B00000000); // wait for HIGH
do count++;
while (PIND & B00001000); // start counting until LOW
float usec = 1.0 * count * 5/16;
Serial.print("CNT: ");
Serial.println(count, DEC);
Serial.print(" equals ");
Serial.print(usec, 2);
Serial.println(" microseconds.");
delay(1000);
}
Thanks alot and actually I am back to my room and I would check it now tomorrow and then would confirm you, thanks again
This is my new version, now using Timer1 to count 1/16 microseconds. As timer1 is a 16bit timer it counts to 65535 before it overflows so it can do 4096 usec max. That would suit your needs.
If it overflows it calls an interrupt and in the IRQ another int is incremented. These two (Timer1 counter + IRQ counter) together make effectively a long, resulting in max time of around 4 minutes. The code is not 100% tested and does sometimes strange - especially first pulse - but the results are promissing.
I have to "fiddle" which settings must be between the two while loops, and which can be before the first one.
Give it a try.
//
// FILE: PulseWidthMeter.pde
// AUTHOR: Rob Tillaart
// DATE: 2012-mar-28
//
// LINK: http://arduino.cc/forum/index.php?action=post;topic=96971.0
//
#include <avr/io.h>
#include <avr/interrupt.h>
volatile unsigned int cnt = 0;
void setup()
{
Serial.begin(38400);
Serial.println("PulseWidthMeter (timer1) 0.1");
pinMode(3, INPUT);
}
void loop()
{
// initialize Timer1
cli();
// reset counters
cnt = 0;
TCNT1 = 0;
// reset registers
TCCR1A = 0;
TCCR1B = 0;
// wait for HIGH
while ((PIND & B00001000) == B00000000);
// enable Timer1 overflow interrupt:
TIMSK1 = (1 << TOIE1);
// Set CS10 bit so timer runs at clock speed: 16 MHz
TCCR1B |= (1 << CS10);
// enable global interrupts:
sei();
// keep counting until LOW
while ((PIND & B00001000) == B00001000);
// stop IRQ's
cli();
// Read the counters and convert to long
unsigned int x = TCNT1; // work copy
unsigned long total = cnt * 65535L + x;
float usec = (1.0 * total) / 16;
// Display values
Serial.print(total, DEC);
Serial.print(" \t ");
Serial.println(usec, 1);
// Wait a while
delay(1000);
}
// count the overflows in IRQ
ISR(TIMER1_OVF_vect)
{
cnt++;
}
Attached is the output of the image for pulses from 1 to 10 microsecond I applied 1 microsecond pulse width and then changed it into steps upto 10 microsecond
This is strange. Seems like it is triggered around the time the timer overflows. Can't explain it yet
Can you tell more about how you connected things?
which function generator you use ?
Did you use a pull down/up resistor?
Connected gnd's?
@ Rob
I am using a simple function generator and giving the pulses from it and yes I have connected both the grounds of the arduino board. I am applying the pulses to the BNC connector and from there I am giving the pulses to the digital pin 3 (D3) and I am not using pull down/up resistor. But did you also check the code output? Can you too check the output and then make the comparison with mine?
Thanks
Not using a pull down resistor can cause that the line keeps high for a longer period than the pulselength (RC effect ?) giving you results you would not expect.
No time to dive into it as one of my own experiments is not working now ![]()
@ Rob
I have made few changes in the code and now the code is working fine
check it out but it only misses out 1 count I don't know how we can improve that? I mean for 1 microsecond pulse it is doing 15 counts (theoretically 16) can we give a external high clock to the timer of the arduino? The code is given below
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint16_t cnt = 0;
unsigned int pulse_counts = 0;
void setup()
{
Serial.begin(115200);
Serial.println("PulseWidthMeter (timer1) 0.1");
pinMode(3, INPUT);
}
void loop()
{
// initialize Timer1
cli();
// reset counters
cnt = 0;
TCNT1 = 0;
// reset registers
TCCR1A = 0;
TCCR1B = 0;
pulse_counts++;
// wait for HIGH
while ((PIND & B00001000) == B00000000);
// Set CS10 bit so timer runs at clock speed: 16 MHz
TCCR1B |= (1 << CS10);
// enable Timer1 overflow interrupt:
TIMSK1 = (1 << TOIE1);
// enable global interrupts:
// sei();
// keep counting until LOW
while ((PIND & B00001000) == B00001000);
// stop IRQ's
TIMSK1 = 0;
TCCR1B = 0;
// cli();
// Read the counters and convert to long
uint16_t x = TCNT1; // work copy
uint32_t total = (uint32_t)cnt + (uint32_t)x;
float usec = (1.0 * total) / 16;
// Display values
Serial.print(x, DEC);
Serial.print(" \t ");
Serial.print(usec, 2);
Serial.print(" \t ");
Serial.print("Count = ");
Serial.println(pulse_counts);
// Wait a while
// delay(1000);
}
// count the overflows in IRQ
ISR(TIMER1_OVF_vect)
{
cnt++;
}
Good to hear that it works, see you removed the cli()/sei() flags. They are not needed as the TCCR1B starts/stops the timer1. They were a leftovers of my experiments, sorry.
In the first microsecond Timer1 misses a few cpu ticks before it is started as the starting of Timer1 itself takes at least one instruction. That can be added in the formula later. Furthermore the 65535L should be 65536L. This results in the following code:
//
// FILE: PulseWidthMeter.pde
// AUTHOR: Rob Tillaart
// DATE: 2012-apr-01
//
// LINK: http://arduino.cc/forum/index.php?action=post;topic=96971.0
//
#include <avr/io.h>
#include <avr/interrupt.h>
volatile unsigned int count = 0;
unsigned int pulseCounter = 0;
void setup()
{
Serial.begin(38400);
Serial.println("PulseWidthMeter (timer1) 0.2");
pinMode(3, INPUT);
}
void loop()
{
// reset Timer1 registers and the counters (timer must be stopped before reset of counters!.
TCCR1A = 0;
TCCR1B = 0;
count = 0;
TCNT1 = 0;
TIMSK1 = (1 << TOIE1); // enable Timer1 overflow interrupt:
while ((PIND & B00001000) == B00000000); // wait for HIGH
TCCR1B |= (1 << CS10); // Set CS10 bit so timer runs at clock speed: 16 MHz
while ((PIND & B00001000) == B00001000); // wait for low
TCCR1B = 0; // stop counting
pulseCounter ++;
// Read the counters and convert to long
unsigned long total = count * 65536L + TCNT1; // Might need a +1 to correct start/stop calibration.
float usec = (1.0 * total) / 16;
// Display values
Serial.print(total, DEC);
Serial.print(" \t ");
Serial.println(usec, 1);
Serial.print("pulses: ");
Serial.println(pulseCounter);
}
// count the overflows in IRQ
ISR(TIMER1_OVF_vect)
{
count++;
}
Maybe move these statements from loop to setup, to make it absolutely minimal
TCCR1A = 0;
TCCR1B = 0;
TIMSK1 = (1 << TOIE1); // enable Timer1 overflow interrupt:
Is it possible to use this sketch to measure two signals at this same time?
Both signals start in the same moment but they have different period.
