I am wanting to have a pulse counter that can read down to 10hz and up around 24khz. I would like it to be ISP, IC2, or with minimal wires to Uno. My input signals will be 0-5V. I would like it to operate with a 5V supply also. I plan to put it in a socket or through-hole not surface mount.
The solution with the minimum number of wires does not require any external chip. You can use the 16 bit hardware timer to count pulses. Arduino does not provide a simple interface to this but I have used the following code to count up to 64k pulses:
// 16 bit timer defines added by mem to enable redifining the timer used
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#if defined(__AVR_ATmega1280__)
#define TCCRnA TCCR5A
#define TCCRnB TCCR5B
#define TCNTn TCNT5
#define TIFRn TIFR5
#define TOVn TOV5
#else
#define TCCRnA TCCR1A
#define TCCRnB TCCR1B
#define TCNTn TCNT1
#define TIFRn TIFR1
#define TOVn TOV1
#endif
void startCount() {
// hardware counter setup ( refer atmega168.pdf chapter 16-bit counter1)
TCCRnA=0; // reset timer/countern control register A
TCCRnB=0; // reset timer/countern control register A
TCNTn=0; // counter value = 0
// set timer/counter1 hardware as counter , counts events on pin Tn ( arduino pin 5 on 168, pin 47 on Mega )
// normal mode, wgm10 .. wgm13 = 0
sbi (TCCRnB ,CS10); // External clock source on Tn pin. Clock on rising edge.
sbi (TCCRnB ,CS11);
sbi (TCCRnB ,CS12);
TCCRnB = TCCRnB | 7; // Counter Clock source = pin Tn , start counting now
}
unsigned int getCount(){
unsigned int count;
TCCRnB = TCCRnB & ~7; // Gate Off / Counter Tn stopped
count = TCNTn;
TCCRnB = TCCRnB | 7; // Counter Clock source = pin Tn , start counting now
}
void setup(){
}
void loop(){
}
Connect the pulse input to Uno pin 5 (make sure the peak is not greater than 5 volts).
startCount() resets the timer and starts
Note that the 16 bit timer is used by the Servo library and for PWM on pins 9 and 10 so this will only work if you don't need those capabilities
Are the counted pulses stored in int getCount? Also how much time is a sample of the pulses, for instance does it sample for .5 second, or .25 seconds?
So, if I wanted to watch this on my monitor would I use this code?
Serial.print(getcount);
startCount(); //reset counter for next sample
If your code is based off of some other examples that you can mention, I will look at them too so, that I can understand it in more detail.
the sketch is based on standard code to access the 16 bit counter inside the Arduino chip. The technical details are in the datasheet section covering the 16 bit counter unit (section 16.5 in the latest version of the ATmega328p datasheet)
The count is stored in the TCNT1 register in the Uno chip. this line access this value:
count = TCNTn;
Note that when compiled for the Uno the compiler will substitute TCNT1 for TCNTn. It looks a little complicated because the same code can be used on bot a Uno and a Mega even though these chips use different registers for counting.
This is just a counter, your sketch would control the length of the sample time by adjusting the duration between calling startCount and getCount. How were you intending to control the count duration if you used an external counter?
the library linked below is probably easier to use than the code posted above. It uses a similar technique for counting and has implemented use of another timer to gate the counter if that is what you want to do.
mem:
This is just a counter, your sketch would control the length of the sample time by adjusting the duration between calling startCount and getCount. How were you intending to control the count duration if you used an external counter?
Thanks again for the additional information and the link!
I was not sure how I was going to control sample times. I have done some looking for chips but, I had not figured out the control aspects of them yet.
I think calling startCount and getCount should be easy enough using micros() for precision so, I can handle that.
After some experimenting I have some results! I am enjoying learning how to use the code! I also have a couple questions.
I used a 4.56kohm resistor from pin 5 then connected it off and on to ground. If I keep pin 5 grounded through the resistor my result was "5" pulses for a 2 second sample time. However, if I worked the ground resistor to simulate pulses I got numbers that look acceptable for now.
Could my resistor value be too large? Besides needing to put the Serial.print messages outside any of the timers and removing the delay do you see anything I might have done wrong?
Here is my version of your code.
// 16 bit timer defines added by mem to enable redifining the timer used
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#if defined(__AVR_ATmega1280__)
#define TCCRnA TCCR5A
#define TCCRnB TCCR5B
#define TCNTn TCNT5
#define TIFRn TIFR5
#define TOVn TOV5
#else
#define TCCRnA TCCR1A
#define TCCRnB TCCR1B
#define TCNTn TCNT1
#define TIFRn TIFR1
#define TOVn TOV1
#endif
void startCount() {
// hardware counter setup ( refer atmega168.pdf chapter 16-bit counter1)
TCCRnA=0; // reset timer/countern control register A
TCCRnB=0; // reset timer/countern control register A
TCNTn=0; // counter value = 0
// set timer/counter1 hardware as counter , counts events on pin Tn ( arduino pin 5 on 168, pin 47 on Mega )
// normal mode, wgm10 .. wgm13 = 0
sbi (TCCRnB ,CS10); // External clock source on Tn pin. Clock on rising edge.
sbi (TCCRnB ,CS11);
sbi (TCCRnB ,CS12);
TCCRnB = TCCRnB | 7; // Counter Clock source = pin Tn , start counting now
}
unsigned int getCount(){
unsigned int count;
TCCRnB = TCCRnB & ~7; // Gate Off / Counter Tn stopped
count = TCNTn;
TCCRnB = TCCRnB | 7; // Counter Clock source = pin Tn , start counting now
}
unsigned long opengate = 0;
unsigned long gatetime = 0;
unsigned long sampletime = 2000;
unsigned int pulses = 0;
int timeset = 1;
void setup(){
Serial.begin(9600);
Serial.print("hello ");
}
void loop(){
if (timeset == 1){
opengate = millis();
}
gatetime = millis();
timeset = 0;
if (gatetime - opengate >= sampletime) {
getCount();
pulses = TCNT1;
Serial.println(" here is the count ");
Serial.print(pulses);
delay(2000);
timeset = 1;
startCount();
}
}
Here are my results.
hello here is the count
78 here is the count
5 here is the count
5 here is the count
5 here is the count
152 here is the count
5 here is the count
5 here is the count
11 here is the count
9 here is the count
10 here is the count
12 here is the count
5 here is the count
5 here is the count
5 here is the count
5 here is the count
5 here is the count
6 here is the count
9 here is the count
5 here is the count
6 here is the count
9 here is the count
13 here is the count
23 here is the count
5 here is the count
5 here is the count
105 here is the count
5 here is the count
5 here is the count
5 here is the count
384 here is the count
100 here is the count
762 here is the count
5 here is the count
That's coming along. The sketch below has a simplified gate duration check and enables the internal pull-up resistor on the input pin. see how that goes:
// 16 bit timer defines added by mem to enable redifining the timer used
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#if defined(__AVR_ATmega1280__)
#define TCCRnA TCCR5A
#define TCCRnB TCCR5B
#define TCNTn TCNT5
#define TIFRn TIFR5
#define TOVn TOV5
#define pulseInPin 47
#else
#define TCCRnA TCCR1A
#define TCCRnB TCCR1B
#define TCNTn TCNT1
#define TIFRn TIFR1
#define TOVn TOV1
#define pulseInPin 5
#endif
void startCount() {
// hardware counter setup ( refer atmega168.pdf chapter 16-bit counter1)
TCCRnA=0; // reset timer/countern control register A
TCCRnB=0; // reset timer/countern control register A
TCNTn=0; // counter value = 0
// set timer/counter1 hardware as counter , counts events on pin Tn ( arduino pin 5 on 168, pin 47 on Mega )
// normal mode, wgm10 .. wgm13 = 0
sbi (TCCRnB ,CS10); // External clock source on Tn pin. Clock on rising edge.
sbi (TCCRnB ,CS11);
sbi (TCCRnB ,CS12);
TCCRnB = TCCRnB | 7; // Counter Clock source = pin Tn , start counting now
}
unsigned int getCount(){
unsigned int count;
TCCRnB = TCCRnB & ~7; // Gate Off / Counter Tn stopped
count = TCNTn;
TCCRnB = TCCRnB | 7; // Counter Clock source = pin Tn , start counting now
}
unsigned long opengate = 0;
unsigned long gatetime = 0;
unsigned long sampletime = 2000;
unsigned int pulses = 0;
int timeset = 1;
void setup(){
digitalWrite(pulseInPin, HIGH); // turn on pull-ups so pin is not floating
Serial.begin(9600);
Serial.print("hello ");
opengate = millis(); // open the gate ready for first count
}
void loop(){
// check if sampletime has elapsed since gate was opened
if( millis()- opengate >= sampletime) {
pulses = getCount();
Serial.println(" here is the count ");
Serial.print(pulses);
delay(2000);
startCount();
opengate = millis(); // start over with new gate time
}
}
Thank you, for the improvements! My readings must have been off because the floating pin. I like how you simplified the duration check, I will have to remember that.
I ran your code on my Uno and for some reason I could not get any counts. I think there might be a problem in the loop but, I could not find it. I set-up a Mega "for a Frequency generator" with a multi-tone sketch to get samples from and with your sketch mixed with my sketch I did get results. However, it seems the minimum pulse count is 5. I get reasonable pulse counts while the tones are changing but, when the tones stop I get "5" until I restart the tones on the Mega.
Here is the code that is working at the moment.
digitalWrite(13, HIGH);
if (timeset == 1){
opengate = millis();
}
gatetime = millis();
timeset = 0;
if (gatetime - opengate >= sampletime) {
getCount();
pulses = TCNT1;
Serial.println(" here is the count ");
Serial.print(pulses);
delay(2000);
timeset = 1;
startCount();
}
}
Here is the result on the monitor.
hello here is the count
220 here is the count
849 here is the count
500 here is the count
546 here is the count
673 here is the count
1119 here is the count
1129 here is the count
707 here is the count
1085 here is the count
1288 here is the count
1330 here is the count
5 here is the count
5 here is the count
5 here is the count
5 here is the count
5 here is the count
5 here is the count
5
This version should correctly return the count. I also moved the initialization code into a separate function as this can be called once only from setup.
// 16 bit timer defines added by mem to enable redifining the timer used
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#if defined(__AVR_ATmega1280__)
#define TCCRnA TCCR5A
#define TCCRnB TCCR5B
#define TCNTn TCNT5
#define TIFRn TIFR5
#define TOVn TOV5
#define pulseInPin 47
#else
#define TCCRnA TCCR1A
#define TCCRnB TCCR1B
#define TCNTn TCNT1
#define TIFRn TIFR1
#define TOVn TOV1
#define pulseInPin 5
#endif
void initCounter() {
// hardware counter setup ( refer atmega168.pdf chapter 16-bit counter1)
TCCRnA=0; // reset timer/countern control register A
TCCRnB=0; // reset timer/countern control register A
// set timer/counter1 hardware as counter , counts events on pin Tn ( arduino pin 5 on 168, pin 47 on Mega )
// normal mode, wgm10 .. wgm13 = 0
sbi (TCCRnB ,CS10); // External clock source on Tn pin. Clock on rising edge.
sbi (TCCRnB ,CS11);
sbi (TCCRnB ,CS12);
TCNTn = 0;
}
void startCount() {
TCNTn=0; // counter value = 0
TCCRnB = TCCRnB | 7; // Counter Clock source = pin Tn , start counting now
}
unsigned int getCount(){
TCCRnB = TCCRnB & ~7; // Gate Off / Counter Tn stopped
return TCNTn;
}
unsigned long opengate = 0; // the time the gate is opened
unsigned long sampletime = 2000; // the pulse counting duration
void setup(){
digitalWrite(pulseInPin, HIGH); // turn on pull-ups so pin is not floating
Serial.begin(9600);
Serial.print("Gate time is ");
Serial.print(sampletime);
Serial.println(" milliseconds");
initCounter();
startCount();
opengate = millis(); // open the gate ready for first count
}
void loop(){
// check if sampletime has elapsed since gate was opened
if( millis()- opengate >= sampletime) {
unsigned int pulses = getCount();
Serial.println(pulses);
startCount();
opengate = millis(); // start over with new gate time
}
}
The last code works perfectly! It has a minimum count of 0 when no pulses are given and it seems to be reading correctly with changing frequencies. I have lowered the gatetime to 100ms and it works well that way also!
I will have to test on other things to find the best gatetime for each case.