Frequency counter for UNO R4 wanted

For the UNO R3 there is software published by Nick Gammon in 2012, and by Paul Stoffregen published in 2018 (or may be before). They can compete with my old Heathkit counter very well, showing stable results up to 5 MHz.
I expected the R4 could do it better, but up to now, their software does not support the UNO R4. So using digitalRead is the only way to read frequencies for me.
The direct counting method results in a maximum frequency of 51 kHz, with reciprocal counting I get 410 kHz, and the results are not very stable. May be somebody finds the time to improve my code.
(I use Serial1 to send results to another Arduino)

const byte input = A0;

void setup() {
  Serial1.begin(9600);
  Serial1.println(__FILE__);
}

void loop() {
  Serial1.println(directCount());
  //Serial1.println(reciprocalCount());
}

long directCount() {
  long count = 0;
  const int scale = 10;
  const long t = micros() + 1E6 / scale;
  byte a = digitalRead(input);
  while (micros() < t) {
    byte b = digitalRead(input);
    // rising edge:
    if (a < b) count++;
    a = b;
  }
  return scale * count;
}

double reciprocalCount() {
  const int maxCount = 10000;
  int count = maxCount;
  byte a = digitalRead(input);
  long t3 = micros();
  while (count) {
    byte b = digitalRead(input);
    // rising edge
    if (a < b) count--;
    a = b;
  }
  long t4 = micros();
  double dt = (t4 - t3) / 1E6; // seconds
  return maxCount / dt;
}

In looking I found this: The Arduino Frequency Counter can measure frequency in the range from 1Hz to 7MHz. The Frequency Detector can detect frequency from 38 Hz to 9612 Hz. This is a SWAG as I am not familiar with the part. To get this they used the internal timer as a counter. If you prescale it you can go even higher.

Well the big problem with the R4 boards is they don't use a crystal but a free running oscillator. This means that any frequency measurement is anything but stable. I would abandon any idea that this is a good board for what you want to do.

1 Like

I am measuring the 48000kHz is stable to c.200Hz using the Clock Frequency Accuracy Measurement Circuit (CAC) with an externally supplied 1kHz clock into the Minima's Heart Pad. The jitter isn't necessarily completely from the actual HOCO but in part being the method of measurement.

47999.93
47999.92
47999.95
47999.88
47999.98
47999.93
47999.93
47999.86
48000.04
47999.91
47999.91
47999.94
47999.91
47999.92
47999.93
47999.98

With the same code running on an EK-RA4M1 eval board, (edit) still HOCO so still "unstable":
N.B. Averaging over 1024 readings per print output.

47999.55
47999.61
47999.59
47999.54
47999.53
47999.55
47999.54
47999.59
47999.51
47999.56
47999.58
47999.52
47999.62
47999.57
47999.54
47999.57
47999.52
47999.58
47999.54
47999.57
47999.52
47999.66
47999.47
47999.57
47999.58
47999.59
47999.58

... which is also c. 200Hz range stable.
So that looks like the normal range for the HOCO option.

I am not sure if you have found anything that works better for you or not.
I have not played much with it, but some of the approaches I might take include:

a) faster digitalRead:
I know some of the others have probably have better versions than the quick and dirty digitalWriteFast, digitalToggleFast and digitalReadFast functions, but I have a set up in the file:
UNOR4-stuff/libraries/UNOR4_digitalWriteFast/UNOR4_digitalWriteFast.h at main ยท KurtE/UNOR4-stuff (github.com)
If you call it with something the compiler knows is a constant pin number, which I believe you are doing, the code will optimize down to a few instructions (hopefully).

b) Something like Paul Stoffregen?
PaulStoffregen/FreqCount: Measures the frequency of a signal by counting the number of pulses during a fixed time. (github.com)

Maybe something like:

const byte input = 12;
volatile uint32_t count = 0;

void setup() {
  while (!Serial && millis() < 5000) {}
  Serial.begin(115200);

  // Set switch as input with pullup
  pinMode(input, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(input), &my_isr, RISING);
}

