Teensy 4.0 Frequency Counter

Solo

New member
New to this forum and Teensy. The goal, using Teensy 4.0, is a frequency counter that can read frequency up to 35 MHz with an accuracy to the nearest 100 Khz. It should be reasonably responsive to frequency changes as it's reading the frequency resulting from someone turning a knob on an instrument. I'm not an accomplished C programmer and had no Teensy knowledge other than some basic capacilities, so depended heavily on the CoPilot AI program to get to the point I'm at now.

I'm using the FreqCount library and counting the injected signal using pin 9 of the Teensy. The issue at the moment is getting the counter to read to the nearest 100 kHz number. The problem seems to be that the read is not returning enough digits to accomplish that. Also need to work on the calibration correction as the error seems to be not a constant value but rather a percentage of the frequency. Any and all comments and suggestions are welcome.

At this point, the Teensy is able to measure the frequency of an input down to around 300mv, either sine or square wave. The output display is an OLED graphics module. The current sketch follows:

C:
#include <FreqCount.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <math.h>

// OLED
U8G2_SSD1306_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, 10,7,6);

// --- smoothing buffer ---
//const int N = 8;
const int N = 256;   // very stable
uint32_t buf[N];
int idx = 0;
//Inj freq is actual freq + 5645 KHz
int IF = 5645;
bool filled = false;

double smoothValue(uint32_t v) {
  buf[idx++] = v;
  if (idx >= N) {
    idx = 0;
    filled = true;
  }
  int count = filled ? N : idx;
  uint64_t sum = 0;
  for (int i = 0; i < count; i++) sum += buf;
  return (double)sum / count;
}

void setup() {
  Serial.begin(115200);
  u8g2.begin();
  u8g2.setFont(u8g2_font_logisoso24_tf);

  // Teensy 4.0 FreqCount backend uses a 1ms gate internally,
  // so we scale raw counts by 1000 to get Hz.
  FreqCount.begin(1000);
}

void loop() {
  if (FreqCount.available()) {
    uint32_t raw = FreqCount.read();
    double s = smoothValue(raw);
    double hz  = s * 1000.0;
    double mhz = hz / 1e6;

    // slow update timing
    static uint32_t lastUpdate = 0;
    if (millis() - lastUpdate < 300) return;
    lastUpdate = millis();

    char out[20];
     // calibration: counter reads 0.0001 MHz high → subtract 0.0001
      const double CAL_OFFSET = -0.0001;
     // mhz += CAL_OFFSET;
    mhz = round(mhz * 10000.0) / 10000.0;
    
      //Put IF freq correction here
      //mhz = mhz - IF;
   snprintf(out, sizeof(out), "%8.4f", mhz);
    u8g2.clearBuffer();
    u8g2.setCursor(0, 40);
    u8g2.print(out);
    u8g2.sendBuffer();

    Serial.println(out);
  }
}
 
Last edited:
Pro tip: format your code using the "</>" button. Personally, if the code is more than a few lines and not formatted, I generally don't read it.
 
FreqCount is intended for counting digital pulses, not frequency analysis of analog signals.
 
Understood. But, over the frequency range I'm looking at, the readings are the same whether fed an analog signal or square waves. That saves me having to use a Schmitt trigger on the input but I could do that if necessary.
 
You should use a schmitt trigger. T4 is very fast and needs very sharp edges to count accurately. Since you are using 1000 us in your call to FreqCount::begin(), available() will be true every 1 ms. If your display code takes longer than 1 ms to run, your program won't keep up with the data and your results will become stale. Instead of writing to the display on every measurement, use while() instead of if() to process however many values are available, and update your display at a lower frequency. Since you're displaying a 256-ms average, 10 Hz updates should be more than sufficient.
 
Back
Top