Greenville, IL
Offline
Edison Member
Karma: 11
Posts: 1287
Warning Novice on board! 0 to 1 chance of errors!
|
 |
« on: August 01, 2011, 09:06:55 am » |
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.
Thanks, Mark
|
|
|
|
|
Logged
|
|
|
|
|
London
Offline
Faraday Member
Karma: 6
Posts: 6226
Have fun!
|
 |
« Reply #1 on: August 01, 2011, 09:45:40 am » |
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
|
|
|
|
|
Logged
|
|
|
|
|
Greenville, IL
Offline
Edison Member
Karma: 11
Posts: 1287
Warning Novice on board! 0 to 1 chance of errors!
|
 |
« Reply #2 on: August 01, 2011, 11:12:20 am » |
Thank you very much for your code! 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. Thanks for the help, Mark
|
|
|
|
|
Logged
|
|
|
|
|
London
Offline
Faraday Member
Karma: 6
Posts: 6226
Have fun!
|
 |
« Reply #3 on: August 01, 2011, 11:41:24 am » |
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?
|
|
|
|
|
Logged
|
|
|
|
|
|
|
Greenville, IL
Offline
Edison Member
Karma: 11
Posts: 1287
Warning Novice on board! 0 to 1 chance of errors!
|
 |
« Reply #5 on: August 01, 2011, 11:54:28 am » |
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. Thank you, Mark
|
|
|
|
|
Logged
|
|
|
|
|
Greenville, IL
Offline
Edison Member
Karma: 11
Posts: 1287
Warning Novice on board! 0 to 1 chance of errors!
|
 |
« Reply #6 on: August 01, 2011, 12:18:02 pm » |
I found the datasheet you mentioned and the bit about the timer. The datasheet has 500+ pages and lots of things that will be helpful in the long run. http://www.atmel.com/dyn/resources/prod_documents/doc8271.pdf I will run some experiments and see how I do. Mark
|
|
|
|
|
Logged
|
|
|
|
|
Greenville, IL
Offline
Edison Member
Karma: 11
Posts: 1287
Warning Novice on board! 0 to 1 chance of errors!
|
 |
« Reply #7 on: August 01, 2011, 09:27:43 pm » |
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
|
|
|
|
|
Logged
|
|
|
|
|
London
Offline
Faraday Member
Karma: 6
Posts: 6226
Have fun!
|
 |
« Reply #8 on: August 02, 2011, 01:40:53 am » |
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 } }
|
|
|
|
|
Logged
|
|
|
|
|
Greenville, IL
Offline
Edison Member
Karma: 11
Posts: 1287
Warning Novice on board! 0 to 1 chance of errors!
|
 |
« Reply #9 on: August 02, 2011, 08:20:04 am » |
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 will experiment with it tonight!
Mark
|
|
|
|
|
Logged
|
|
|
|
|
London
Offline
Faraday Member
Karma: 6
Posts: 6226
Have fun!
|
 |
« Reply #10 on: August 02, 2011, 08:50:10 am » |
a minor tweak, the following two variables are no longer used and can be deleted: unsigned long gatetime = 0; int timeset = 1;
have fun!
|
|
|
|
|
Logged
|
|
|
|
|
Greenville, IL
Offline
Edison Member
Karma: 11
Posts: 1287
Warning Novice on board! 0 to 1 chance of errors!
|
 |
« Reply #11 on: August 03, 2011, 07:05:22 pm » |
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
|
|
|
|
|
Logged
|
|
|
|
|
London
Offline
Faraday Member
Karma: 6
Posts: 6226
Have fun!
|
 |
« Reply #12 on: August 03, 2011, 09:20:12 pm » |
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 } }
|
|
|
|
|
Logged
|
|
|
|
|
Greenville, IL
Offline
Edison Member
Karma: 11
Posts: 1287
Warning Novice on board! 0 to 1 chance of errors!
|
 |
« Reply #13 on: August 04, 2011, 06:23:03 am » |
Thank you Mem for the code and continuing helping with this process!
Mark
|
|
|
|
|
Logged
|
|
|
|
|
Greenville, IL
Offline
Edison Member
Karma: 11
Posts: 1287
Warning Novice on board! 0 to 1 chance of errors!
|
 |
« Reply #14 on: August 05, 2011, 05:21:48 pm » |
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.
Thanks Mem! You really helped a ton! Mark
|
|
|
|
|
Logged
|
|
|
|
|
|