void loop() {
  // No code in Loop
  Serial1.println(directCount());
}

long directCount() {
  const int scale = 10;
  count = 0;
  const long t = micros() + 1E6 / scale;
  while (micros() < t) {}
  return scale * count;
}

void my_isr() {
  count++;
}

Note: typed on the fly... It compiles but not tested.

This uses attachInterrupt, which is only available on some of the pins. The Arduino documentation for the UNO R4 mainly says this is supported on pins 2 and 3 only.

However, I know it is supported on some other pins as well. If you look at the advanced section of the pinouts web page: ABX00080-full-pinout.pdf (arduino.cc)
You will see which pins.

There is another thread, that talks about some of this.
Pin Change interrupt - Finding various variable names - UNO R4 / UNO R4 Minima - Arduino Forum

Quick update: I did a quick and dirty sketch on a Teensy Micromod,
which used PWM to generate tics.

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  while(!Serial && millis() < 5000) {}
  Serial.begin(115200);
  Serial.print("Starting at 1000");
  analogWriteFrequency(2, 1000.0);
  analogWrite(2, 32);

}

void loop() {
  if (Serial.available()) {
    uint32_t new_frequency = 0;
    int ch;
    while ((ch = Serial.read()) > 0) {
      if (ch >= '0' && ch <= '9') {
        new_frequency = new_frequency * 10 + ch - '0';
      }
      else break;
    }
    while (Serial.read() != -1) {};
    if (new_frequency) {
      analogWriteFrequency(2, new_frequency);
      Serial.printf("New Frequency %u\n", new_frequency);
    } 
  }
  digitalToggleFast(LED_BUILTIN);
  delay(500);

}

I then tried sketch similar to above, except it could be configured to use digitalReadFast or do it by Interrupts.

#include <UNOR4_digitalWriteFast.h>

const byte input = 12;
volatile uint32_t count = 0;

//#define USE_ATTACH_INTERRUPT

void setup() {
  while (!Serial && millis() < 5000) {}
  Serial.begin(115200);

  // Set switch as input with pullup
  pinMode(input, INPUT_PULLUP);

#ifdef USE_ATTACH_INTERRUPT
  attachInterrupt(digitalPinToInterrupt(input), &my_isr, RISING);
#else
#endif  
}

void loop() {
  // No code in Loop
  Serial.println(directCount());
}

#ifdef USE_ATTACH_INTERRUPT
long directCount() {
  const int scale = 10;
  count = 0;
  uint32_t t = micros() + 1E6 / scale;
  while (micros() < t) {}
  return scale * count;
}

void my_isr() {
  count++;
}
#else
long directCount() {
  count = 0;
  const int scale = 10;
  uint32_t t = micros() + 1E6 / scale;
  uint32_t a = digitalReadFast(input);
  while (micros() < t) {
    uint32_t b = digitalReadFast(input);
    // rising edge:
    if (a < b) count++;
    a = b;
  }
  return scale * count;
}
#endif

The Interrupt version worked better, but I think it sort of topped out at around 300,000 pulses per second.

This does not surprise me too much, that it did not get up to something like 5mhz, as the processor only runs at 48mhz so less than 10 clocks per interrupt, might be asking for too much.

Probably could still speed it up, if one bypassed the core layering and tried to field the interrupts directly and not through a callback...

The digitalReadFast, was not nearly as stable or as fast, as noted in the first post.

Clock Frequency Accuracy Measurement Circuit (CAC)
Using EK-RA4M1 eval board... HOCO internal clock still used for USB module
Leonardo Timer4 for 1mS external timer into P204
Note: 1024 reading averaging for each printed result.

Main Clock Oscillator - external 12.0MHz crystal

11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24
11999.24

.
XTAL source Peripheral Module Clock (PCLKB) - changing the PLL multiplier changes this value.

23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48
23999.48

.
For comparison: HOCO source Peripheral Module Clock (PCLKB)

