NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Need help with frequency counter

February 27, 2012
by nismotony
nismotony's Avatar

Hi guys, I'm trying to build a frequency counter that will measure 10Hz - 30kHz. I have yet to experiment with the timers and counters so this is going to be a good learning project for me. I have read the tutorial and have seen the app note from Atmel. It seems that I need to be able to use the T0 or T1 pins on the ATMEGA168. This are occupied by the nerdkits LCD though. Any suggestions? This project has really been kicking my butt, I'm an analog design engineer. Any help or code examples will be much appreciated.

Tony

February 27, 2012
by 6ofhalfdozen
6ofhalfdozen's Avatar

Hiya Tony,

I made something similar a while back for a readout on a pulse-out flow meter. I don't have my code on this computer, else I would post it for you.

I think I started with the real time clock code and removed most of the commands sending data to the LCD. From there, I added pin change interrupt code for PC4 (?, one of the unused pins near the temp sensor). Since my pulses could be less than .1hz, I let the real time clock run for 15 seconds before dumping the variable to the calculations (pulses/timercounts) and LCD output. I needed a little tweeking with my timing and calculation to get it where I needed it, but it was pretty quick and easy. I used a 555 to double check my higher frequency results, and it made life a lot easier than spinning that flow meter all the way up. I don't know if that will work for you, but hopefully points you in a right-ish direction.

February 27, 2012
by nismotony
nismotony's Avatar

Thank you for the advice. So maybe I should look into the nerdkits lcd.c and see if I can modify it? I'd like to see your code too.

Thanks

February 28, 2012
by nismotony
nismotony's Avatar

Ok, here is where I am now. I've taken code from the nerdkits real time clock and code from the obdII scanner and hacked them together. At this point I'm just trying to have a running real time clock and then an external interrupt that will measure pin change. Maybe with this I can measure the time between cycles of the pin. Problem is my code isn't working, I can't get "count" to increment when I manually short pin PC5 to ground. Here is my code:

// realtimeclock1.c
// for NerdKits with ATmega168
// mrobbins@mit.edu

#define F_CPU 14745600

#include <stdio.h>
#include <stdlib.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:

void realtimeclock_setup() {
  // setup Timer0:
  // CTC (Clear Timer on Compare Match mode)
 // TOP set by OCR0A register
  TCCR0A |= (1<<WGM01);
 // clocked from CLK/1024
 // which is 14745600/1024, or 14400 increments per second
 TCCR0B |= (1<<CS02) | (1<<CS00);
 // set TOP to 143
 // because it counts 0, 1, 2, ... 142, 143, 0, 1, 2 ...
 // so 0 through 143 equals 144 events
 OCR0A = 143;
// enable interrupt on compare event
// (14400 / 144 = 100 per second)
  TIMSK0 |= (1<<OCIE0A);
}

volatile int32_t the_time;
volatile int32_t count;

 void reader_init() {

 PORTC |= (1<<PC5);
 // For reader:
 // enable pin change interrupt PCINT13
 PCICR |= (1<<PCIE1);
 PCMSK1 |= (1<<PCINT13);

 }

 SIGNAL(SIG_OUTPUT_COMPARE0A)  {
// when Timer0 gets to its Output Compare value,
// one one-hundredth of a second has elapsed (0.01 seconds).
 the_time++;
 }

 ISR(PCINT1_vect){        
 count++;
      }

 int main() {
 realtimeclock_setup();
 reader_init();  
 lcd_init();
 FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
 lcd_home();

  sei();

  while(1) {
  lcd_home();
  fprintf_P(&lcd_stream, PSTR("%16.2f sec"), (double) the_time / 100.0);
lcd_line_two();
fprintf_P(&lcd_stream, PSTR("%16.2f count"), (count));

  }

  return 0;
 }
February 28, 2012
by 6ofhalfdozen
6ofhalfdozen's Avatar

Tony,

I still can't find my code, it might be on my other other computer. sigh. Hopefully I can post it tonight.

Anyhow, your cut and paste code looks decent. The only major thing that I see is that you don't setup PC5 as an input in the DDR. I am fairly certain this is why your count is not changing. Also, just toggling to ground might not be enough. I would suggest running a 1k resistor from PC5 to an empty spot on the breadboard and using a jumper wire from that row to toggle back and forth from Vcc and ground which should show up better as a clear toggle to the mcu.

March 01, 2012
by nismotony
nismotony's Avatar

6, thanks. I'm going to give that a try tonight.

March 02, 2012
by 6ofhalfdozen
6ofhalfdozen's Avatar

Tony,

I am having a nightmere of a time trying to find where I put that code. I will try to spend a bit more time looking for it, or just recreating it from my notes this weekend. I haven't forgotten, just no luck so far.. All the same, it looks like you are making a decent bit of progress with your project!

March 05, 2012
by 6ofhalfdozen
6ofhalfdozen's Avatar

Tony,

Finally, I found it!! 3 computers and 6 flash drives later.. anyhow, below is my code for the collective viewing. A couple tidbits in case you decide to try and use it.

A. To fit into the enclosure for this, I had to cut back to a 8x2 character LCD. So the LCD lines are very short and only two. If I could have used a 20x4 LCD, I would have added more info, but not enough space in the box. Adjust your code accordingly.

B. I hooked this up to a roots flow meter which output 2 pulses per rotation, so in line 116 there is a " / 2" which is used for this specific application and might not be needed in yours. Adjust your code accordingly.

C. Because the roots meter can send outputs less than 1 pulse per second, I have the NK counting the number of pulses in 15 seconds. This is the whole point of the if/then loop starting on line 114. This let me measure low flows which the system sometimes sees. Anyhow, this is setup specifically for this device and if you are expecting drastically faster or slower pulse rates you will need to adjust this for your needs or just remove it.

D. While it may not work well in the higher 35kHz and up range, from 0.02-5000 pulses per second it performed very well.

//This is the PulseOutput Roots Meter flowrate program
// RootsPulse.c
// for NerdKits with ATmega168 
// Based on keyboard.c and realtimeclock.c code by hevans@nerdkits.edu 
// compiled/Frankenstein-ed together and modified for attempted use by 6ofhalfdozen
// any errors are mine and any thanks go to the NK guys
#define F_CPU 14745600 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

//declaring 32 bit int for holding pulses per 15 secs
volatile uint32_t ppfs;
//declaring 16bit int for holding rpm value
volatile uint16_t rpm;

  // PIN DEFINITIONS:

   // 
   // PC4 -- Pulse Input

   void realtimeclock_setup() {
   // setup Timer0:
   // CTC (Clear Timer on Compare Match mode)
   // TOP set by OCR0A register
   TCCR0A |= (1<<WGM01);
   // clocked from CLK/1024
   // which is 14745600/1024, or 14400 increments per second
   TCCR0B |= (1<<CS02) | (1<<CS00);
   // set TOP to 143
   // because it counts 0, 1, 2, ... 142, 143, 0, 1, 2 ...
   // so 0 through 143 equals 14400 events
   OCR0A = 143;
   // enable interrupt on compare event
   // (14400 / 144 = 100 per second)
   TIMSK0 |= (1<<OCIE0A);
   }
   // the_time will store the elapsed time
   // in hundredths of a second.
   // (100 = 1 second)
   // 
   // note that this will overflow in approximately 248 days!
   //
   // This variable is marked "volatile" because it is modified
   // by an interrupt handler.  Without the "volatile" marking,
   // the compiler might just assume that it doesn't change in 
   // the flow of any given function (if the compiler doesn't
   // see any code in that function modifying it -- sounds 
   // reasonable, normally!).
   //
   // But with "volatile", it will always read it from memory 
   // instead of making that assumption.
   volatile int32_t the_time;

   SIGNAL(SIG_OUTPUT_COMPARE0A) {
   // when Timer0 gets to its Output Compare value,
   // one one-hundredth of a second has elapsed (0.01 seconds).
    the_time++;
   }
   void init_pulses(){
   //turn on pull up resistor
   //PORTC |=(1<<PC4);

   //make PC4 input
   DDRC &= ~(1<<PC4);

//enble interrupt on pin change set 1
PCICR |= (1<<PCIE1);

    //setup for only PC4 active for interupt
    PCMSK1 |= (1<<PCINT12);
}

ISR(PCINT1_vect){
//meter runs on high to low transistion so check for high
//if(PINC & ((1<<PC4))){
//increment value of pulses per fifteen seconds
ppfs++;

//return;

}

int main() {

//declaring 16bit int for holding last pulse count
volatile uint16_t oldp;

  realtimeclock_setup();

  // init lcd
  lcd_init();
  //FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // init serial port
  uart_init();
 FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
 stdin = stdout = &uart_stream;

  init_pulses();

  // turn on interrupt handler
  sei();

  while(1) {

  if (the_time == (1500)){

  rpm = ((rpm  + ppfs) / 2 );

    //DDRC|= (1<<PC5);
    //delay_ms(25);
    //DDRC|= ~(1<<PC5);
    lcd_home();
    lcd_line_one();
    lcd_write_string(PSTR("P"));
    lcd_write_int16(ppfs);
    lcd_write_string(PSTR(" "));
    lcd_write_int16(oldp);

    //lcd_line_two();
    //lcd_write_string(PSTR("R:      "));
    lcd_line_two();
    lcd_write_string(PSTR("R: "));
    lcd_write_int16(rpm);

    oldp = ppfs;
    ppfs = 0;
    the_time = 0;
    return;

    } else {

    lcd_home();
    lcd_line_one();
    lcd_write_string(PSTR("P"));
    lcd_write_int16(ppfs);
    lcd_write_string(PSTR(" "));
    lcd_write_int16(oldp);
   lcd_write_string(PSTR(" "));
    return;
    }

  }

  return 0;
         }

Post a Reply

Please log in to post a reply.

Did you know that interrupts can cause problems if you're not careful about timing and memory access? Learn more...