23999.31
23999.23
23999.33
23999.28
23999.30
23999.33
23999.27
23999.30
23999.33
23999.30
23999.27
23999.30
23999.27
23999.29
23999.29
23999.33
23999.26
23999.32
23999.27
23999.30

12.0MHz XTAL source Peripheral Module Clock (PCLKB) - 1mS frame - no averaging.

23999
24000
23999
24000
23999
24000
23999
24000
23999
24000
23999
24000
23999
24000

Depending on how accurate one wants one's result, but the HOCO isn't stable for precision values.

HOCO source Peripheral Module Clock (PCLKB) - 1mS frame - no averaging.

Value   Max     Min
24013	24036	23963
23992	24036	23963
23984	24036	23963
23986	24036	23963
23991	24036	23963
23999	24036	23963
23999	24036	23963
24003	24036	23963
24015	24036	23963
24012	24036	23963
24010	24036	23963
23997	24036	23963
23995	24036	23963
23985	24036	23963
23987	24036	23963
23989	24036	23963
24000	24036	23963
24000	24036	23963
24006	24036	23963
24011	24036	23963
24011	24036	23963
24015	24036	23963
23997	24036	23963
23988	24036	23963
23985	24036	23963
23986	24036	23963

Note different data copy from the graph.

Code for doing the HOCO to XTAL switching here:

Note: Obviously don't use this code if there is NOT a crystal fitted!!!

I have added scope images of XTAL versus HOCO stability.
TL/DR - the XTAL is stable, the HOCO isn't (but we know that!).

@kurtE what is this code meant to do, count never gets incremented?

Arduino digital read is beginner safe for all uses. Using direct port manipulation is like 5x faster.

I want to do the same on my R4 WiFI but it seems to be a bit of a kludge to fit a XTAL. Not sure how different Minima is. the WiFi schematic shows XTAL on pins 9/10 (P213/P212) of the R7FA4M1 MCU connected to 9/10 (P213/P212) on the LED matrix. I couldn't find board layout for R4 (e.g eagle .brd files) . I'm wondering how you wired it in exactly? There are 11 pins tips protruding on top of the R4 next to the LED matrix, presumably these are LED Matrix pins 0-10. But which end is which? :slight_smile: Maybe the pins are in the order shown on sheet 2 of the schematic (attached)?

I have a DG1022Z frequency generator that I can try before going to a XTAL.

UNO R4 WiFI - schematic.pdf (3.2 MB)

Actually it is...

Earlier in the code we attached an interrupt:
attachInterrupt(digitalPinToInterrupt(input), &my_isr, RISING);

And the interrupt handler:

void my_isr() {
  count++;
}

So it is important that the global variable count is defined as volatile.
volatile uint32_t count = 0;
Which tells the compiler that something else might change the value...

1 Like

Ah OK. Helps to read the whole sketch! :upside_down_face:

Hi @ninja2,

I have the Minima, and it has the pads accessible on the PCB to fit a crystal and the two capacitors... although I ended up "trimming" with three per side!!!

I don't see it possible on the WiFi without a major attack - if I was doing it I would lift the two pins off the PCB and free-wire the crystal to them. But even I would rather not have to do that, and I do build free-wire prototypes with 0.50mm pitch devices!

1 Like

As I feared .... let's see how I go.

Since adding a crystal clearly improves general precision, it would make sense to provide some pads for crystal install on the next UNO hardware release. Maybe there's somewhere on this forum where I can add that suggestion to the list ?

PS: your photo ... now that's what I call a busy workplace ... organised chaos :grinning:

PPS: curious: how much is a EK-RA4M1 eval board to buy?

Get you rolling Tutorial to making a stand-alone AVR with utilities for chip ID and bootloading.

Note that the second AVR is an ATmega1284P with 32 IO pins on 4 ports, 2 UARTs and the AVR features like ADC and I2C and SPI ports.
Oh yeah, 128K flash (big fast storage), 16K SRAM and 4K EEPROM.
That chip is the biggest DIP in the AVR line! I paid $5.50 each for them at Mouser before 2020.